Automated FTP from Dropbox with Hazel

Macstories.net linked to QuickShot 2.0. It's an iOS app that can send photos directly to Dropbox. But I was thinking, I would prefer an app that could quickly send images to my FTP server at Macdrifter.com. So I came up with a little Hazel rule to connect Dropbox and my FTP server.

Design Considerations

I want to put these files on my FTP server so that I can use them for posts to this site. That means I need an easy way to get the URL of the image. I previously built an email rule that uploaded the file and returned the link to me in a reply email. That worked really well, but email is not where I write my posts. I wanted the URL somewhere convenient. So I decided to have the resulting links added to a URL log file in my Dropbox Notes folder. NVAlt, WriteRoom and Nebulous Notes all point to my Notes folder.

I knew I would use FTP. I really like Transmit for FTP, but it seemed a little heavy handed for Hazel automation. I decided I needed to use straight Python for the FTP and skip the application scripting.

The Hazel rule is simple. It just looks for a new file it has never matched before. All of the work is done by a Python script.

Hazel Rule

Note that the Shell path is actually invoking Python.

The Script

I learned something new with this Python script, which is generally my goal. I learned about using the Paramiko module for SFTP. Paramiko is primarily a Python module for SSH that also includes some convenient methods for SFTP. I'm not sure if it passes the Drang sniff test but I like it.

To install Paramiko, just use pip:

:::bash
sudo pip install paramiko

It should also install the pycrypto dependency.

Here's the entire Python script.

:::python
import paramiko
from datetime import date
import os
import sys
import urllib

try:
    # This is how Hazel gets the incoming file path
    localPath = sys.argv[1]

    # Location to write the URL links
    logFilePath = "/Users/weatherh/Dropbox/Notes/Linkin_Logs.txt"

    # Base File Location on the FTP Host to upload the file
    remotePath = "/home/macdrifter/webapps/wp/wp-content/uploads/"
    fileName = os.path.basename(localPath)

    # Just logging paramiko incase of issues
    paramiko.util.log_to_file('/tmp/paramiko.log')
    host = "my_host_name"
    port = 22

    # This will be the base URL path for the file link
    url_Base = "http://www.macdrifter.com/uploads.html"
    transport = paramiko.Transport((host, port))

    password = "my_super_strong_password"
    username = "my_sftp_user_name"

    # Determine the current month and year to create the upload path
    today = date.today()
    datePath = today.strftime("%Y/%m/")

    # Used to create full remote file path
    remoteFilePath =  remotePath + datePath + fileName
    transport.connect(username = username, password = password)
    sftp = paramiko.SFTPClient.from_transport(transport)

    # Do the deed. Use the SFTP put command
    sftp.put(localPath, remoteFilePath)
    sftp.close()
    transport.close()

    #Open the URL log file for appending
    logfile = open(logFilePath, "a")

    try:
        # %% encode the file name and append the URL to the log file
        logfile.write(url_Base+datePath+urllib.quote(fileName)+'\n')

    finally:
        logfile.close()

except IOError:
    pass

Obviously, you will need to provide your own connection details and file paths. This script is specific to my needs.

The Workflow

There are two ways I am using this. The first is on my Mac. Any file I drag to this Dropbox FTP folder gets uploaded and a link added my link log. It's amazingly convenient.

Link Log

The second is on iOS. The QuickShot app is cool. I configured it to upload to this Dropbox FTP folder. The file is uploaded to the server and a link silently added to my log file. I can upload any file from an application that supports Dropbox uploads. But QuickShot is pretty convenient.

I'll also add that it is convenient to have a Dropbox folder filled with the images I have uploaded to my server. It provides a history for me to see what I was thinking while adding screen shots. I take a heck of a lot of screen shots.

Comments

There are a lot of modules imported for this script. sys is needed just to handle the connection with Hazel. os is needed just for getting the file name and writing the log file. Heck, I even import the urllib module just to encode the file name. But you know what? It works and it is fast, so I don't care. CPU cycles are cheap. Isn't that the point of these scripting languages?

It's never a great idea to hard code login credentials. I might change this in the future to use a config file, but I'm not too concerned with the kind of stuff I use this for.

The error handling in the script is pretty lean. Other than the try and except blocks there is none. I should add more. I probably will not.

The FTP put command will overwrite any file with the same name in the same location. That's by design. If I want to edit a file on my Mac, I can just open it in Acorn or optimize it with ImageOptim and Hazel will reprocess it when I save. The new version is uploaded but the URL link remains the same.

This will work with any file type. That's cool but a bit scary. If I accidentally drop-in something private, then it will be uploaded to my public FTP host automatically. I'm aware of the potential problems. I'm a big boy. I can handle it.

Future Plans

I'd like to add some automatic image processing. I definitely want to include ImageOptim processing but I would also like to resize any image larger than 650px. It would save a lot of trouble with iPad images. I might need some different rules to provide multiple options for image resolution.

I'm thinking about adding the links to my pasteboard on my Mac. That way I would have the log file and also a running list in my pasteboard.