Skip to content

Instantly share code, notes, and snippets.

@scoffey
Created November 25, 2010 04:49

Revisions

  1. scoffey created this gist Nov 25, 2010.
    89 changes: 89 additions & 0 deletions pytunes.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,89 @@
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-

    """
    This script implements an algorithm for sorting a given iTunes playlist
    according to the least played tracks in each album.
    It aims to identify the tracks that were played the least and to show
    how many times they should be played in order to reach the same playcount
    as the most played track in the album they belong to.
    The program input is the name of the playlist where tracks should be
    taken from and the name of the new playlist to create or replace. The
    output, apart from the new iTunes playlist, is a table showing the
    candidate tracks and the playcount difference used for sorting them.
    """

    __author__ = 'Santiago Coffey'
    __email__ = '[email protected]'
    __version__ = '0.1'
    __license__ = 'GNU General Public License 3'
    __docformat__ = 'restructuredtext en'

    import sys

    import appscript

    iTunes = appscript.app('itunes')

    def make_catalog(tracks):
    """ Makes a music catalog dict by artist+album with track playcounts """
    catalog = dict()
    for track in tracks:
    artist = track.album_artist() or track.artist()
    album = track.album()
    album_list = catalog.get((artist, album), {})
    album_list[track] = track.played_count()
    catalog[(artist, album)] = album_list
    return catalog

    def make_tracks_tuples(album_list, artist, album):
    """ Returns tuples with artist name, album name, track object and
    playcount difference """
    max_playcount = max(album_list.values())
    return [(artist, album, track, max_playcount - n) for track, n \
    in album_list.iteritems() if n < max_playcount]

    def get_candidates(playlist):
    """ Returns candidate tracks data sorted by playcount difference """
    candidates = []
    catalog = make_catalog(playlist.file_tracks())
    for artist_album, album_list in catalog.iteritems():
    tuples = make_tracks_tuples(album_list, *artist_album)
    candidates.extend(tuples)
    candidates.sort(key=lambda x: x[3], reverse=True)
    return candidates

    def show_candidates(candidates):
    """ Prints candidate tracks data as a table """
    template = '\t | '.join(['%s'] * 4)
    for artist, album, track, count in candidates:
    print template % (count, artist, album, track.name())

    def replace_playlist(name, tracks):
    """ Creates or replaces an iTunes playlist with given tracks """
    playlist = iTunes.user_playlists[name]
    if iTunes.exists(playlist): # empty existing playlist
    if len(playlist.tracks()):
    iTunes.delete(playlist.tracks)
    else: # create playlist
    iTunes.make(new=appscript.k.playlist, \
    with_properties={appscript.k.name: name})
    for track in tracks:
    iTunes.duplicate(track, to=playlist)

    def main(program, *args):
    """ Main program: Computes and creates new playlist according to input """
    source = raw_input('Enter your source playlist name: ')
    playlist = iTunes.user_playlists[source]
    if iTunes.exists(playlist):
    destination = raw_input('Enter your destination playlist name: ')
    candidates = get_candidates(playlist)
    show_candidates(candidates)
    replace_playlist(destination, [i[2] for i in candidates])
    else:
    print 'Error: No such iTunes playlist: %s' % source

    if __name__ == '__main__':
    main(*sys.argv)