A django app for consuming public FriendFeed streams
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.



A django app for consuming public FriendFeed streams


FriendFeed is a fantastic service which does all of the hard work in aggregating and "normalizing" (for lack of a better description) all of the online activity we generate on socal sites like Flickr, YouTube, et al.

Consonance is merely a set of django models which mirrors the structure set forth in the FriendFeed API Documentation and a convenient cron-callable script for fetching updates from FriendFeed. I imagine that it will be most useful in presenting a lifestream to readers of your blog or similar. How you display the content is up to you -- there are no views or templates included with this application.

License & How to Contribute

Sad that these two subjects rarely go together in a README. They should.

Consonance is yours to use, modify, and redistribute according to the terms of the BSD license, the full text of which is in a file named LICENSE.txt, which should be in the same directory as this readme.

Consonance is hosted on github, with issuetracking supplied by the equally-lovely lighthouse. It is ridiculously easy to contribute code, and it is ridiculously easy to fork off your own branch.



Consonance requires:

It might work with python < 2.5 but I haven't tested and don't intend to. If some kind soul verifies that it works on older python versions I'll note it here, but I don't plan on spending time supporting such a configuration.

Getting Consonance

Using easy_install: run easy_install consonance from your favorite shell. The latest version of consonance will be fetched and installed from PyPI. If you don't already have easy_install, go get it.

Note: the version of easy_install used to create the installer is 0.6c9. Sadly, python on the mac (leopard) comes bundled with 0.6c7 and this causes installation to fail even if you have a newer setuptools in your site_packages. Some googling has yielded this information, but the short solution is to open your favorite terminal and upgrade the built-in easy_install script as follows:

sudo easy_install -s /usr/bin setuptools

From source: get yourself a copy of consonance from the homepage. If you're comfortable with git, then clone yourself a copy. If you just want a tarball, click on the "download" button near the top of the page, download a .zip or tarball, and unpack it somewhere convenient. Either way, you should end up with a directory looking something like this:

|- LICENSE.txt
|- CHANGES.rst
|- README.rst
|- README.html
|- consonance_fetch.py
|- setup.py
|- ez_setup.py
|- consonance
   |- __init__.py
   |- admin.py
   |- fetch.py
   |- models.py
   |- views.py

Put the inner consonance directory somewhere on your python path. You can copy it to your django directory, to your site-packages directory. You can also just run python setup.py install.

You will also need to make use of the consonance_fetch.py script to fetch updates, so put that somewhere on your $PATH.

Using Consonance

Make sure to add consonance to the INSTALLED_APPS list in settings.py.

New in 0.2: Consonance will look for a dictionary of friendfeed usernames called CONSONANCE_USERS in your project's settings.py:

    'user1' : {},

Make sure to add at least one name to CONSONANCE_USERS.

Filtering Entries Received from FriendFeed

Consonance can selectively ignore certain entries it receives from friendfeed. Familiarizing yourself with the FriendFeed API Documentation is necessary, in particular the description of the JSON feed format.

Filtering works by specifying a "path" to some child of a friendfeed entry, and specifying a list of patterns. If the child exists, and one of the patterns match, then that entire entry will be skipped during fetch. The patterns are any pattern accepted as input to Python's re (regular expression) module.

An example here is worth a thousand words. Say you want to exclude all twitter updates from the entries you're fetching from To use filtering. Alter your CONSONANCE_USERS to read as follows:

    'myusername' : {
        "service.name" : ["Twitter",],

You can easily specify multiple rules; an entry is filtered if any of them match:

    'myusername' : {
        "service.name" : ["Flickr"],
        "anonymous" : [True],
        "hidden" : [False],
        "link" : ["^http://twitter.com/*"],
The following is a list of valid path specifiers:
  • id
  • title
  • link
  • published
  • updated
  • hidden
  • anonymous
  • user.id, user.name, user.nickname, user.profileUrl
  • service.id, service.name, service.iconUrl, service.profileUrl
  • via.name, via.url
  • room.id, room.name, room.nickname, room.url

Fetching Entries from FriendFeed

Consonance does not perform fetches automatically. You've got to do it yourself, using a script called consonance_fetch.py. If you installed consonance using easy_install, then it should be present on your path. You can invoke the script as follows:

consonance_fetch.py --projectpath="/path/to/my/django/project"

The projectpath argument should contain the path to your django project, which is usually wherever your project's settings.py resides.

Every time the script is run, it fetches the new updates for each of the users specified in the CONSONANCE_USERS. You'll probably want to run this script periodically via a cron job or similar.

Be nice to FriendFeed's servers. You probably don't generate new content more than once every ten minutes. If you call consonance_fetch.py too often, eventually FriendFeed's API will throttle/ignore you.

If you want to see more about what consonance_fetch.py can do, run it as follows:

consonance_fetch.py --help


Originally I wanted an easy way to build a "lifestream" application in Django. Naturally, I took the long and stupid route. Originally starting with FriendFeed, I eventually decided (for reasons I can't recall now) that I should Build My Own, and boldly created Djangregator, which abandoned FriendFeed and embraced a pluggable-backend model, with one backend for each of the supported online services. I wrote three such backends (Twitter, Flickr, Delicious).

Since the ultimate goal of Djangregator was to provide a lifestream for a blog, I started looking at oembed support, and django-oembed, so users could easily embed media instead of just a link to some media. Then I got to thinking about "batching" -- what happens when you upload 52 images to Flickr? Should there be 52 separate entries that the user needs to deal with at display-time?

It turns out that FriendFeed already:
  • Does all of this for you
  • Does it for a heckuva lot more services than the three I cobbled together
  • And probably does it in a more robust, bug-free fashion given their userbase
  • Probably doesn't need to worry about API rate-limiting as it's a big-name consumer
  • Deals with batching!
  • Deals with service-specific date and time parsing (WTF? Can't everybody just agree on ISO8601?)
  • For that matter, gives all datetimes normalized to UTC
  • Makes tea and fetches your slippers

Update: Sadly it seems that "enclosures" doesn't contain the embed HTML. Looking into doing it with django-oembed, but that seems to be broken too.

In retrospect, Djangregator was a good learning experience. I'm glad I came full-circle, though. Thank you FriendFeed!


Going with the jazz themes of Django, and the fact that FriendFeed shines at pulling together the disparate elements of your online life:


agreement or compatibility between opinions or actions: consonance between conservation measures and existing agricultural practice.
  • the recurrence of similar sounds, esp. consonants, in close proximity (chiefly as used in prosody).
  • Music the combination of notes that are in harmony with each other due to the relationship between their frequencies.