Photo feed as Desktop Background Creative Commons License

posted mid-afternoon by Constantinos. Filed under Code, Google, OS X

This post was originally published in 2008
The tips and techniques explained may be outdated.

I first came up with this idea after reading a post on HackerMoJo.com about getting an RSS feed of images as a Screen Saver using a python script. The idea is simple: Take a feed, parse it, and save the photos it links to as numbered images in a folder on the disk, then use that folder as a Screen Saver. Well, as it turns out you can also use that folder as a desktop background, but there are a few improvements that can be made.

First, the script only worked with that particular feed. If you wanted to use a different feed, you would have to change the code. Second, the script would always download all the photos linked to from the feed, thus increasing the load placed on the hosting server. I wanted to use the script to download the photos from Chromasia, which brings me to point number three: some feeds don't link to the large photo, but instead only include a thumbnail. To solve these issues, I created a small application using Google's wonderful App Engine, which takes care of everything. More on how to use this after the jump.

This application takes care of keeping a database of the most recent photos linked to from the provided feeds, thus balancing the load placed on the hosting servers, and also allowing me to serve the feeds in a single standardised format. So far, the script supports the feeds of David Nightingale's Chromasia who kindly agreed to let me download web-scaled versions of his photos for use with this script and also helped me test early versions of the app, and Sam Javanrouh's daily dose of imagery.

There are several ways to use these feeds. The easiest way is through iPhoto 6 or 7. Just subscribe to the iPhoto photo feed for either chromasia (http://deskfeed.neophytou.net/feed/chromasia) or daily dose of imagery (http://deskfeed.neophytou.net/feed/ddoi). There are three down sides to using this method: You have to open iPhoto every day to refresh the feeds manually, every time iPhoto refreshes it has to download all the photos over again, and you can only use the photos from a single feed, not multiple ones. Also in Tiger, I believe you can only use an iPhoto photo feed as a Screen Saver, not as a Desktop Background.

The other way to use this application, is through a small Python script which I wrote. To use this, you need to create a folder called DeskFeed inside the Pictures folder located in your Home directory (resulting path: ~/Pictures/DeskFeed, where ~/ is your home directory). Then, download this script into the folder you just created, and run the following commands in the Terminal (found under Applications/Utilities/Terminal):

chmod a+x ~/Pictures/DeskFeed/deskfeed.py
~/Pictures/DeskFeed/deskfeed.py

This will automatically download the latest photos from the Chromasia and daily dose of imagery blogs, and save them in the DeskFeed folder. To modify which feeds are downloaded (or not), open the deskfeed.py file you just downloaded and comment out / uncomment the photo feeds you want on the first few lines that are clearly marked. You can have as many feeds as you want in the script, but they have to be generated from the App Engine script which I wrote.

To set this up as a periodic task using cron, create a text file on your desktop called dfcron, and in it place this single line:

5      1       *       *       *       /Users/username/Pictures/DeskFeed/deskfeed.py

Make sure you replace username with your own short username. What this file contains is a command that will tell cron to run the deskfeed.py file every day at 5 minutes past 1am. Then, run this command in the terminal once:

crontab ~/Desktop/dfcron

You can now safely delete dfcron. To set this folder as your desktop background, go to System Preferences / Desktop & Screen Saver, press the little '+' button under the list of folders, navigate to the DeskFeed folder, and select Choose. I found that "Fit to Screen" with a black background works best.

I am open to suggestions about what other photo feeds to include in this application, it only takes about 10 minutes to add a new feed. Furthermore, I will be creating a more solid application that will be customisable through preferences rather than relying on a Python script. But as this is a first attempt, I'm publishing the code to the script to see if anyone else has any suggestions for improvement. If you just want to look at the code used instead of downloading the above file, here it is:

#!/usr/bin/env python
import os
import md5
from urllib import URLopener
from urllib2 import urlopen
from xml.dom.minidom import parseString
 
rss_feeds = {}
 
#
# Comment / Uncomment the following lines to use the required feeds
#
rss_feeds['chromasia'] = 'http://deskfeed.neophytou.net/rss/chromasia'
rss_feeds['ddoi'] = 'http://deskfeed.neophytou.net/rss/ddoi'
 
dst_dir = os.path.expanduser('~/Pictures/DeskFeed/')
 
# Based on script found at HackerMoJo.com
# http://hackermojo.com/mt-static/archives/2008/04/metropulse-launched.html
def parseFeed(rss):
  try:
    rss_contents = urlopen( rss )
  except:
    print "Failed to read rss feed %s" % rss
    return
  rss_src = rss_contents.read()
  rss_contents.close()
  dom = parseString( rss_src )
  node_lst = dom.getElementsByTagName('item')
  items = range(node_lst.length)
  hashes = {}
  group = node_lst.item(0).getElementsByTagName('title')[0].getAttribute('group')
  # Store the md5 hashes of all the saved photos
  for i in items:
    img_filename = "%s%s_%s.jpg" % (dst_dir,group,i)
    try:
      image_file = open(img_filename, "r")
      hashes[img_filename] = md5.md5(image_file.read()).hexdigest()
      image_file.close()
    except IOError:
      hashes[img_filename] = ""
      pass
  # Go through all the photos in the rss feed, in reverse order
  items.reverse()
  for i in items:
    element = node_lst.item(i)
    group = element.getElementsByTagName( 'title' )[0].getAttribute('group')
    link = element.getElementsByTagName( 'link' )[0].getAttribute('source')
    photo_hash = element.getElementsByTagName('hash')[0].firstChild.data
    img_filename = "%s%s_%s.jpg" % (dst_dir, group, i)
    # Check all the saved photos and see if any of them have the same hash
    for j in range(i):
      old_img_filename = "%s%s_%s.jpg" % (dst_dir, group, j)
      if (hashes[old_img_filename] == photo_hash):
        # If one does, give it the current photo filename
        os.system("mv %s %s" % (old_img_filename, img_filename))
        hashes[old_img_filename] = ""
        hashes[img_filename] = photo_hash
        break
    try:
      image_file = open(img_filename, "r")
      old_hash = md5.md5(image_file.read()).hexdigest()
      image_file.close()
      if (old_hash == photo_hash):
        continue
    except IOError:
      pass
    try:
      URLopener().retrieve(link, img_filename)
    except IOError:
      continue
 
def main():
  for key in rss_feeds:
    parseFeed(rss_feeds[key])
 
if __name__ == "__main__":
  main()

Download this script

That's pretty much it. As you can see, the script is not that complicated, and I can very easily modify the way the photos are served to the clients. This code has been tested under Leopard using Python 2.5, but also under Ubuntu using Python 2.4.3, and I believe it should also work under Tiger using Python 2.3, but I could very well be wrong. So as always, I'm open to feedback and suggestions. If anyone knows how to create a Preference pane, please contact me. I know exactly what needs to be done in the Preference pane, but I have no idea how to use Xcode.

10 Responses to “Photo feed as Desktop Background”

  1. 1. Comment by Stephen
    on 21 Apr 2008 @ 1:24 am

    The python script seems to be working great (OSX 10.4.11). I like how Chromasia’s photos are labeled. This will become more valuable when a number of feeds are in the mix.

    After I add the script to a cronjob, how do I remove it?

  2. 2. Comment by Constantinos
    on 21 Apr 2008 @ 6:25 pm

    You would have to edit the file /etc/crontab and remove the last entry corresponding to the deskfeed.py file. In the terminal, ( > is the terminal prompt) type the following:

    > vi /etc/crontab

    Then press the down arrow until you’re over the line you wish to delete, press the letter “d” twice, then type this:

    :wq

    (that’s a colon, followed by “w” and then “q“, and hit return), which will write the file and quit the editor.

  3. 3. Comment by Beth
    on 9 Aug 2008 @ 4:52 pm

    :cries:

    I want it for Windoze. ๐Ÿ™

  4. 4. Comment by ahimsauzi
    on 10 Aug 2008 @ 9:10 pm

    I love your script thank you very much for sharing it. I installed it on Leopard screensaver and it works great! One question: as the feed refreshes would I have more images in the folder or some will be overridden? A great feature will be to choose weather to keep all images or just the current feed.

    Thanks again!

  5. 5. Comment by Dan
    on 14 Apr 2009 @ 10:47 pm

    Could you please add this feed? it’s the astronomy picture of the day.

    http://www.acme.com/jef/apod/rss.xml

  6. 6. Comment by drew
    on 25 May 2009 @ 5:08 am

    Ok, stupid question, but should this be able to handle an atom feed?

  7. 7. Comment by BVE
    on 7 Oct 2009 @ 1:57 pm

    [quote post=”110″]Could you please add this feed? itโ€™s the astronomy picture of the day[/quote]

    Just add this line:
    rss_feeds[‘apod’] = ‘http://www.acme.com/jef/apod/rss.xml’

    below this line:
    rss_feeds[‘ddoi’] = ‘http://deskfeed.neophytou.net/rss/ddoi’

    using a good text editor and save the script.

  8. 8. Comment by JC
    on 21 Jul 2010 @ 12:30 am

    Let me know when you fix the script for issues like md5 deprecation, Index out of range, etc. for OSX 10.6.4

  9. 9. Comment by Dalton
    on 22 Jul 2011 @ 8:58 pm

    How would I add this feed instead of the chromasia feed?
    ‘http://feeds.feedburner.com/bingimages’

  10. 10. Comment by Gui
    on 16 Dec 2011 @ 3:45 pm

    Hi,
    I really enjoy your script, congratulations ! On Ubuntu 11.10 with gnome 2.0, it works perfectly for chromasia and ddoi, but when I tried to add another feed like the one from APOD :

    rss_feeds[‘ddoi’] = ‘http://deskfeed.neophytou.net/rss/ddoi’
    rss_feeds[‘apod’] = ‘http://apod.nasa.gov/apod.rss’

    it returns an error :

    ~/Images/DeskFeed$ ./deskfeed.py
    Traceback (most recent call last):
    File “./deskfeed.py”, line 79, in
    main()
    File “./deskfeed.py”, line 76, in main
    parseFeed(rss_feeds[key])
    File “./deskfeed.py”, line 50, in parseFeed
    photo_hash = element.getElementsByTagName(‘hash’)[0].firstChild.data
    IndexError: list index out of range

    do you understand what that means ?
    Thanks !