Webloc to Pinboard

If I want to keep a URL around for later I generally drag the URL from Safari’s URL bar onto the desktop. This creates an “internet clipping file” (with a .webloc file extension). These files are like little self contained bookmarks that, when double clicked open the linked webpage in Safari. Because they’re just ordinary files, you can manage them like any other file, store them in folders, copy them around, delete them, etc.

Eventually these clippings will get deleted or filed in a folder and forgotten. In the past I’ve written Spotlight importers to try and gather all these clippings and then export them in a format I could use in a bookmark manager application, such as WebNoteHappy. But most bookmark manager apps tend to be quite limited and I’ve found online services such as delicious, instapaper and now pinboard.in to be far superior to any desktop application.

But getting from an internet clipping on my desktop to an entry in an online bookmark manager usually involves a lot of manual labour.

I’ve found a great solution using Hazel and a custom Python script. I have a Hazel rule that finds files with a “.webloc” file extension on my desktop. The rule then runs a single python script to add the URL to my pinboard.in account and then moves the file into the trash. Usually Hazel notices the clipping, adds it to pinboard.in and trashes the file within a couple of seconds.

Screen shot 2009-12-17 at 09.12.35

The python script is where all the magic happens:

#!/usr/bin/python

import sys
import urllib
import urllib2
import re
import subprocess
import Foundation
from Carbon import File, Files, Res

def infoForWebloc(inPath):
    theDisplayName = Foundation.NSFileManager.defaultManager().displayNameAtPath_(inPath)
    resNum = Res.FSOpenResourceFile(inPath, File.FSGetResourceForkName(), Files.fsRdPerm)
    Res.UseResFile(resNum)
    theResource = Res.Get1Resource('url ', 256)
    theURLFromResource = theResource.data
    Res.CloseResFile(resNum)
    theData = Foundation.NSData.dataWithContentsOfFile_(inPath)
    thePropertyList = Foundation.NSPropertyListSerialization.propertyListWithData_options_format_error_(theData, 0, None, None)
    theURLFromData = thePropertyList['URL'] 
    return theDisplayName, theURLFromResource

def getAccount(inServer):
    theArguments = ['security', 'find-internet-password', '-s', inServer, '-g']
    thePipe = subprocess.Popen(theArguments, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    theOutput = thePipe.stdout.read()
    theMatch = re.search(r'"acct"<blob>="(.+)"', theOutput)
    theUsername = theMatch.groups()[0]
    theOutput = thePipe.stderr.read()
    theMatch = re.match(r'^password: "(.+)"$', theOutput)
    thePassword = theMatch.groups()[0]
    return theUsername, thePassword

def upload(inURL, inDescription):
    password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
    top_level_url = "https://api.pinboard.in/v1"
    theUsername, thePassword = getAccount('api.pinboard.in')
    password_mgr.add_password(None, top_level_url, theUsername, thePassword)
    handler = urllib2.HTTPBasicAuthHandler(password_mgr)
    opener = urllib2.build_opener(handler)
    inURL = urllib.quote(inURL)
    inDescription = urllib.quote(inDescription)
    theURL = 'https://api.pinboard.in/v1/posts/add?url=%s&shared=no&replace=yes&description=%s' % (inURL, inDescription)
    theResult = opener.open(theURL)

def main(args):
    for thePath in args:
        theDescription, theURL = infoForWebloc(thePath)
        upload(theURL, theDescription)

if __name__ == '__main__':
    main(sys.argv[1:])

This script reads the .webloc file to extract the URL (.webloc files store the URL in both the Carbon resource fork and in the data fork in a property list format, this script extracts the URL from both locations but only uses the URL from the resource fork.

Then script uploads the URL to pinboard.in via pinboard.in’s delicious style API. The script gets your pinboard.in username and password from your keychain. To add the username and password to your keychain you’ll want to run this little shell script.

#!/bin/sh

HOST=api.pinboard.in
read -p "Username: " USERNAME
read -p "Password: " -s PASSWORD
security add-internet-password -U -r http -s "$HOST" -a "$USERNAME" -w "$PASSWORD"

You can download the Hazel rule here, but do remember to set up your pinboard.in keychain item before installing it.

Instead of using the entire desktop as a sort of a dropbox for URLs you could easily adapt the script and put it into a automator action or perhaps a system service.

This entry was posted in Default and tagged , , , , . Bookmark the permalink.