<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Fuego &#187; Tutorial</title>
	<atom:link href="http://renderthis.wordpress.com/category/tutorial/feed/" rel="self" type="application/rss+xml" />
	<link>http://renderthis.wordpress.com</link>
	<description>a fire not to be put out; his eyes all filled with righteous doubt</description>
	<lastBuildDate>Sun, 03 Feb 2008 04:59:36 +0000</lastBuildDate>
	<generator>http://wordpress.com/</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<cloud domain='renderthis.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://www.gravatar.com/blavatar/a6f9e1345f4fd38127e637d0c82bf54c?s=96&#038;d=http://s.wordpress.com/i/buttonw-com.png</url>
		<title>Fuego &#187; Tutorial</title>
		<link>http://renderthis.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://renderthis.wordpress.com/osd.xml" title="Fuego" />
		<item>
		<title>dual booting the ipod? okay.</title>
		<link>http://renderthis.wordpress.com/2006/08/25/dual-booting-the-ipod-okay/</link>
		<comments>http://renderthis.wordpress.com/2006/08/25/dual-booting-the-ipod-okay/#comments</comments>
		<pubDate>Fri, 25 Aug 2006 13:59:18 +0000</pubDate>
		<dc:creator>renderthis</dc:creator>
				<category><![CDATA[Music]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Review]]></category>
		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">https://renderthis.wordpress.com/2006/08/25/dual-booting-the-ipod-okay/</guid>
		<description><![CDATA[alwaysBETA, a great site with all sorts of random articles (their button is on my blogroll) recently put up an article detailing the installation of rockbox, an alternative non-linux based os, to one&#8217;s ipod.  It&#8217;s pretty involved, but as long as you follow the fairly straight-forward directions you should be fine.  Basically, with [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=renderthis.wordpress.com&blog=277518&post=23&subd=renderthis&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p><a href="http://www.alwaysbeta.com">alwaysBETA</a>, a great site with all sorts of random articles (their button is on my blogroll) recently put up an article detailing the installation of rockbox, an alternative non-linux based os, to one&#8217;s ipod.  It&#8217;s pretty involved, but as long as you follow the fairly straight-forward directions you should be fine.  Basically, with rockbox, you add about 25 games, including Doom, Bejewled, Snood, PACMAN, Snake, Snake 2, Tetris, Asteroids, and a whole bunch of others; as well as support for OGG and FLAC files and a kick-ass list of user-creatable themes to replace that same old apple interface you&#8217;ve gotten bored with, many of which support a &#8220;next song&#8221; feature so you can peak ahead on your shuffle, for example.  Unfortunately for some, this only works with ipods that are 4th gen or newer.  I tried it on my ipod video (5g) and it worked like a charm.  I&#8217;m still deciding on what theme to use, and there&#8217;s a crapload of functionality I need to wrap my head around before I can use it effectively, but it&#8217;s still pretty damn cool.</p>
<p>Basically, you have to install the iPodLinux Loader2 bootloader, then install Rockbox and set it up to automatically detect your mp3s via ID3 tags instead of via file system since Apple screws with the filenames on your iPod.  Then you can install a theme, play a game, run a search, or one of literally hundreds of other available actions, all on your iPod.  Here&#8217;s the <a>link</a>.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/renderthis.wordpress.com/23/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/renderthis.wordpress.com/23/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/renderthis.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/renderthis.wordpress.com/23/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/renderthis.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/renderthis.wordpress.com/23/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/renderthis.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/renderthis.wordpress.com/23/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/renderthis.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/renderthis.wordpress.com/23/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/renderthis.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/renderthis.wordpress.com/23/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=renderthis.wordpress.com&blog=277518&post=23&subd=renderthis&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://renderthis.wordpress.com/2006/08/25/dual-booting-the-ipod-okay/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/32e22af72e382f010fb109cccb39fe66?s=96&#38;d=identicon" medium="image">
			<media:title type="html">renderthis</media:title>
		</media:content>
	</item>
		<item>
		<title>added music-fu</title>
		<link>http://renderthis.wordpress.com/2006/08/07/added-music-fu/</link>
		<comments>http://renderthis.wordpress.com/2006/08/07/added-music-fu/#comments</comments>
		<pubDate>Mon, 07 Aug 2006 04:13:06 +0000</pubDate>
		<dc:creator>renderthis</dc:creator>
				<category><![CDATA[Music]]></category>
		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">https://renderthis.wordpress.com/2006/08/07/added-music-fu/</guid>
		<description><![CDATA[Lifehacker  just posted a second article about Yes.com, mentioning the first one, which I missed.  Basically, if you hear a song on the radio and want to know more about it without the mess of googling the lyrics, go to yes.com, put in the radio station&#8217;s call letters if you know them, or [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=renderthis.wordpress.com&blog=277518&post=19&subd=renderthis&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p><a href="http://www.lifehacker.com/software/music/watch-radio-playlists-in-real-time-192366.php">Lifehacker </a> just posted a second article about Yes.com, mentioning the <a href="http://lifehacker.com/software/top/never-miss-a-song-again-with-yescom-190817.php">first one</a>, which I missed.  Basically, if you hear a song on the radio and want to know more about it without the mess of googling the lyrics, go to yes.com, put in the radio station&#8217;s call letters if you know them, or your zip code if you don&#8217;t (you&#8217;ll have to choose from the available radio stations for your area), and click on the handy &#8220;24 hour look-up&#8221; link.  Scroll through the songs for when you heard it, and voila, you have the artist and song name.  Pretty exciting stuff.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/renderthis.wordpress.com/19/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/renderthis.wordpress.com/19/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/renderthis.wordpress.com/19/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/renderthis.wordpress.com/19/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/renderthis.wordpress.com/19/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/renderthis.wordpress.com/19/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/renderthis.wordpress.com/19/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/renderthis.wordpress.com/19/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/renderthis.wordpress.com/19/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/renderthis.wordpress.com/19/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/renderthis.wordpress.com/19/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/renderthis.wordpress.com/19/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=renderthis.wordpress.com&blog=277518&post=19&subd=renderthis&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://renderthis.wordpress.com/2006/08/07/added-music-fu/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/32e22af72e382f010fb109cccb39fe66?s=96&#38;d=identicon" medium="image">
			<media:title type="html">renderthis</media:title>
		</media:content>
	</item>
		<item>
		<title>music-fu</title>
		<link>http://renderthis.wordpress.com/2006/08/03/music-fu/</link>
		<comments>http://renderthis.wordpress.com/2006/08/03/music-fu/#comments</comments>
		<pubDate>Thu, 03 Aug 2006 23:32:38 +0000</pubDate>
		<dc:creator>renderthis</dc:creator>
				<category><![CDATA[Music]]></category>
		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">https://renderthis.wordpress.com/2006/08/03/music-fu/</guid>
		<description><![CDATA[Situation:  You just heard an amazing song on the TV, say in the background of a commercial, or on your favorite show, and you manage to catch a few odd words, but not the name of the band or the song.  Here&#8217;s how you find it and get the song.
1.    [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=renderthis.wordpress.com&blog=277518&post=17&subd=renderthis&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>Situation:  You just heard an amazing song on the TV, say in the background of a commercial, or on your favorite show, and you manage to catch a few odd words, but not the name of the band or the song.  Here&#8217;s how you find it and get the song.</p>
<p>1.    The first step is to identify the song.</p>
<p>I usually google the name of the show, if you heard it on a show, or the product if it was a commercial, and the words you remember from the song.  For example, let&#8217;s say you heard that song in the background of the Budweiser select commercial.  Something about &#8220;don&#8217;t hold back&#8221;?  Sometimes even googling ["budweiser select" song] will be specific enough, as it is in this case: the first two results both mention &#8220;Galvanize&#8221;, but searching for ["budweiser select" "don't hold back"] yields an affirmation that &#8220;Galvanize&#8221; is, indeed the name of the song, and clicking through to any of the search results will reveal that the song is <strong>Edit: a remix of a Q-Tip song (thanks Dan)</strong> by &#8220;The Chemical Brothers&#8221;.  At this point, you can proceed to the next step.</p>
<p>2.    Making sure it&#8217;s the right song.</p>
<p>In other words, finding any source that has this song &#8211; streaming, or w/e.  At this point, we&#8217;re just verifying, we don&#8217;t actually need the mp3 yet.  For this, we turn to www.singingfish.com.  Since we just want to make sure we have the right song, search for &#8220;Audio &amp; Video&#8221; of &#8220;Any Length&#8221; with all the format and category checkboxes filled.  The search terms are the song name in quotes, or, if it&#8217;s common, the name of the band in quotes.  Since &#8220;Galvanize&#8221; isn&#8217;t a very common song name, we don&#8217;t need to bother searching for the band name, but if we were looking for a more common song name, we&#8217;d need to search for &#8220;Chemical Brothers&#8221; and look for galvanize among the results (notice I left out &#8220;the&#8221;, since sometimes &#8220;the chemical brothers&#8221; are referred to as just &#8220;chemical brothers&#8221;, and searching for &#8220;chemical brothers&#8221; will match both &#8220;chemical brothers&#8221; and &#8220;the chemical brothers&#8221;).  As I said, we&#8217;ll just search for Galvanize this time; we don&#8217;t need quotes since the name of the song is only one word (quotes around a search term means &#8220;find the words between the quotes exactly as they appear&#8221; &#8211; the difference between &#8220;chemical brothers&#8221; and chemical brothers can be a large one, but there&#8217;s no difference between &#8220;galvanize&#8221; and galvanize).  Once you&#8217;re satisfied that you&#8217;ve found the correct song, it&#8217;s time to . . .</p>
<p>3.    Find a downloadable version of the song.</p>
<p>There are several methods I use for this:</p>
<p>www.singingfish.com with &#8220;audio only&#8221;, &#8220;&gt;3 minutes&#8221;, and only &#8220;mp3&#8243; checked;<br />
www.altavista.com with &#8220;&gt; 1 minute&#8221; and only &#8220;mp3&#8243; and &#8220;wav&#8221; checked (others may result in streaming files)<br />
&#8212;search for the song name in quotes<br />
&#8212;search for the band name in quotes and find the song in the results<br />
www.google.com<br />
&#8212;search for ["index of" mp3 -html -htm] and the song name in quotes<br />
&#8212;search for ["index of" mp3 -html -htm] and the band name in quotes, looking for the right song in the results<br />
&#8212;search for the band name in quotes and [torrent], assuming you have a bittorrent client (BitTorrent, Azureus, and uTorrent are popular clients)<br />
&#8212;search for the song name in quotes and [torrent], assuming the same<br />
www.mininova.org,<br />
www.isohunt.com,<br />
www.thepiratebay.org,<br />
www.bittorrent.com<br />
&#8212;assuming you have a bittorrent client, search for the band name (no quotes necessary)<br />
&#8212;assuming same, search for the song name and be prepared to sift through a lot of irrelevant crap to find it</p>
<p>when all else fails, check the following four sources:</p>
<p>the band&#8217;s official website (find via google) and the &#8220;media&#8221; or &#8220;downloads&#8221; section<br />
any fan sites of the band (again, find via google)<br />
the band&#8217;s purevolume website, if they have one, and if any of their songs there are available for download<br />
the band&#8217;s myspace, if they have one, and if any of their songs are available for download</p>
<p>If none of these methods bear fruit, ask your friendly neighborhood google guru to find it for you.</p>
<p>Alternatively, if you have a program that can record speaker output, say <a href="http://audacity.sourceforge.net/">Audacity</a>, and have a lot of time on your hands, whole new worlds are open to you.  You could sign up for the &#8220;25 free plays per month&#8221; plan with Rhapsody (which offers 25 streaming songs for free each month).  You could search in &#8220;Winamp Music&#8221; under &#8220;Online Media&#8221; in Winamp, or even &#8220;Winamp Videos&#8221;, since you&#8217;re only recording the audio anyway.  You can record any of the streaming music you found via singingfish or altavista.  Songs on the band&#8217;s website which aren&#8217;t available for download can nevertheless be recorded.  Granted, the quality won&#8217;t be as good, but it&#8217;s better than nothing.</p>
<p>Happy hunting.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/renderthis.wordpress.com/17/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/renderthis.wordpress.com/17/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/renderthis.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/renderthis.wordpress.com/17/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/renderthis.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/renderthis.wordpress.com/17/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/renderthis.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/renderthis.wordpress.com/17/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/renderthis.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/renderthis.wordpress.com/17/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/renderthis.wordpress.com/17/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/renderthis.wordpress.com/17/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=renderthis.wordpress.com&blog=277518&post=17&subd=renderthis&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://renderthis.wordpress.com/2006/08/03/music-fu/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/32e22af72e382f010fb109cccb39fe66?s=96&#38;d=identicon" medium="image">
			<media:title type="html">renderthis</media:title>
		</media:content>
	</item>
		<item>
		<title>iTunes and Python: Creating Custom Playlists: UPDATE</title>
		<link>http://renderthis.wordpress.com/2006/06/28/itunes-and-python-creating-custom-playlists-update/</link>
		<comments>http://renderthis.wordpress.com/2006/06/28/itunes-and-python-creating-custom-playlists-update/#comments</comments>
		<pubDate>Wed, 28 Jun 2006 20:47:25 +0000</pubDate>
		<dc:creator>renderthis</dc:creator>
				<category><![CDATA[Music]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">https://renderthis.wordpress.com/2006/06/28/itunes-and-python-creating-custom-playlists-update/</guid>
		<description><![CDATA[All right, the other day I posted a tutorial on manipulating the iTunes COM with Python to give you better searching capabilities than iTunes does natively, and how to make a playlist with the resulting tracks.  Since then, I&#8217;ve managed to change the code a bit because there were a couple of places where [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=renderthis.wordpress.com&blog=277518&post=11&subd=renderthis&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>All right, <a href="http://renderthis.wordpress.com/2006/06/27/itunes-and-python-creating-custom-playlists/">the other day</a> I posted a tutorial on manipulating the iTunes COM with Python to give you better searching capabilities than iTunes does natively, and how to make a playlist with the resulting tracks.  Since then, I&#8217;ve managed to change the code a bit because there were a couple of places where it wasn&#8217;t working correctly before (eg including duplicate tracks) and some features I felt were missing.  So let&#8217;s see the changes, shall we?</p>
<pre>def union(l1, l2):
    pl1 = [i.GetITObjectIDs() for i in l1]
    pl2 = [i.GetITObjectIDs() for i in l2]
    pl3 = sorted(list(set(pl1+pl2)),
                 key=lambda x: list(pl1+pl2).index(x))
    return [list(l1+l2)[i] for i in
            [list(pl1+pl2).index(x) for x in pl3]]

def andnot(l1, l2):
    pl1 = [i.GetITObjectIDs() for i in l1]
    pl2 = [i.GetITObjectIDs() for i in l2]
    pl3 = [i for i in pl1 if i not in pl2]
    pl3 = sorted(list(set(pl3)),
                 key=lambda x: pl3.index(x))
    return [l1[i] for i in [pl1.index(x) for x in pl3]]

def intersect(l1, l2):
    pl1 = [i.GetITObjectIDs() for i in l1]
    pl2 = [i.GetITObjectIDs() for i in l2]
    pl3 = [i for i in pl1 if i in pl2]
    pl3 = sorted(list(set(pl3)),
                 key=lambda x: pl3.index(x))
    return [l1[i] for i in [pl1.index(x) for x in pl3]]

def difference(l1, l2):
    pl1 = [i.GetITObjectIDs() for i in l1]
    pl2 = [i.GetITObjectIDs() for i in l2]
    pl3 = [[i for i in pl1 if i not in pl2],
           [i for i in pl2 if i not in pl1]]
    pl3 = [sorted(list(set(pl3[0])),
                  key=lambda x: pl3[0].index(x)),
           sorted(list(set(pl3[1])),
                  key=lambda x: pl3[1].index(x))]
    return [l1[i] for i in
            [pl1.index(x) for x in pl3[0]]] +
           [l2[i] for i in
            [pl2.index(x) for x in pl3[1]]]</pre>
<p>Okay, before I talked about how with &#8220;OR&#8221;, all you had to do was add the two lists together, but that sometimes means duplicates, so I wrote a <code>union</code> function to fix that.  Let&#8217;s take a look.  Like the other functions, it takes in two lists/<code>IITTrackCollections</code> and makes parallel lists containing the IDs of the tracks, this time.  Next, it makes a set of the two lists together.  However, since sets are unordered, when making pl3 a list again, it sorts it by the index of each track in the combined list, returning it to the correct order.  Then it performs the same last step as the others, returning a list which contains the corresponding tracks to the IDs in pl3.</p>
<p>The only change to <code>intersect</code> and <code>difference</code> is a similar fix to pl3.  In addition to the previous construction of pl3, I&#8217;ve added the same sorting of the list resulting of the set made from pl3.</p>
<p>I also added an <code>andnot</code> function to allow the &#8220;not&#8221; operator &#8211; ie find tracks that match one query, but not another.</p>
<p>Also, as you can see, the duplicates are removed via track ID instead of name, now, so that multiple songs with the same name by different artist, for example, aren&#8217;t weeded out.</p>
<p>In addition, I changed <code>custompl</code> to allow for searching not limited to the default searches.  For example, you can now search within the year of a track, or its comment, or even the genre or date added, assuming you know the correct format (the field for date added, for example, is &#8220;DateAdded&#8221; &#8211; all the possible fields can be found in the help file for the iTunes COM).</p>
<pre>def extend(alist, element):
    alist.extend(element)
    return alist

def custompl(quer1, logic, quer2):
    res = []
    temp = [quer1,quer2]
    if len(quer1) == 3:
        if len(quer2) == 3:
            return dic[logic](custompl(*quer1),
                              custompl(*quer2))
        res.append(custompl(*quer1))
        temp.remove(quer1)
    elif len(quer2) == 3:
        res.append(custompl(*quer2))
        temp.remove(quer2)
    for query in temp:
        srchstr, field = query
        if field == "Playlist":
            res = res+[reduce(extend,extend(res[0:0],
                      [list(pl.Tracks)
                          for pl in source.Playlists
                              if srchstr in pl.Name]))]
        elif field in dic:
            res.append(list(library.Search(srchstr,
                dic[field])))
        else:
            try:
                res.append([track
                    for track in library.Tracks
                        if srchstr in eval(
                            "track.%s"%field)])
            except TypeError:
                res.append([track
                    for track in library.Tracks
                        if srchstr in repr(
                            eval("track.%s"%field))])
            except AttributeError:
                __import__("sys").stderr.write("ValueError"+
                    ": Sorry, can't find %s field to search
                    "in.&#92;n"%field)
    return dic[logic](res[0],res[1])</pre>
<p>As you can see, instead of just checking for &#8220;Playlist&#8221; as a field, <code>custompl</code> now also checks if the field is in the dictionary, and if it&#8217;s not, tries to find it by accessing it with <code>eval</code>.  AGAIN, THIS SHOULD NOT BE ATTEMPTED UNLESS YOU TRUST YOUR END USER.  For example, by making the following function call, &#8220;bad&#8221; gets written to your stdout; and with a few modifications, all sorts of havoc can be wreaked: <code>customplaylist((" ", '__setattr__("Name",__import__("sys").stdout.write("bad"))' ))</code> (Note that this doesn&#8217;t actually change the name of any tracks, but rather raises an error after writing to the stdout.)  Alternatively, you could make a dictionary with all the possible fields to search in and check if the field is in that dictionary and work through it that way to avoid eval.</p>
<p>In any case, first we try to see if the search string is in the field, then, if the field isn&#8217;t a string (eg the <code>DateAdded</code> field, which is a <code>time</code> instance), we see if the search string is in Python&#8217;s representation of the field (<code>'&lt;PyTime:4/17/2006 12:05:32 AM&gt;'</code> for a sample <code>DateAdded</code>, for example).  At some point I&#8217;d like to see if I can make it possible to specify whether you want exact pattern matching for each query, ie <code>if srchstr == eval(...)</code> or <code>if srchstr == repr(eval(...))</code>.  Anyway, if the field isn&#8217;t available, we write the error code to stderr without stopping the program.</p>
<p>You&#8217;ll notice that I changed the treatment of playlists a bit.  This is because the previous method only kept the tracks in the first playlist found.  This way, all the tracks from all the matching playlists are added together, then that list is added to res.  The way I acheived this is by taking an empty list (<code>res[0:0]</code>), and using <code>reduce</code> to extend it by the list of tracks from each matching playlist.  Then, I added the current res to it, making sure we didn&#8217;t lose the tracks already in it.  Since we add <code>[reduce(...)]</code>, it keeps all the tracks from this query in a separate list, like it should.  The way <code>reduce</code> works is to apply the first parameter (a function) to the elements of the second one in twos.  For example, <code>reduce(operator.add,range(10))</code> is the equivalent of <code>(((((((((0+1)+2)+3)+4)+5)+6)+7)+8)+9)</code>.  So our code is the equivalent of <code>extend(extend(extend(res[0:0],tracklist1),<br />
tracklist2),tracklist3)</code> etc.  So we contain that in a list (<code>[reduce(...)]</code>) and add <code>res</code> to it to get the new <code>res</code>.</p>
<p>I also modified the main function to allow for single query searches:</p>
<pre>def customplaylist(querylist,title=None):
    pls = []
    if type(querylist) is str:
        querylist = (eval(
            querylist.replace(" XOR ",", 'X',"
                     ).replace(" AND ",", '+',"
                     ).replace(" OR ",", '|',"
                     ).replace('" in "',"', '"
                     ).replace('"',"'"
                     ).replace(" AND NOT ",", '+!',")))
    if len(querylist) != 1:
        querylist = [querylist]
    try:
        pls.extend(custompl(*querylist[0]))
    except TypeError:
        res = []
        srchstr, field = querylist[0]
        if field == "Playlist":
            res.extend(reduce(extend,
                extend(res[0:0],
                    [list(pl.Tracks)
                        for pl in source.Playlists
                            if srchstr in pl.Name])))
        elif field in dic:
            res.extend(list(library.Search(srchstr,
                dic[field])))
        else:
            try:
                res.extend([track
                    for track in library.Tracks
                        if srchstr in repr(
                            eval("track.%s"%field)
                                           )])
            except TypeError:
                res.extend([track
                    for track in library.Tracks
                        if srchstr == repr(
                            eval("track.%s"%field)
                                           )])
            except AttributeError:
               __import__("sys").stderr.write("ValueError"+
               ": Sorry, can't find %s field to search in."+
               "&#92;n"%field)
        pls.extend(res)
    plname = str(querylist).replace(", 'X',"," XOR "
                          ).replace(", '+',"," AND "
                          ).replace(", '|',"," OR "
                          ).replace("', '","' in '"
                          ).replace("[",""
                          ).replace("]",""
                          ).replace(", '+!',"," AND NOT")
    cpl = win32com.client.CastTo(
              iTunes.CreatePlaylist(title or plname),
              'IITUserPlaylist')
    for track in pls:
        cpl.AddTrack(track)</pre>
<p>We first try to send <code>querylist</code> to <code>custompl</code>, but if there&#8217;s only one query, it&#8217;ll raise a TypeError, which we catch; and then we proceed to copy the code from <code>custompl</code>, slightly modified.  The only difference is that, since there&#8217;s only one query, we don&#8217;t need the crap at the beginning of <code>custompl</code>, nor do we need as much fancy footwork to make <code>res</code> have two elements, one from each side.  We just <code>extend</code> <code>res</code> by all the tracks found.</p>
<p>Finally, we just need to update <code>dic</code>, and we&#8217;re on our way.</p>
<pre>dic = {"All":0,"Visible":1,"Artist":2,"Album":3,
       "Composer":4,"Name":5,"+":intersect,
       "|":union,"X":difference,"+!":andnot}</pre>
<p>All right.  Head over <a href="http://rafb.net/paste/results/GP1tZc81.nln.html">here</a> to get my updated full code.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/renderthis.wordpress.com/11/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/renderthis.wordpress.com/11/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/renderthis.wordpress.com/11/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/renderthis.wordpress.com/11/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/renderthis.wordpress.com/11/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/renderthis.wordpress.com/11/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/renderthis.wordpress.com/11/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/renderthis.wordpress.com/11/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/renderthis.wordpress.com/11/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/renderthis.wordpress.com/11/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/renderthis.wordpress.com/11/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/renderthis.wordpress.com/11/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=renderthis.wordpress.com&blog=277518&post=11&subd=renderthis&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://renderthis.wordpress.com/2006/06/28/itunes-and-python-creating-custom-playlists-update/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/32e22af72e382f010fb109cccb39fe66?s=96&#38;d=identicon" medium="image">
			<media:title type="html">renderthis</media:title>
		</media:content>
	</item>
		<item>
		<title>iTunes and Python: Creating Custom Playlists</title>
		<link>http://renderthis.wordpress.com/2006/06/27/itunes-and-python-creating-custom-playlists/</link>
		<comments>http://renderthis.wordpress.com/2006/06/27/itunes-and-python-creating-custom-playlists/#comments</comments>
		<pubDate>Tue, 27 Jun 2006 06:13:26 +0000</pubDate>
		<dc:creator>renderthis</dc:creator>
				<category><![CDATA[Music]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">https://renderthis.wordpress.com/2006/06/27/itunes-and-python-creating-custom-playlists/</guid>
		<description><![CDATA[A blow-by-blow breakdown of how to use the iTunes COM to search for and make playlists out of songs that meet specific criteria through Python (much more complex than the native iTunes search capabilities).  I&#8217;ll be doing the step-through in a rather legible, but not very line-efficient method; also, I&#8217;d like to apologize in [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=renderthis.wordpress.com&blog=277518&post=8&subd=renderthis&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>A blow-by-blow breakdown of how to use the iTunes COM to search for and make playlists out of songs that meet specific criteria through Python (much more complex than the native iTunes search capabilities).  I&#8217;ll be doing the step-through in a rather legible, but not very line-efficient method; also, I&#8217;d like to apologize in advance for the horrible formatting &#8211; the columns in this theme have a fixed size, so text just gets cut off if it&#8217;s too long; my actual final script is available here (rename the resulting file to .py instead of .py.txt): <a href="http://renderthis.files.wordpress.com/2006/06/customplpy.txt">custompl.py</a>.  If you want to look without downloading, it&#8217;s also up <a href="http://rafb.net/paste/results/zAPtTa30.nln.html">here</a>.</p>
<p>If you don&#8217;t have the iTunes SDK, get it <a href="http://developer.apple.com/sdk/itunescomsdk.html" title="iTunes COM SDK">here</a>.  If you don&#8217;t have Python, or don&#8217;t know what it is, check <a href="http://en.wikipedia.org/wiki/Python_programming_language" title="Python Programming Language">here</a>  (Wikipedia entry) and <a href="http://www.python.org" title="Python Official Homepage">here</a> (Python&#8217;s official homepage).</p>
<p>If you already know what you&#8217;re doing with Python, skip to the code below.  Once you have Python installed, fire up your favorite IDE, or just use IDLE, the one Python&#8217;s bundled with, and start typing.  If you use IDLE, it will be an interactive prompt, so while you can see the results of what you do immediately, you can&#8217;t very easily save what you do directly from the prompt.  Instead, you should go to File -&gt; New Window and save the blank page as &#8220;&lt;insert script name here&gt;.py&#8221;.  This will highlight everything for you the same way the interactive prompt will.  I suggest doing both &#8211; experimenting with the prompt, then saving what works &#8211; but you can do it either way.</p>
<pre>import win32com.client
iTunes = win32com.client.gencache.EnsureDispatch(
         "iTunes.Application")</pre>
<p>Okay, first thing we have to do is <code>import win32com.client</code>, so that we can access the iTunes COM.  Next, we connect to iTunes, using <code>win32com.client</code>&#8217;s <code>gencache.EnsureDispatch</code> so that we don&#8217;t get the funky behavior that <code>Dispatch</code> can sometimes cause.</p>
<pre>source = iTunes.LibrarySource
library = iTunes.LibraryPlaylist</pre>
<p>All right, now we have the iTunes source (contains all the playlists) and the main Library playlist.  Now, if you want to search for songs using the iTunes COM, you have to specify which fields to search in as well as the search string (this is documented in the help file that comes with the iTunes SDK).  So what we&#8217;re going to do is make a dictionary that will convert the string parameter of which field to search in into a number that iTunes can understand:</p>
<pre>field_dict = {"All":0,"Visible":1,"Artist":2,
              "Album":3,"Composer":4,"Name":5}</pre>
<p>We&#8217;ll worry about searching in playlist names later.  Basically, the default choices available for searching iTunes are All fields, all Visible fields, Artist, Album, Composer, or Name of the song.  All right, time to get down to some actual searching &#8211; just one query at a time to start with (we&#8217;ll add multiple searches at once later, and smart titling at the same time):</p>
<pre>custom_playlist = iTunes.CreatePlaylist("untitled")
custom_playlist = win32com.client.CastTo(custom_playlist,
                                         'IITUserPlaylist')
query = "Imogen Heap"
field = "Artist"
results = library.Search(query, field_dict[field]))</pre>
<p>All right, first we used iTunes to create an untitled playlist. Now, unfortunately, the default when creating a playlist this way, for some reason, doesn&#8217;t give you access to all the methods that the documentation suggests. So you have to use <code>win32com.client.CastTo</code> to, well, cast the result to a proper iTunes <code>IITUserPlaylist</code> (documented, again, in the help file). Next we can perform the search and start populating the playlist.  Okay, so we searched the library for any tracks whose artist is <a href="http://myspace.com/imogenheap">Imogen Heap</a> &#8211; a freakishly good singer, by the way &#8211; she used to be part of a band called <a href="http://myspace.com/froufrou">Frou Frou</a> before going solo . . . Coming back to the code, all we have left to do is populate the playlist we made with the tracks we found.</p>
<pre>for track in tracks:
    custom_playlist.AddTrack(track)</pre>
<p>And that&#8217;s it.  Those are the basics.  To be able to use multiple queries, quite a few additions to the above basics need to be made.  We&#8217;ll start with adding logical operators &#8211; because there&#8217;s nothing more irritating than having a program assume you want all of the conditions to be met when there&#8217;s a specific order to which you want and which you can do without:</p>
<pre>field_dict = {"All":0,"Visible":1,"Artist":2,"Album":3,
              "Composer":4,"Name":5,"+":intersect,
              "|":__import__("operator").add,"X":difference}</pre>
<p>First thing we have to do is update the dictionary to allow for the logical operators: &#8220;+&#8221; for AND, &#8220;|&#8221; for OR, &#8220;X&#8221; for XOR.  For those not familiar with logical operators, AND, appropriately enough, requires the fulfillment of both conditions (a AND b), OR requires one or the other or both, and XOR (aka exclusive or) requires one or the other but not both.  Now, this may still seem a bit odd as there are no &#8220;intersect&#8221; or &#8220;difference&#8221; functions yet, and what&#8217;s this business with <code>__import__</code>?  Well, we&#8217;ll work backwards.  The expression <code>__import__("operator").add</code> is roughly equivalent to <code>operator.add</code>, once <code>operator</code> has been imported.  I say roughly because <code>__import__</code> doesn&#8217;t permanently import its parameter, it merely allows one to access the method of a class not in the present namespace.  Anyway, by putting this reference to <code>operator.add</code> in <code>field_dict</code>, we can achieve the following: <code>field_dict["|"](a,b)</code> will produce the same as <code>a+b</code>.  Now, just to be perfectly clear, this is NOT the same as <code>field_dict["+"](a,b)</code>.  I&#8217;m talking about putting <code>a+b</code> in the interactive prompt; actually adding them together.  There&#8217;s a reason for this, which I&#8217;ll explain in due time.  First, to define intersect and difference:</p>
<pre>def intersect(tracks1, tracks2):
    names1 = [i.Name for i in tracks1]
    names2 = [i.Name for i in tracks2]
    names3 = [i for i in names1 if i in names2]
    return [tracks1[i] for i in
              [names1.index(x) for x in names3]]

def difference(tracks1, tracks2):
    names1 = [i.Name for i in l1]
    names2 = [i.Name for i in l2]
    names3 = [[i for i in names1 if i not in names2],
              [i for i in names2 if i not in names1]]
    return [tracks1[i] for i in
              [names1.index(x) for x in names3[0]]]+
           [tracks2[i] for i in
              [names2.index(x) for x in names3[1]]]</pre>
<p>Well, well.  This is confusing indeed.  Let&#8217;s take a look at intersect first.  This is meant to mirror the intersect method of the built in <code>set</code> class.  There are two parameters: each a list of tracks or an <code>IITTrackCollection</code>, composed of <code>IITTracks</code>.  Each <code>IITTrack</code> has a <code>Name</code> attribute, so the first thing we do is make some parallel lists containing the <code>Names</code> of the tracks in the arguments.  Once these have been made, we make a list of tracks which are in both.  Then, we make and return a list of the actual tracks (as opposed to just the <code>Names</code>) which are represented by the <code>Names</code> in the list we just made.  Simple enough, right?</p>
<p>Okay, now we get to the really confusing bit.  Again, this is meant to mirror the set method of the same name.  The parameters are the same as in the previous function, as is the next step.  Once we have two lists of <code>Names</code> which correspond to the provided lists, we make a third list which has two elements.  The first is a list of all the <code>Names</code> in the first <code>Names</code> list which don&#8217;t appear in the second, and the second element is the reverse.  With this in hand, we proceed to perform the same step as in the last function &#8211; using the <code>Names</code> we want to refer back to the original <code>IITTracks</code>.  The only difference is that, since this time we have two lists to refer from (<code>names3[0]</code> and <code>names3[1]</code>), we have to collect the <code>IITTracks</code> from both and add them together.  All right, now we can get to the fun part: parsing user input.</p>
<pre>def customplaylist(querylist,title=None):
    pls = []
    if type(querylist) is str:
        querylist = (eval(querylist.replace(" XOR ",", 'X',"
                                  ).replace(" AND ",", '+',"
                                  ).replace(" OR ",", '|',"
                                  ).replace('" in "',"', '"
                                  ).replace('"',"'")))
    if len(querylist) != 1:
        querylist = [querylist]
    pls.extend(custompl(*querylist[0]))
    plname = str(querylist).replace(", 'X',"," XOR "
                          ).replace(", '+',"," AND "
                          ).replace(", '|',"," OR "
                          ).replace("', '","' in '"
                          ).replace("[",""
                          ).replace("]","")
    cpl = win32com.client.CastTo(
              iTunes.CreatePlaylist(title or plname),
              'IITUserPlaylist')
    for track in pls:
        cpl.AddTrack(track)</pre>
<p>Okay, one thing at a time.  Some of this should look familiar, since we&#8217;ve used some of the pieces before.  First off, we take in a list of queries, which should look vaguely like this: <code>("&lt;search string&gt;", "&lt;field&gt;")</code> or <code>'"&lt;search string&gt;" in "&lt;field&gt;"'</code>.  The queries will be interspersed with logical operators where necessary.  To see a working example of a call to <code>customplaylist</code>, look at the bottom of the final code, which is available by clicking on the link at the top of this post.</p>
<p>The second parameter is a title, should the user want to name the playlist something other than the string form of <code>querylist</code>, which tends to look like <code>'(("Toasty" in "Playlist") XOR ("Something" in "Artist")) AND (("Sum" in "Playlist") OR ("Killer" in "Album"))'</code>, which is the &#8220;smart titling&#8221; I was talking about before.  (Another of the features of the final product is that the search acts like an iTunes search, providing matches of &#8220;All Killer, No Filler&#8221;, say, for the search string of &#8220;Killer&#8221;.  This means the user gets to be all kinds of lazy when searching.)</p>
<p>Our next step is to create an empty list, which will eventually hold the <code>IITTracks</code> we want to populate our playlist with.  What follows that is an ugly way to parse the string version of <code>querylist</code>: you replace all the human legible stuff with the Python equivalent where it needs to be, then <code>eval</code> it so it&#8217;s a list and not a string.  There are hundreds of rants and essays about this everywhere, so I&#8217;ll just put a small word in here.  DO NOT USE THIS TECHNIQUE IF YOU DON&#8217;T TRUST THE END USER ON YOUR COMPUTER.  <code>eval</code> has the potential to do a lot of damage to your computer in the hands of the right, or rather, wrong, person.  If you don&#8217;t trust the end user, just change the function to look like this, and it won&#8217;t support strings anymore:</p>
<pre>def customplaylist(querylist,title=None):
    pls = []
    if len(querylist) != 1:
        querylist = [querylist]
    pls.extend(custompl(*querylist[0]))
    plname = str(querylist).replace(", 'X',"," XOR "
                          ).replace(", '+',"," AND "
                          ).replace(", '|',"," OR "
                          ).replace("', '","' in '"
                          ).replace("[",""
                          ).replace("]","")
    cpl = win32com.client.CastTo(
              iTunes.CreatePlaylist(title or plname),
              'IITUserPlaylist')
    for track in pls:
        cpl.AddTrack(track)</pre>
<p>Okay, now the next thing we see here is that we check the length of <code>querylist</code>, and if it&#8217;s not 1, we make it one by making it the only element of a new list.  Why?  Because the <code>custompl</code> function, which we haven&#8217;t seen yet, works recursively and requires there to be one group to begin with.  That much is fairly straightforward.</p>
<p>Next, though, we see an odd call to <code>custompl</code>.  What the hell does it mean when there&#8217;s an asterisk in front of a parameter?  Well, when that parameter is a list, it separates the elements of that list so that each element is treated as a separate argument by the function you&#8217;re calling.  For example, let&#8217;s say you have a function <code>printparams</code> that takes in three parameters, <code>a</code>, <code>b</code>, and <code>c</code> and prints them out.  And let&#8217;s further say you had a list, <code>foo</code>, with elements <code>"b"</code>, <code>"a"</code>, and <code>"r"</code>.  The function call to <code>printparams</code> would look like this: <code>printparams(*foo)</code>, and would output <code>b a r</code>.  Instead of branching off into <code>custompl</code> here,  I&#8217;m going to finish up the rest of the main function, and then talk about what&#8217;s going on behind the scenes.</p>
<p>Once we&#8217;ve finished collecting the tracks we want, we make the name of the playlist by reversing what we saw before for parsing the string version of <code>querylist</code>.  Then, we create the playlist and cast it to an <code>IITUserPlaylist</code> in one step.  The only thing you haven&#8217;t seen is <code>title or plname</code>.  The way Python treats <code>True</code> and <code>False</code>, <code>None</code> is equivalent to <code>False</code>, so if the user doesn&#8217;t enter a title in the parameter list, we end up with <code>None or plname</code>, which Python evaluates to get <code>plname</code>.  This works even if a title has been provided because Python only evaluates the second half of an <code>and</code> expression if the first condition is false.  After that, like you saw before, we add our tracks to the playlist and that&#8217;s that.</p>
<p>All right, so the last thing to look at here is <code>custompl</code>, the function we&#8217;re using to really parse <code>querylist</code> and return the right tracks:</p>
<pre>def custompl(query1, logic, query2):
    results = []
    temp = [query1,query2]
    if len(query1) == 3:
        if len(query2) == 3:
            return field_dict[logic](custompl(*query1),
                                     custompl(*query2))
        results.append(custompl(*query1))
        temp.remove(query1)
    elif len(query2) == 3:
        results.append(custompl(*query2))
        temp.remove(query2)
    for query in temp:
        search_string, field = query
        if field == "Playlist":
            results.extend([list(pl.Tracks)
                            for pl in source.Playlists
                               if search_string in pl.Name])
        else:
            results.append(list(
                           library.Search(search_string,
                                        field_dict[field])))
    return field_dict[logic](results[0],results[1])</pre>
<p>Whoo boy, here we go.  Remember how I said that the asterisk makes the function treat the list as its component elements?  Well here&#8217;s where it comes into play.  When it comes down to it, every group, no matter how complicated, has three parts.  The left side, the logical operator, and the right side.  So first we make an empty list to hold our results.  Then we make another list to hold the left and right side.  Then we check each side to see if it is, itself, a group, or if it&#8217;s really a query (remember, groups have three components, and queries have two: search string and field).  If they&#8217;re both groups, it applies the function that corresponds with the operator on the result of breaking down the group again by running *it* through the function.  And so on until they both aren&#8217;t groups anymore.  Once only the first one is a group, it adds the results of breaking down that group to the main results and removes it from <code>temp</code> to let the function know not to try to treat it as a query.  On the other hand, if only the *right* side is a group, it does the same on that side.  Once there are no more groups, it gets to the <code>for</code> loop.  This looks at whichever side is left, or both, if they happened to have the same level of grouping, and breaks it down into search string and field. </p>
<p>Here&#8217;s where we deal with searching in Playlist names.  We <code>extend</code> the results by the list of <code>Tracks</code> of each Playlist whose name even partially matches the search string.  What this means is, it adds each track in that list to the end of <code>results</code>, one by one, instead of just adding the whole list as a list.  This way, all the tracks on this side are at the same level.</p>
<p>Now, if we&#8217;re *not* searching in the Playlist name, we do what we did way back when.  We search the library for the search string in the field, and <code>append</code> the list to <code>results</code>, or add it to the end.  Now we should have two elements in <code>results</code>: one from each side.  Even if there&#8217;s an uneven level of grouping on each side, the way we set it up, we still have two elements.  Remember?  If one side is a group and the other isn&#8217;t, we add the results of breaking it down to results, then get to here and add the results of breaking the non-group side down.  Two elements.  If it&#8217;s hard for you to visualize, try coming up with the most convoluted <code>querylist</code> you can imagine and working through <code>custompl</code> to see how it works.  By the way, this type of programming, wherein a function calls itself, is called &#8220;Recursive&#8221; programming.  To see simpler examples of this, just google &#8220;recursive&#8221; and the programming language of your choice.  Since we have two elements now, we can apply the logical operator to them to get what we want.  Oh yeah, I never explained why &#8220;OR&#8221; corresponds to &#8220;+&#8221;.  Well, think about it.  &#8220;OR&#8221; means &#8220;one or the other or both&#8221;.  Which means that if we have two lists, every track in each list will be in either one, or the other, or both.  We don&#8217;t have to do anything fancy to them, just add them together.</p>
<p>Anyway, that&#8217;s the whole cake.  Once you have the results, you return them back to the main function, where we <code>extend</code> <code>pls</code> by the results, and add the tracks to our iTunes playlist.</p>
<p>Whew!  That was a mouthful.</p>
<img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/renderthis.wordpress.com/8/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/renderthis.wordpress.com/8/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/renderthis.wordpress.com/8/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/renderthis.wordpress.com/8/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/renderthis.wordpress.com/8/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/renderthis.wordpress.com/8/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/renderthis.wordpress.com/8/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/renderthis.wordpress.com/8/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/renderthis.wordpress.com/8/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/renderthis.wordpress.com/8/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/renderthis.wordpress.com/8/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/renderthis.wordpress.com/8/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=renderthis.wordpress.com&blog=277518&post=8&subd=renderthis&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://renderthis.wordpress.com/2006/06/27/itunes-and-python-creating-custom-playlists/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/32e22af72e382f010fb109cccb39fe66?s=96&#38;d=identicon" medium="image">
			<media:title type="html">renderthis</media:title>
		</media:content>
	</item>
	</channel>
</rss>