ftp download images, process, and email - works great, almost ...

This is a script that queries a remote server for recently uploaded image files, downloads them, and attaches jpeg thumbnails to an email. EPS files are distilled to PDFs (because Image Events can’t handle EPS) and zip/rar archives are unstuffed before thumbnailing. Needless to say, all are welcome to whatever parts of this you find useful.

Issues:

  1. The error handling could be better. Right now it just creates a generic error email whenever it encounters a file that Image Events can’t handle. I’d rather have it pre-screen the downloaded files for unrecognized file types but not sure the best way to do this.

  2. Sometimes native Illustrator files (.ai) are processed properly; sometimes not. I suspect it has to do with whether they are saved with PDF content or not. Any way to check that ahead of time?

  3. The biggest problem is that the script blows up when a customer uploads folder(s) instead of loose files. Files are processed properly but the script leaves the empty folder structure behind, causing the script to error next time it runs. (I run it once an hour via Cronnix). Whenever a folder is uploaded, I come in the next morning and find 10 instances of the script running, all timed out because Transmit is waiting to be told how to handle the duplicate items.

How can I make it delete the empty folders from the ‘In’ folder? Transmit doesn’t return a folder list when it downloads, so I would need some other way of building a list of folders to delete.

set the server_path to "afp://192.168.1.120/Home Directory/"
set the preview_images to {}
set the downloads to {}
set the success_list to {}
set the failure_list to {}

tell application "Finder"
	set localhostLogon to "afp://name:password@192.168.1.120/Home Directory/"
	if not (exists folder "Home Directory:Desktop:Shared:Uploads" of desktop) then
		mount volume localhostLogon
	end if
	set the server_volume to (folder "Home Directory:Desktop:Shared:Uploads" of desktop)
	repeat with folderTestMe in {"Previews", "In", "Out", "Skipped"}
		if not (exists folder folderTestMe of the server_volume) then
			set name of (make new folder of the server_volume) to folderTestMe
		end if
	end repeat
	set the preview_folder to (folder "Previews" of the server_volume)
	set the in_folder to (folder "In" of the server_volume)
	set the Out_Folder to (folder "Out" of the server_volume)
	set the Skipped_Folder to (folder "Skipped" of the server_volume)
end tell

--connect to ftp server
tell application "Transmit"
	make new document at before front document
	tell current session of document 1
		if (connect to "[url=ftp://ftp.remote-server.com]ftp.remote-server.com[/url]" as user "username" with password "password") then
			if (set your stuff to "/Volumes/Home Directory/Desktop/Shared/Uploads/In") then
				if (set their stuff to "/public/customer_uploads/") then
					set the remote_files to (list remote folder)
				end if
			end if
		end if
	end tell
end tell

--list files not on local volume
tell application "Finder"
	repeat with existsMe in the remote_files
		if not (exists file existsMe of entire contents of the server_volume) then
			set the downloads to the downloads & existsMe
		end if
	end repeat
end tell
--download the results
if the downloads ≠ {} then
	tell application "Transmit"
		with timeout of 600 seconds
			tell current session of document 1
				repeat with download_me in the downloads
					download item download_me
				end repeat
			end tell
		end timeout
	end tell
	set dateObj to current date
	set the local_name to "Received " & (numDateUS from dateObj) & " | " & (timeString from dateObj) as string
	tell application "Finder" to set the local_folder to (make new folder of the Out_Folder with properties {name:the local_name})
end if

tell application "Finder"
	set the zipped_files to (every file of (entire contents of in_folder) whose name extension is "zip") & (every file of (entire contents of in_folder) whose name extension is "rar")
	set the eps_files to (every file of (entire contents of in_folder) whose name extension is "eps")
end tell
if the zipped_files ≠ {} then
	tell application "StuffIt Expander"
		repeat with h in the zipped_files
			expand (h as alias)
		end repeat
		quit
	end tell
	tell application "Finder" to move the zipped_files to the local_folder
end if
if the eps_files ≠ {} then
	tell application "Acrobat Distiller"
		repeat with distillMe in the eps_files
			Distill sourcePath (POSIX path of (distillMe as alias))
		end repeat
		quit
	end tell
	tell application "Finder" to move the eps_files to the local_folder
	
end if

tell application "Finder" to set the in_files to (entire contents of the in_folder)

repeat with previewMe in the in_files
	set the_info to (info for previewMe as alias)
	if kind of the_info ≠ "Folder" then
		set the generic_name to my strip_name(previewMe)
		set the Image_Path to ((preview_folder as alias) & (generic_name as string) & ".jpg") as string
		set the Image_Path to (POSIX path of (the Image_Path))
		
		--convert to jpeg max size 300 x 400
		tell application "Image Events"
			try
				set this_image to (open file (POSIX path of (previewMe as alias)))
				set target_width to 400
				set target_height to 300
				copy dimensions of this_image to {current_width, current_height}
				if current_width > current_height then
					if current_width > target_width then
						scale this_image to size target_width
					end if
				else if current_width ≤ current_height then
					if current_height > target_height then
						scale this_image to size target_height
					end if
				end if
				save this_image in (the Image_Path) as JPEG with icon
				tell application "System Events"
					set the preview_images to the preview_images & the Image_Path
					set the raw_size to size of (the_info)
					set mySize to (convertByteSize of me on the raw_size)
				end tell
				set the destination to the local_folder
				set the success_list to the success_list & return & (name of previewMe) & tab & tab & (mySize)
			on error
				set the destination to the Skipped_Folder
				set the failure_list to the failure_list & return & (name of previewMe)
				
			end try
		end tell
		tell application "Finder" to move previewMe to the destination with replacing
	end if
end repeat
get {success_list, failure_list}

tell application "Mail"
	
	if the success_list ≠ {} then
		set myMessage to (make new outgoing message with properties {subject:"Files Received", content:"Files received " & (current date) & "." & return & return & "Name" & tab & tab & "Size" & return & the success_list & return & return & "NOTE: The attached images are for preview only!" & return})
		tell myMessage
			--make new to recipient at end of to recipients with properties {address:"luke@vomax.com"}
			make new to recipient at end of to recipients with properties {address:"sales@vomax.com"}
			make new to recipient at end of to recipients with properties {address:"artdept@vomax.com"}
			repeat with attachMe from 1 to (count the preview_images)
				tell content
					make new attachment with properties {file name:(item attachMe of the preview_images)} at after the last paragraph
				end tell
			end repeat
			send
		end tell
	end if
	if the failure_list ≠ {} then
		set myMessage to (make new outgoing message with properties {subject:"Download Errors", content:"Some downloaded files were not processed " & (current date) & "." & return & return & "Name:" & return & the failure_list & return, visible:true})
		tell myMessage
			make new to recipient at end of to recipients with properties {address:"luke@vomax.com"}
			send
		end tell
	end if
	
end tell

tell application "Transmit"
	close every document
	quit
end tell

to convertByteSize on the raw_size
--thanks NIgel!
	set oneK to 2 ^ 10
	set oneMB to 2 ^ 20
	set oneGB to 2 ^ 30
	tell the raw_size to ¬
		if it ≥ oneGB then
			return ((it div oneGB) & "." & text 2 thru 3 of ((100 + ((it mod oneGB) div (oneK * 1.0E+4))) as string) & " GB") as string
		else if it ≥ oneMB then
			return ((it div oneMB) & "." & text 2 thru 3 of ((100 + ((it mod oneMB) div (oneK * 10))) as string) & " MB") as string
		else if it ≥ oneK then
			return (it div oneK & " KB") as string
		else
			return (it & " bytes") as string
		end if
end convertByteSize

on strip_name(previewMe)
	tell application "Finder"
		set {name:Nm, name extension:Ex} to info for (previewMe as alias)
		if Ex is missing value then return Nm
		return text 1 thru ((count Nm) - (count Ex) - 1) of Nm
	end tell
end strip_name

-- timeString -- by Richard Morton 2000
-- Return a file name friendly time string from a date object
on timeString from dateObj
	set timeStr to time string of dateObj
	tell AppleScript -- Just saves typing
		set oD to text item delimiters
		set text item delimiters to ":" -- assumes colon time separator
		set timeStrItems to text items of timeStr
		set text item delimiters to "." -- change to something else if req.
		set newTimeStr to timeStrItems as string
		set text item delimiters to oD
		return newTimeStr
	end tell
end timeString

-- numDateUS - mm-dd-yyyy
-- Garvey/Levy/Morton, 1998-2003
on numDateUS from dateObj
	copy dateObj to dateTemp
	set month of dateTemp to January
	return text 2 thru 3 of ((100 + ((dateTemp - 2.5E+6 - dateObj) div -2.5E+6)) as string) ¬
		& "-" & text 2 thru 3 of ((100 + (dateObj's day)) as string) & "-" & year of dateObj
end numDateUS

Well, here’s my magnum opus in its working form. Save it as an app and run it via Cronnix for automatic downloading & processing of images. It unzips archives, distills eps files to pdf’s, and mails jpeg thumbnails of the images to a recipient list. Dig in and help yourself!


    --FTP download & process images by Luke Jaeger, except the parts that other people wrote
    --downloads new files from an ftp server and emails jpeg thumbnails to specified recipients
    set the server_path to "afp://192.168.1.120/Home Directory/"
    set the preview_images to {}
    set the downloads to {}
    set the success_list to {}
    set the failure_list to {}

    tell application "Finder"
       set localhostLogon to "afp://user:password@192.168.1.120/Home Directory/"
       if not (exists folder "Home Directory:Desktop:Shared:Uploads" of desktop) then
           mount volume localhostLogon
       end if
       set the server_volume to (folder "Home Directory:Desktop:Shared:Uploads" of desktop)
       repeat with folderTestMe in {"Previews", "In", "Out", "Skipped"}
           if not (exists folder folderTestMe of the server_volume) then
               set name of (make new folder of the server_volume) to folderTestMe
           end if
       end repeat
       set the preview_folder to (folder "Previews" of the server_volume)
       set the in_folder to (folder "In" of the server_volume)
       set the Out_Folder to (folder "Out" of the server_volume)
       set the Skipped_Folder to (folder "Skipped" of the server_volume)
    end tell

    --connect to ftp server
    tell application "Transmit"
       make new document at before front document
       tell current session of document 1
           if (connect to "[url=ftp://ftp.yoursite.com]ftp.yoursite.com[/url]" as user "username" with password "password") then
               if (set your stuff to "/Volumes/Home Directory/Desktop/Shared/Uploads/In") then
                   if (set their stuff to "/public/customer_uploads/") then
                       set the remote_files to (list remote folder)
                   end if
               end if
           end if
       end tell
    end tell

    --list files not on local volume
    tell application "Finder"
       repeat with existsMe in the remote_files
           if not (exists file existsMe of entire contents of the server_volume) and not (exists folder existsMe of entire contents of the server_volume) then
               set end of downloads to existsMe
           end if
       end repeat
    end tell
    --download the results
    if the downloads ≠ {} then
       tell application "Transmit"
           with timeout of 600 seconds
               tell current session of document 1
                   repeat with download_me in the downloads
                       download item download_me with resume mode skip
                   end repeat
               end tell
           end timeout
       end tell
       set dateObj to current date
       set the local_name to "Received " & (numDateUS from dateObj) & " | " & (timeString from dateObj) as string
       tell application "Finder" to set the local_folder to (make new folder of the Out_Folder with properties {name:the local_name})
       tell application "Finder" to set the zipped_files to every file of (entire contents of in_folder) whose {"zip", "rar", "sit", "sitx"} contains name extension
       
       if the zipped_files ≠ {} then
           tell application "StuffIt Expander"
               repeat with H in the zipped_files
                   expand (H as alias)
               end repeat
           end tell
           
       end if
       tell application "Finder" to set the eps_files to (every file of (entire contents of in_folder) whose name extension is "eps")
       
       if the eps_files ≠ {} then
           tell application "Acrobat Distiller"
               repeat with distillMe in the eps_files
                   Distill sourcePath (POSIX path of (distillMe as alias))
               end repeat
           end tell
           
       end if
       
       tell application "Finder" to set the in_files to (entire contents of the in_folder)
       
       repeat with previewMe in the in_files
           set the_info to (info for previewMe as alias)
           if kind of the_info ≠ "Folder" then
               set the generic_name to my strip_name(previewMe)
               set the Image_Path to ((preview_folder as alias) & (generic_name as string) & ".jpg") as string
               set the Image_Path to (POSIX path of (the Image_Path))
               
               --convert to jpeg max size 300 x 400
               tell application "Image Events"
                   try
                       set this_image to (open file (POSIX path of (previewMe as alias)))
                       set target_width to 400
                       set target_height to 300
                       copy dimensions of this_image to {current_width, current_height}
                       if current_width > current_height then
                           if current_width > target_width then
                               scale this_image to size target_width
                           end if
                       else if current_width ≤ current_height then
                           if current_height > target_height then
                               scale this_image to size target_height
                           end if
                       end if
                       save this_image in (the Image_Path) as JPEG with icon
                       tell application "System Events"
                           set the preview_images to the preview_images & the Image_Path
                           set the raw_size to size of (the_info)
                           set mySize to (convertByteSize of me on the raw_size)
                       end tell
                       set the success_list to the success_list & return & (name of previewMe) & tab & tab & (mySize)
                   on error
                       if characters -1 thru -4 of (name of previewMe as text) ≠ {".", "e", "p", "s"} then
    -- don't generate error message for eps files, because they should have been distilled already
                           set the failure_list to the failure_list & return & (name of previewMe)
                       end if
                   end try
               end tell
           end if
       end repeat
       
       tell application "Finder" to move the in_files to the local_folder
       get {success_list, failure_list}
       
       tell application "Mail"
           
           if the success_list ≠ {} then
               set myMessage to (make new outgoing message with properties {subject:"Files Received", content:"Files received " & (current date) & "." & return & return & "Name" & tab & tab & "Size" & return & the success_list & return & return & "NOTE: The attached images are for preview only!" & return, visible:true})
               tell myMessage

                   make new to recipient at end of to recipients with properties {address:"someone@yourcompany.com"}
                   make new to recipient at end of to recipients with properties {address:"someone-else@yourcompany.com"}
                   repeat with attachMe from 1 to (count the preview_images)
                       tell content
                           make new attachment with properties {file name:(item attachMe of the preview_images)} at after the last paragraph
                       end tell
                   end repeat
                   send
               end tell
           end if
           if the failure_list ≠ {} then
               set myMessage to (make new outgoing message with properties {subject:"Download Errors", content:"Some downloaded files were not processed " & (current date) & "." & return & return & "Name:" & return & the failure_list & return, visible:true})
               tell myMessage
                   make new to recipient at end of to recipients with properties {address:"you@yourcompany.com"}
                   send
               end tell
           end if
           
       end tell
    end if
    tell application "Transmit"
       close every document
       quit
    end tell
    tell application "Acrobat Distiller" to quit
    tell application "StuffIt Expander" to quit

    to convertByteSize on the raw_size
    -- does Nigel Garvey rock? The answer is yes.
       set oneK to 2 ^ 10
       set oneMB to 2 ^ 20
       set oneGB to 2 ^ 30
       tell the raw_size to ¬
           if it ≥ oneGB then
               return ((it div oneGB) & "." & text 2 thru 3 of ((100 + ((it mod oneGB) div (oneK * 1.0E+4))) as string) & " GB") as string
           else if it ≥ oneMB then
               return ((it div oneMB) & "." & text 2 thru 3 of ((100 + ((it mod oneMB) div (oneK * 10))) as string) & " MB") as string
           else if it ≥ oneK then
               return (it div oneK & " KB") as string
           else
               return (it & " bytes") as string
           end if
    end convertByteSize

    on strip_name(previewMe)
    --did I mention that StefanK is an applescript genius?
       tell application "Finder"
           set {name:Nm, name extension:Ex} to info for (previewMe as alias)
           if Ex is missing value then return Nm
           return text 1 thru ((count Nm) - (count Ex) - 1) of Nm
       end tell
    end strip_name

    -- timeString -- by Richard Morton 2000
    -- Return a file name friendly time string from a date object
    on timeString from dateObj
       set timeStr to time string of dateObj
       tell AppleScript -- Just saves typing
           set oD to text item delimiters
           set text item delimiters to ":" -- assumes colon time separator
           set timeStrItems to text items of timeStr
           set text item delimiters to "." -- change to something else if req.
           set newTimeStr to timeStrItems as string
           set text item delimiters to oD
           return newTimeStr
       end tell
    end timeString

    -- numDateUS - mm-dd-yyyy
    -- Garvey/Levy/Morton, 1998-2003
    on numDateUS from dateObj
       copy dateObj to dateTemp
       set month of dateTemp to January
       return text 2 thru 3 of ((100 + ((dateTemp - 2.5E+6 - dateObj) div -2.5E+6)) as string) ¬
           & "-" & text 2 thru 3 of ((100 + (dateObj's day)) as string) & "-" & year of dateObj
    end numDateUS


Hi misterfriendly,

thank you very much for the kindly mention in your script.
But I’d like to be quoted correctly ;):
info for isn’t a part of the Finder, it belongs to Standard Additions.
A Finder tell block is useless

Good Heavens, man — don’t you ever sleep???

There was some reason why it didn’t work without that Finder tell and now I can’t remember what it was. Maybe the sub was called from inside a Finder tell? Would that make a difference?

No, a tell block is valid only for the current scope