You are here

Using AppleScript and cURL to import Content from iPhoto into Drupal

Error message

Deprecated function: The each() function is deprecated. This message will be suppressed on further calls in menu_set_active_trail() (line 2405 of /is/htdocs/wp1022769_0UTS5D8WKG/www/siarp.de/includes/menu.inc).

I'm using Apple's iPhoto to organize my private photographs and Drupal is what comes to my mind first when there's need for a web-based solution. Having lots of pictures from a recent trip I want to show people I started thinking about some kind of web gallery. Being a developer I need to come up with something fancier than just some stock gallery product like Gallery2 (actually I don't really know that product so bear with me). I did some research and found some article about different approaches to build photo galleries in Drupal and also a nice video tutorial. I've used image module and gallery module before but the flexibility that CCK extended by imagefield and imagecache and views seem to offer is just too tempting. But how would I get my hundreds of pictures imported into my yet-to-be-built gallery site? There are plenty of exporter plug-ins for iPhoto sending pictures to galleries like said Gallery2, Flickr and so on. I did not find much information on how to write a plug-in for iPhoto but I found some AppleScript code that did just an export to Drupal. This one seemed to be very outdated but it was a good start. I started a thread on the Drupal developer mailing list which gave me interesting options but I stayed with the concept of using cURL because it's the most flexible and probably the fasted way for my one-time requirement and there are no dependencies. The missing piece was this handbook page on d.o.

Note: The following is a kind of tutorial but the result is just a custom script that needs to be adjusted to your scenario.

AppleScript is probably not the most powerful language but it allows us to call shell scripts. The idea is to get all the image information from iPhoto and then use shell commands to upload to a Drupal site. The first step is to extract the cURL commands from the PHP code from the handbook page so I don't need a separate script. Here are the shell commands to create content on a Drupal site:

First we log in which means getting a cookie with session data.

curl http://localhost/drupal/?q=user \
        -s \
        -c /tmp/curl-cookies.txt \
        -b /tmp/curl-cookies.txt \
        -F 'name=user' \
        -F 'pass=pass' \
        -F 'form_id=user_login' \
        -F 'op=Log in' \
        --output ~/response_login.html

This calls the user page with your username and password. Check response_login.html to see if this has been successful.

Next we need to get the security token of the content creation form.

curl http://localhost/drupal/?q=node/add/image \
        -s \
        -c /tmp/curl-cookies.txt \
        -b /tmp/curl-cookies.txt \
        --output ~/response_create_form.html

I'm using a custom content type called "image" – substitute that with whatever your content type is. response_create_form.html contains the HTML form to add an image including the security token which we can extract using a perl regular expression:

s/.*edit-image-node-form-form-token" *value="([^"]*)".*/$1/s

Finally we actually upload our content.

curl http://localhost/drupal/?q=node/add/image \
        -s \
        -c /tmp/curl-cookies.txt \
        -b /tmp/curl-cookies.txt \
        -F 'title=My Title' \
        -F 'field_image[0][fid]=0' \
        -F 'field_image[0][list]=1' \
        -F 'files[field_image_0]=@/Users/me/myimage.jpg' \
        -F 'body=My content.' \
        -F 'name=Myself' \
        -F 'status=1' \
        -F 'revision=1' \
        -F 'op=Save' \
        -F 'form_id=image_node_form' \
        -F 'form_token=63fe773e820d2a4565720ab3bd0fc991' \

Check your Drupal site – there should be new content. My content type contains an imagefield that takes the above values. The "@" makes cURL upload the actual file.

Now to upload all selected images in iPhoto to the Drupal site I merge the snippets above with the AppleScript from iPhotoToDrupal:

Update: I had an issue with special characters as I entered "'" in iPhoto's description field. I found out about quoted form of variablename which is used to escape strings in AppleScript to use them in shell scripts. This funtion puts the string in single quotes automatically so I couldn't use it like " -F 'body=" & quoted form of this_comment & "'". Fortunately it's also possible to quote the value only like -F body='xxx' so " -F body=" & quoted form of this_comment works. Just being at it I replaced it for other variables also. Finally, I added a "Done" dialog.

set DrupalUsername to "user"
set DrupalPassword to "pass"
set DrupalSite to "http://localhost/drupal/"
set DrupalContentType to "image"
set DrupalAddPage to "?q=node/add/" & DrupalContentType
set CookieFile to "/tmp/curl-cookies.txt"


set curlLogin to "curl"
set curlLogin to curlLogin & " " & DrupalSite & "?q=user"
set curlLogin to curlLogin & " -s"
set curlLogin to curlLogin & " -c " & CookieFile
set curlLogin to curlLogin & " -b " & CookieFile
set curlLogin to curlLogin & " -F 'name=" & DrupalUsername & "'"
set curlLogin to curlLogin & " -F 'pass=" & DrupalPassword & "'"
set curlLogin to curlLogin & " -F 'op=Log in'"
set curlLogin to curlLogin & " -F 'form_id=user_login'"
do shell script curlLogin

set curlAddForm to "curl"
set curlAddForm to curlAddForm & " " & DrupalSite & DrupalAddPage
set curlAddForm to curlAddForm & " -s"
set curlAddForm to curlAddForm & " -c " & CookieFile
set curlAddForm to curlAddForm & " -b " & CookieFile
set tokenSrc to do shell script curlAddForm

set token to do shell script "echo '" & tokenSrc & "' | perl -pe 's/.*edit-" & DrupalContentType & "-node-form-form-token\" *value=\"([^\"]*)\".*/$1/s';"


tell application "iPhoto"
        activate
       
        try
                copy (my selected_images()) to these_images
               
                -- Make sure we have one or more image selected, and if we do, then grab the details of each and upload
                if these_images is false or (the count of these_images) is 0 then
                        error "Please select one or more images."
                end if
               
                repeat with this_image in these_images
                       
                        set this_imagefile to ((image path of this_image) as POSIX file)
                        set this_uploadfile to (POSIX path of this_imagefile)
                        set this_comment to (the comment of this_image) as string
                        set this_name to the title of this_image
                       
                        set curlCreate to "curl"
                        set curlCreate to curlCreate & " " & DrupalSite & DrupalAddPage
                        set curlCreate to curlCreate & " -s"
                        set curlCreate to curlCreate & " -c " & CookieFile
                        set curlCreate to curlCreate & " -b " & CookieFile
                        set curlCreate to curlCreate & " -F title=" & quoted form of this_name
                        set curlCreate to curlCreate & " -F field_image[0][fid]=0"
                        set curlCreate to curlCreate & " -F field_image[0][list]=1"
                        set curlCreate to curlCreate & " -F files[field_image_0]=@" & quoted form of this_uploadfile
                        set curlCreate to curlCreate & " -F body=" & quoted form of this_comment
                        set curlCreate to curlCreate & " -F name=" & quoted form of DrupalUsername
                        set curlCreate to curlCreate & " -F status=1"
                        set curlCreate to curlCreate & " -F revision=1"
                        set curlCreate to curlCreate & " -F op="
                        set curlCreate to curlCreate & " -F form_id=" & DrupalContentType & "_node_form"
                        set curlCreate to curlCreate & " -F form_token=" & token
                        do shell script curlCreate
                       
                end repeat
               
        on error error_message number error_number
                if the error_number is not -128 then
                        display dialog error_message buttons {"Cancel"} default button 1
                end if
        end try
       
end tell

display dialog "Done." buttons {"Ok"} default button 1


-- Taken right out the "Show Image File" AppleScript on the Apple site:
-- http://www.apple.com/applescript/iphoto/
on selected_images()
        tell application "iPhoto"
                try
                        -- get selection
                        set these_items to the selection
                        -- check for single album selected
                        if the class of item 1 of these_items is album then error
                        -- return the list of selected photos
                        return these_items
                on error
                        return false
                end try
        end tell
end selected_images

Just go to iPhoto, select some images and start the script. The images including title and description will be uploaded as new nodes to your Drupal site.

Comments

Hi-

Nice job. I've been trying to use your applescript but am getting an error, "the command exited with a non-zero status". My Drupal install has clean URLs on. Do I need to modify the strings in your script to fit, or will the "non-clean" URLs still work?

Here's the details on my parts: Drupal 6.17 iPhoto 7.1.5 (aka iPhoto '08)

Thanks!

In my Drupal 7.12 i needed to alter your regex line :

set token to do shell script "echo '" & tokenSrc & "' | perl -pe 's/.*edit-" & DrupalContentType & "-node-form-form-token\" *value=\"([^\"]*)\".*/$1/s';"

to:

set token to do shell script "echo '" & tokenSrc & "' | grep form_token | perl -pe 's/.*name=\"form_token\" *value=\"([^\"]*)\".*/$1/s';"

Somehow the name attribute was "form_token" and not "edit-image-node-form-form-token" as your code suggested.

Now it works great. Thanks for sharing!

I also added on the fly image resizing. All credits here go to https://discussions.apple.com/thread/2174350?start=0&tstart=0 from where I borrowed the resize code.

set longest_side to 1024

set DrupalUsername to "admin"
set DrupalPassword to "XXXXXX"
set DrupalSite to "http://localhost/~arminhelbach/drupal7/"
set DrupalContentType to "image"
set DrupalAddPage to "?q=node/add/" & DrupalContentType
set CookieFile to "/tmp/curl-cookies.txt"
set tmp_path to "/tmp"

set curlLogin to "curl"
set curlLogin to curlLogin & " " & DrupalSite & "?q=user"
set curlLogin to curlLogin & " -s"
set curlLogin to curlLogin & " -c " & CookieFile
set curlLogin to curlLogin & " -b " & CookieFile
set curlLogin to curlLogin & " -F 'name=" & DrupalUsername & "'"
set curlLogin to curlLogin & " -F 'pass=" & DrupalPassword & "'"
set curlLogin to curlLogin & " -F 'op=Log in'"
set curlLogin to curlLogin & " -F 'form_id=user_login'"
do shell script curlLogin

set curlAddForm to "curl"
set curlAddForm to curlAddForm & " " & DrupalSite & DrupalAddPage
set curlAddForm to curlAddForm & " -s"
set curlAddForm to curlAddForm & " -c " & CookieFile
set curlAddForm to curlAddForm & " -b " & CookieFile
set tokenSrc to do shell script curlAddForm

set token to do shell script "echo '" & tokenSrc & "' | grep form_token | perl -pe 's/.*name=\"form_token\" *value=\"([^\"]*)\".*/$1/s';"

tell application "iPhoto"
        activate
       
        try
                copy (my selected_images()) to these_images
               
                -- Make sure we have one or more image selected, and if we do, then grab the details of each and upload
                if these_images is false or (the count of these_images) is 0 then
                        error "Please select one or more images."
                end if
               
                repeat with this_image in these_images
                       
                        set this_imagefile to ((image path of this_image) as POSIX file)
                       
                        set this_comment to (the comment of this_image) as string
                        set this_name to the title of this_image
                       
                        tell application "Image Events" to set t to PNG
                        copy (my convert_image(this_imagefile as alias, tmp_path, longest_side)) to tmpimagefile
                        set this_uploadfile to (POSIX path of tmpimagefile)
                       
                        set curlCreate to "curl"
                        set curlCreate to curlCreate & " " & DrupalSite & DrupalAddPage
                        set curlCreate to curlCreate & " -s"
                        set curlCreate to curlCreate & " -c " & CookieFile
                        set curlCreate to curlCreate & " -b " & CookieFile
                        set curlCreate to curlCreate & " -F title=" & quoted form of this_name
                        set curlCreate to curlCreate & " -F field_image[und][0][fid]=0"
                        set curlCreate to curlCreate & " -F field_image[und][0][list]=1"
                        set curlCreate to curlCreate & " -F files[field_image_und_0]=@" & quoted form of this_uploadfile
                        set curlCreate to curlCreate & " -F body=" & quoted form of this_comment
                        set curlCreate to curlCreate & " -F name=" & quoted form of DrupalUsername
                        set curlCreate to curlCreate & " -F status=1"
                        set curlCreate to curlCreate & " -F revision=1"
                        set curlCreate to curlCreate & " -F op="
                        set curlCreate to curlCreate & " -F form_id=" & DrupalContentType & "_node_form"
                        set curlCreate to curlCreate & " -F form_token=" & token
                        do shell script curlCreate
                       
                        do shell script "rm '" & this_uploadfile & "'"
                end repeat
               
        on error error_message number error_number
                if the error_number is not -128 then
                        display dialog error_message buttons {"Cancel"} default button 1
                end if
        end try
       
end tell

display dialog "Done." buttons {"Ok"} default button 1


-- Taken right out the "Show Image File" AppleScript on the Apple site:
-- http://www.apple.com/applescript/iphoto/
on selected_images()
        tell application "iPhoto"
                try
                        -- get selection
                        set these_items to the selection
                        -- check for single album selected
                        if the class of item 1 of these_items is album then error
                        -- return the list of selected photos
                        return these_items
                on error
                        return false
                end try
        end tell
end selected_images


on convert_image(this_item, target_path, longest_side)
        tell application "Image Events"
                try
                        launch
                        -- open the image file
                        set this_image to open this_item
                        tell application "Finder"
                                set new_item to ((target_path) as string) & (name of this_item) & ".jpg"
                        end tell
                        copy dimensions of this_image to {current_width, current_height}
                        if current_width is greater than current_height then
                                set new_height to (longest_side * current_height) / current_width
                                if (new_height * longest_side) < (current_width * current_height) then
                                        scale this_image to size longest_side
                                end if
                        else
                                if (new_height * longest_side) < (current_width * current_height) then
                                        set new_height to (longest_side * current_width) / current_height
                                        scale this_image to size new_height
                                end if
                        end if
                        save this_image in new_item as JPEG
                        return new_item
                on error msg
                        return msg
                end try
        end tell
end convert_image