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...')