import errno import os import pythoncom import sys import time import win32com.client # TODO - put these into a config file? Command line args? ENCODER = "MP3 Encoder" BITRATE = 128 CONVERSION_PLAYLIST = "To Convert" DELETE_FILE = True CONSOLIDATE = True VERBOSE = True CONVERSON_PROGRESS_INTERVAL = 10 def main(): iTunes = win32com.client.gencache.EnsureDispatch("iTunes.Application") # Set encoder and bit rate original_encoder = iTunes.CurrentEncoder iTunes.CurrentEncoder = iTunes.Encoders.ItemByName(ENCODER) #original_bitrate = ??? #iTunes.??? = BITRATE if VERBOSE: print 'encoder set to', ENCODER #, BITRATE # Build a mapping of all existing tracks accessable by unique ID if VERBOSE: print "building full library mapping..." library = iTunes.LibraryPlaylist library_tracks = dict((get_track_unique_id(track), track) for track in com_collection_iter(library.Tracks)) if VERBOSE: print "built" # Build a mapping of track's user playlists accessable by unique ID if VERBOSE: print "building track to playlist mapping..." track_playlists = dict() for playlist in com_collection_iter(iTunes.LibrarySource.Playlists): #We don't want the conversion playlist itself, and we only want user playlists if playlist.Name == CONVERSION_PLAYLIST or \ playlist.Kind != win32com.client.constants.ITPlaylistKindUser: continue playlist = win32com.client.CastTo(playlist, 'IITUserPlaylist') # We don't want smart playlists if playlist.Smart: continue tracks = playlist.Tracks for track in (tracks.Item(index) for index in range(1, tracks.Count+1)): track_playlists.setdefault((get_track_unique_id(track)), list()).append(playlist) if VERBOSE: print "built" # Process all tracks in the playlist conversion_playlist = iTunes.LibrarySource.Playlists.ItemByName(CONVERSION_PLAYLIST) converted_tracks = 0 if VERBOSE: track_count = conversion_playlist.Tracks.Count for index, old_track in enumerate(list(com_collection_iter(conversion_playlist.Tracks))): if VERBOSE: print index+1, 'of', track_count, '-', \ old_track.Artist, '-', \ old_track.Album, '-', \ old_track.Name, \ ', bitrate: ', old_track.BitRate if old_track.BitRate != BITRATE: old_track_via_library = library_tracks[get_track_unique_id(old_track)] old_track_location = win32com.client.CastTo(old_track_via_library, 'IITFileOrCDTrack').Location # Convert the track to use the encoder specified above. conversion_status = iTunes.ConvertTrack2(old_track) while conversion_status.InProgress: # Wait 'till it's finished if VERBOSE: print 'converting', \ old_track.Artist, '-', \ old_track.Album, '-', \ old_track.Name, '...' time.sleep(CONVERSON_PROGRESS_INTERVAL) # Set new track metadata from old track new_track = conversion_status.Tracks.Item(1) new_track.PlayedCount = old_track.PlayedCount new_track.PlayedDate = old_track.PlayedDate new_track.Rating = old_track.Rating new_track.VolumeAdjustment = old_track.VolumeAdjustment + 1 new_track.Enabled = old_track.Enabled # Add new track into old track's playlists for playlist in track_playlists.get(get_track_unique_id(old_track), []): if VERBOSE: print 'adding to playlist', playlist.Name playlist.AddTrack(new_track) # Remove old track. (We need to use the one found via the library, # 'cos if we call .Delete() on the one found via the playlist, we # only remove the track from that playlist. We also need to # explicitly delete the file.) old_track_via_library.Delete() if DELETE_FILE: try: os.remove(old_track_location) except OSError, os_error: if os_error.errno != errno.EACCES: raise else: if VERBOSE: print 'making', old_track_location, 'writable...' os.chmod(old_track_location, 0777) os.remove(old_track_location) if VERBOSE: print 'deleted', old_track_location converted_tracks += 1 if VERBOSE: print 'converted', converted_tracks, 'tracks' #if CONSOLIDATE: # iTunes.???() # if VERBOSE: print "library consiolidated" # Restore old encoder and bit rate iTunes.CurrentEncoder = original_encoder #iTunes.??? = original_bitrate if VERBOSE: print 'encoder restored to', original_encoder.Name #, original_bitrate def get_track_unique_id(track): return track.GetITObjectIDs()[-1] def com_collection_iter(collection): return (collection.Item(index) for index in range(1, collection.Count+1)) if __name__ == '__main__': main() raw_input('Hit enter to continue...')