Example #1
0
class GMusicPodcastSyncDestination(PodcastSyncDestination):
    def __init__(self):
        super(GMusicPodcastSyncDestination, self).__init__()
        self.serviceIdentifier = "GOO"
        self.serviceName = "Google Music"
        self.musicManager = Musicmanager()
        self.mobileClient = Mobileclient()
        oAuthFile = "gMusic.oauth"
        if not os.path.isfile(oAuthFile):
            if not self.musicManager.perform_oauth(oAuthFile, True):
                print "Failed to authenticate Music Manager."
                raise PodcastSyncDestinationException("Authentication Failure.")
        else:
            try:
                self.musicManagerauthenticated = self.musicManager.login(oAuthFile)
            except gmusicapi.exceptions.AlreadyLoggedIn:
                pass
        username = raw_input("Enter Google Username: "******"Enter Google Password: "******"Authentication Failure.")
            # perform a push task. should return a PodFastPodcastPushedEpisode instance

    def pushEpisode(self, podcastSyncTaskEpisodePush):
        (uploaded, matched, not_uploaded) = self.musicManager.upload([podcastSyncTaskEpisodePush.localFilename])
        songGoogleMusicID = ""
        if not_uploaded:  # If the track was not uploaded, it may have been uploaded in the past.
            p = re.compile("ALREADY_EXISTS\\((.*)\\)")
            m = p.findall(not_uploaded[podcastSyncTaskEpisodePush.localFilename])
            songGoogleMusicID = m[0]
        else:
            songGoogleMusicID = uploaded[podcastSyncTaskEpisodePush.localFilename]
        print "Track uploaded Successfully. ID:" + songGoogleMusicID
        gmusicPlayLists = self.mobileClient.get_all_playlists()
        playListExists = False
        gmusicPlaylistID = ""
        for gmusicPlayList in gmusicPlayLists:
            if gmusicPlayList["name"] == podcastSyncTaskEpisodePush.playlistName:
                playListExists = True
                gmusicPlaylistID = gmusicPlayList["id"]
                break
        if not playListExists:
            print "Creating playlist..."
            gmusicPlaylistID = self.mobileClient.create_playlist(podcastSyncTaskEpisodePush.playlistName)
        addedEntries = self.mobileClient.add_songs_to_playlist(gmusicPlaylistID, [songGoogleMusicID])
        print "Moved track to playlist."
        return songGoogleMusicID
        # Pull (deletes) an episode from the destination returns true on success, False on faiilure

    def pullEpisode(self, podcastSyncTaskEpisodePull):
        self.mobileClient.delete_songs([podcastSyncTaskEpisodePull.syncDestinationID])
        # TODO: Error check here.
        return True
def main():
    if len(sys.argv) != 2:
        print_help()
        sys.exit(1)
    else:
        username = sys.argv[1]
    password = getpass.getpass()

    mc = Mobileclient()
    mc.login(username, password, Mobileclient.FROM_MAC_ADDRESS)

    mm = Musicmanager()
    mm.perform_oauth()
    mm.login()

    uploaded_songs = mm.get_uploaded_songs()
    uploaded_ids = [track['id'] for track in uploaded_songs]
    for part in chunks(uploaded_ids, 100):
        complete = mc.delete_songs(part)
        if len(complete) != len(part):
            print("Something is wrong")
Example #3
0
            if track_txt_key not in uniq_tracks:
                uniq_tracks.append(track_txt_key)
            else:
                duplicatesTracks.append(track)
    return duplicatesTracks


api = Mobileclient()
logged_in = api.login('login', 'password')

if logged_in:
    print "Successfully logged in. Beginning duplication removal process."
    all_tracks = api.get_all_songs()
    print "all tracks: ", len(all_tracks)
    duplicate_tracks = getDuplicatesSongInAlbums(all_tracks)
    print "duplication tracks count: %s" % len(duplicate_tracks)
    show_tracks = raw_input("Show list tracks to delete?:[y] Y/n ")
    if show_tracks.lower() == 'y' or show_tracks == '':
        for track in duplicate_tracks:
            name = u"%s - %s - %s" % (track['artist'], track['album'], track['title'])
            print name.encode('utf8')
    if len(duplicate_tracks) > 0:
        deleteConfirnation = raw_input("Are you sure you want to delete tracks (%d)? [y] Y/n" % len(duplicate_tracks))
        if deleteConfirnation.lower() == 'y' or deleteConfirnation == '':
            duplicate_track_ids = get_track_ids(duplicate_tracks)
            deleted_track_ids = api.delete_songs(duplicate_track_ids)
            print "Successfully deleted " + str(len(deleted_track_ids)) + " of " + str(len(duplicate_track_ids)) + " queued songs for removal."
    else:
        print "I didn't find any duplicate tracks."
    print "Thank you!"
Example #4
0
for song in all_songs:
    song_id = song.get('id')
    timestamp = song.get('recentTimestamp')
    
    key = "%s: %d-%02d %s" % ( song.get('album'), song.get('discNumber'), song.get('trackNumber'), song.get('title') )
    
    if key in new_songs:
        if new_songs[key]['timestamp'] < timestamp:
            old_songs[key] = new_songs[key]
            new_songs[key] = { 'id': song_id, 'timestamp': timestamp }
        else:
            old_songs[key] = { 'id': song_id, 'timestamp': timestamp }
    
    new_songs[key] = { 'id': song_id, 'timestamp': timestamp }

if len( old_songs ):
    print "Duplicate songs"
    
    old_song_ids = []
    
    for key in sorted( old_songs.keys() ):
        old_song_ids.append( old_songs[key]['id'] )
        print "    " + key.encode('utf-8')
    
    if raw_input( "Delete duplicate songs? (y, n): ") is 'y':
        print "Deleting songs ..."
        client.delete_songs( old_song_ids )
else:
    print "No duplicate songs"
Example #5
0
from pyItunes import *
from gmusicapi import Mobileclient

# iTunes songs
l = Library('<full path to iTunes Music Library.xml>')
songs = [(song.artist, song.name) for id,song in l.songs.items()]

# Google Music songs
api = Mobileclient()
api.login('<account>', '<password>', Mobileclient.FROM_MAC_ADDRESS)
library = api.get_all_songs()

# Find songs to delete
delete = filter(lambda song: (song['artist'], song['title']) not in songs, library)
delete = [song['id'] for song in delete]

if len(delete):
    # Display songs
    print "Duplicate songs"
    old_song_ids = []
    for key in sorted(delete):
        key = '%s: %d-%02d %s' % (song.get('album'), song.get('discNumber'), song.get('trackNumber'), song.get('title'))
        print "    " + key.encode('utf-8')

    # Delete songs
    if raw_input( "Delete duplicate songs? (y, n): ") is 'y':
        print "Deleting songs..."
        api.delete_songs(delete)
else:
    print 'No deleted songs'
Example #6
0
class GMusic(object):
    def __init__(self):
        self.mob_client = Mobileclient()
        self.web_client = Webclient()
        self.logfile = None
        self.logfile_open = False
        # logged_in is True if login was successful
        logged_in = self.mob_client.login(MY_GMAIL, MY_PASSWORD, Mobileclient.FROM_MAC_ADDRESS)
        if logged_in:
            print("GoogleMusic MobileClient Logged In")
        else:
            raise Exception("Couldn't log in, exiting...")
        logged_in = self.web_client.login(MY_GMAIL, MY_PASSWORD)
        if logged_in:
            print("GoogleMusic WebClient Logged In")
        else:
            raise Exception("Couldn't log in, exiting...")

    def build_play_list_dummy(self):
        library = self.mob_client.get_all_songs()
        tracks = [track for track in library if track['artist'] == 'Adhesive Wombat'
                  and "night shade" in track['title'].lower()]
        # for track in sweet_tracks:
        #     print(track)

        playlist_id = self.mob_client.create_playlist('Test playlist')
        for track in tracks:
            self.mob_client.add_songs_to_playlist(playlist_id, track['id'])

        return playlist_id

    def _setlogfile(self, logfile):
        if self.logfile_open:
            self._print_and_log("logfile {} already opened! Not opening again!")
        else:
            self.logfile = logfile
            with open(self.logfile, "w") as logfile:
                logfile.write("LOGSTART: {}, script: {}\n".format(asctime(localtime()), __file__))
            self.logfile_open = True

    def _print_and_log(self, msg):
        if self.logfile:
            with open(self.logfile, "a") as logfile:
                logfile.write(msg+"\n")
        print msg

    def find_duplicate_songs(self, outfile=None):
        duplicates = []
        if outfile:
            if path.exists(path.dirname(outfile)):
                self._setlogfile(outfile)
            else:
                raise IOError("Output filename given: {} is in an none-existing dir".format(outfile))

        library = self.mob_client.get_all_songs()
        tracks = [track for track in library]
        while tracks:
            track = tracks[0]
            dup_list = []
            dup_idx_list = []
            for idx, track_i in enumerate(tracks):
                if track['artist'].lower() == track_i['artist'].lower() and\
                   track['album'].lower() == track_i['album'].lower() and\
                    track['discNumber'] == track_i['discNumber'] and\
                    track['trackNumber'] == track_i['trackNumber'] and\
                   track['title'].lower() == track_i['title'].lower():
                    dup_idx_list.append(idx)
                    dup_list.append(track_i)
            # Remove them:
            for index in sorted(dup_idx_list, reverse=True):
                del tracks[index]
            if len(dup_list) > 1:
                duplicates.append(dup_list)
        for idx, dup_list in enumerate(duplicates):
            self._print_and_log("{}: '{}' was found {} times!".format(idx+1, dup_list[0]['title'].encode("utf-8"),
                                                                      len(dup_list)))
        self._print_and_log("Found a total of {} duplicate songs!".format(len(duplicates)))
        # Display important stuff
        for idx, track_list in enumerate(duplicates):
            self._print_and_log("{}: BAND: {}, NAME:  '{}'".format(idx+1, track_list[0]['artist'],
                                                                   track_list[0]['title'].encode("utf-8")))
            for el in track_list[0]:
                for track in track_list:
                    if el not in track:
                        track[el] = "NO VALUE"
                    if track[el] != track_list[0][el] and el not in ['id', 'url', 'recentTimestamp', 'storeId', 'nid', 'clientId']:
                        # unicode?
                        try:
                            r_val = track_list[0][el].encode("utf-8")
                        except:
                            r_val = track_list[0][el]
                        # unicode?
                        try:
                            l_val = track[el].encode("utf-8")
                        except:
                            l_val = track[el]

                        self._print_and_log("track_id {}: {}='{}'".format(track_list[0]['id'], el, r_val))
                        self._print_and_log("track_id {}: {}='{}'".format(track['id'], el, l_val))

            # raw_input("Press any key to continue...")
        return duplicates

    def delete_duplicates(self, duplicates):
        self._print_and_log("Cleaning duplicates [removing oldest of each duplicant]:")
        old_song_ids = []
        for idx, dup_list in enumerate(duplicates):
            self._print_and_log("{}: BAND: {}, NAME:  '{}'".format(idx+1, dup_list[0]['artist'],
                                                                   dup_list[0]['title'].encode("utf-8")))
            track_timstamp = None
            oldest_id = None
            for el in dup_list:
                if track_timstamp is None and oldest_id is None:
                    track_timstamp = el["timestamp"]
                    oldest_id = el["id"]
                elif el["timestamp"] < track_timstamp:
                    track_timstamp = el["timestamp"]
                    oldest_id = el["id"]
            # finished with dup_list - log oldest id:
            self._print_and_log("Will delete {}, track_id: {}".format(el["title"], el["id"]))
            old_song_ids.append(oldest_id)

        self.mob_client.delete_songs(old_song_ids)
        self._print_and_log("track_ids deleted:\n{}".format(old_song_ids))
    def _delete_duplicates(self, api: Mobileclient) -> None:
        """Delete duplicates."""

        self._combine_play_counts(api)
        discard_ids = [song["id"] for song in self.get_discard_songs()]
        api.delete_songs(discard_ids)
Example #8
0
# wanted_keys = ['artist', 'title', 'id', 'rating']

for song in songs_list:
    if song['rating'] == RATING_THUMBS_DOWN:
        songs_to_delete.append(song)
        # songs_to_delete.append({k: song[k] for k in wanted_keys})

if not songs_to_delete:
    print("No songs to delete so far")
    exit(0)

# songs_to_delete = songs_to_delete[-2]
ids_to_delete = [song['id'] for song in songs_to_delete]
titles_to_delete = [song['title'] for song in songs_to_delete]

deleted = api.delete_songs(ids_to_delete)

# print(titles_to_delete)

feedbackText = ""
if (len(deleted) != len(songs_to_delete)):
    feedbackText = "Algo no ha anat bé"
else:
    feedbackText = "<h1>Deleted all %d songs:<h1><br>" % len(deleted)
    feedbackText += '<br>'.join(titles_to_delete)

print(feedbackText)

destEmail = config['Genericos']['DestinationEmail']
titleEmail = 'GMusicAPI batch feedback'
Example #9
0
            i['artist'] = 'ARTIST-' + str(n)
        if i['title'] == '':
            i['title'] = 'TITLE-' + str(n)
        try:  # try to add song to existing album
            all_albums[i['album']].add(i['artist'], i['genre'], i['title'],
                                       i['id'])
#    	print('>>> added', i['title'], 'to album', i['album'])
        except:  # no album exists -> create it, then add song
            all_albums[i['album']] = Album(i['album'])
            all_albums[i['album']].add(i['artist'], i['genre'], i['title'],
                                       i['id'])
#    	print('>>> created', i['album'], '(' + i['artist'] + ')')
        n += 1
else:
    print('login for', USER, 'failed')

print('read', n, 'songs in', len(all_albums), 'albums')

for a in sorted(all_albums):  # across all albums
    i = all_albums[a]
    print(i.artist[0], ':', i.album,
          '(' + i.genre[0] + ')')  # Artist : Album (Genre)
    for t in i.title:  # list all titles of the album
        print('\t', t)

    rm = input("Delete album? (y/n)? ")
    if rm == 'y':
        ret = api.delete_songs(i.ID)  # delete all songs of the album
        print(ret, 'deleted')
        print()
Example #10
0
      i['album'] = 'ALBUM-' + str(n)
    if i['artist'] == '':
      i['artist'] = 'ARTIST-' + str(n)
    if i['title'] == '':
      i['title'] = 'TITLE-' + str(n)
    try:                                   # try to add song to existing album
    	all_albums[i['album']].add(i['artist'], i['genre'], i['title'], i['id'])
#    	print('>>> added', i['title'], 'to album', i['album'])
    except:                                # no album exists -> create it, then add song
    	all_albums[i['album']] = Album(i['album'])
    	all_albums[i['album']].add(i['artist'], i['genre'], i['title'], i['id'])
#    	print('>>> created', i['album'], '(' + i['artist'] + ')')
    n += 1
else:
  print('login for', USER, 'failed')

print('read', n, 'songs in', len(all_albums), 'albums')

for a in sorted(all_albums):        # across all albums    
  i = all_albums[a]
  print(i.artist[0], ':', i.album, '('+i.genre[0]+')')   # Artist : Album (Genre)
  for t in i.title:                 # list all titles of the album
    print('\t', t)

  rm = input("Delete album? (y/n)? ")
  if rm == 'y':
    ret = api.delete_songs(i.ID)    # delete all songs of the album
    print(ret, 'deleted')
    print()

class MusicSync(object):
    def __init__(self, email=None, password=None):
        self.mm = Musicmanager()
        self.wc = Webclient()
        self.mc = Mobileclient()
        if not email:
            email = raw_input("Email: ")
        if not password:
            password = getpass()

        self.email = email
        self.password = password

        self.logged_in = self.auth()

        print "Fetching playlists from Google..."
        self.playlists = self.mc.get_all_user_playlist_contents()
        #self.playlists = self.mc.get_all_playlists()
        #self.playlists = self.wc.get_all_playlist_ids(auto=False)
        self.all_songs = self.mc.get_all_songs()
        #print "Got %d playlists." % len(self.playlists['user'])
        print "Got %d playlists containing %d songs." % (len(
            self.playlists), len(self.all_songs))
        print ""

    def auth(self):
        self.logged_in = self.mc.login(self.email, self.password)
        #self.logged_in = self.wc.login(self.email, self.password)
        if not self.logged_in:
            print "Login failed..."
            exit()

        print ""
        print "Logged in as %s" % self.email
        print ""

        if not os.path.isfile(OAUTH_FILEPATH):
            print "First time login. Please follow the instructions below:"
            self.mm.perform_oauth()
        self.logged_in = self.mm.login()
        if not self.logged_in:
            print "OAuth failed... try deleting your %s file and trying again." % OAUTH_FILEPATH
            exit()

        print "Authenticated"
        print ""

    def sync_playlist(self, filename, remove_missing):
        #def sync_playlist(self, filename, remove_missing=False):
        filename = self.get_platform_path(filename)
        os.chdir(os.path.dirname(filename))
        title = os.path.splitext(os.path.basename(filename))[0]
        print "Syncing playlist: %s" % filename
        #if title not in self.playlists['user']:
        #print "   didn't exist... creating..."
        #self.playlists['user'][title] = [self.wc.create_playlist(title)]
        print ""

        plid = ""

        for pl in self.playlists:
            if pl['name'] == title:
                plid = pl['id']
                goog_songs = pl['tracks']

        if plid == "":
            print "   didn't exist... creating..."
            plid = self.mc.create_playlist(self, title)

        #plid = self.playlists['user'][title][0]
        #goog_songs = self.wc.get_playlist_songs(plid)
        print "%d songs already in Google Music playlist" % len(goog_songs)
        pc_songs = self.get_files_from_playlist(filename)
        print "%d songs in local playlist" % len(pc_songs)
        print ""

        # Sanity check max 1000 songs per playlist
        if len(pc_songs) > MAX_SONGS_IN_PLAYLIST:
            print "    Google music doesn't allow more than %d songs in a playlist..." % MAX_SONGS_IN_PLAYLIST
            print "    Will only attempt to sync the first %d songs." % MAX_SONGS_IN_PLAYLIST
            del pc_songs[MAX_SONGS_IN_PLAYLIST:]

        existing_files = 0
        added_files = 0
        failed_files = 0
        removed_files = 0
        fatal_count = 0

        for fn in pc_songs:
            if self.file_already_in_list(fn, goog_songs, self.all_songs):
                existing_files += 1
                continue
            print ""
            print "Adding: %s" % os.path.basename(fn).encode('cp1252')
            #print "Adding: %s" % os.path.basename(fn)
            #online = False
            online = self.find_song(fn, goog_songs, self.all_songs)
            #online = self.find_song(fn)
            song_id = None
            if online:
                song_id = online['id']
                print "   already uploaded [%s]" % song_id
            else:
                attempts = 0
                result = []
                while not result and attempts < MAX_UPLOAD_ATTEMPTS_PER_FILE:
                    print "   uploading... (may take a while)"
                    attempts += 1
                    try:
                        result = self.mm.upload(fn)
                    except (BadStatusLine, CannotSendRequest):
                        # Bail out if we're getting too many disconnects
                        if fatal_count >= MAX_CONNECTION_ERRORS_BEFORE_QUIT:
                            print ""
                            print "Too many disconnections - quitting. Please try running the script again."
                            print ""
                            exit()

                        print "Connection Error -- Reattempting login"
                        fatal_count += 1
                        self.wc.logout()
                        self.mc.logout()
                        self.mm.logout()
                        result = []
                        time.sleep(STANDARD_SLEEP)

                    except:
                        result = []
                        time.sleep(STANDARD_SLEEP)

                try:
                    if result[0]:
                        song_id = result[0].itervalues().next()
                    else:
                        song_id = result[1].itervalues().next()
                    print "   upload complete [%s]" % song_id
                except:
                    print "      upload failed - skipping"
                    tag = self.get_id3_tag(fn)
                    print "      failed song:\t%s\t%s\t%s" % (
                        tag['title'].encode('cp1252'),
                        tag['artist'].encode('cp1252'),
                        tag['album'].encode('cp1252'))

            if not song_id:
                failed_files += 1
                continue

            added = self.mc.add_songs_to_playlist(plid, song_id)
            time.sleep(.3)  # Don't spam the server too fast...
            print "   done adding to playlist"
            added_files += 1

        if remove_missing:
            for g in goog_songs:
                for s in self.all_songs:
                    if g['trackId'] == s['id']:
                        print ""
                        print "Removing: %s" % s['title'].encode('cp1252')
                        self.mc.remove_entries_from_playlist(g['id'])
                        #self.wc.remove_songs_from_playlist(plid, s.id)
                        time.sleep(.3)  # Don't spam the server too fast...
                        removed_files += 1

        print ""
        print "---"
        print "%d songs unmodified" % existing_files
        print "%d songs added" % added_files
        print "%d songs failed" % failed_files
        print "%d songs removed" % removed_files

    def get_files_from_playlist(self, filename):
        files = []
        f = codecs.open(filename, encoding='cp1252')
        #f = codecs.open(filename, encoding='utf-8')
        for line in f:
            line = line.rstrip().replace(u'\ufeff', u'')
            if line == "" or line[0] == "#":
                continue
            path = os.path.abspath(self.get_platform_path(line))
            if not os.path.exists(path):
                print "File not found: %s" % line
                continue
            files.append(path)
        f.close()
        return files

    def file_already_in_list(self, filename, goog_songs, all_songs):
        tag = self.get_id3_tag(filename)
        print "Searching for\t%s\t%s\t%s" % (tag['title'].encode('cp1252'),
                                             tag['artist'].encode('cp1252'),
                                             tag['album'].encode('cp1252'))
        i = 0
        while i < len(goog_songs):
            for s in all_songs:
                if goog_songs[i]['trackId'] == s['id']:
                    if self.tag_compare(s, tag):
                        print "Found match\t%s\t%s\t%s" % (
                            s['title'].encode('cp1252'),
                            s['artist'].encode('cp1252'),
                            s['album'].encode('cp1252'))
                        goog_songs.pop(i)
                        return True
            i += 1
        return False

    def get_id3_tag(self, filename):
        data = mutagen.File(filename, easy=True)
        r = {}
        if 'title' not in data:
            title = os.path.splitext(os.path.basename(filename))[0]
            print 'Found song with no ID3 title, setting using filename:'
            print '  %s' % title
            print '  (please note - the id3 format used (v2.4) is invisible to windows)'
            data['title'] = [title]
            data.save()
        r['title'] = data['title'][0]
        r['track'] = int(data['tracknumber'][0].split('/')
                         [0]) if 'tracknumber' in data else 0
        # If there is no track, try and get a track number off the front of the file... since thats
        # what google seems to do...
        # Not sure how google expects it to be formatted, for now this is a best guess
        if r['track'] == 0:
            m = re.match("(\d+) ", os.path.basename(filename))
            if m:
                r['track'] = int(m.group(0))
        r['artist'] = data['artist'][0] if 'artist' in data else ''
        r['album'] = data['album'][0] if 'album' in data else ''
        return r

    def find_song(self, filename, goog_songs, all_songs):
        tag = self.get_id3_tag(filename)
        print "Searching for\t%s\t%s\t%s" % (tag['title'].encode('cp1252'),
                                             tag['artist'].encode('cp1252'),
                                             tag['album'].encode('cp1252'))
        #results = self.wc.search(tag['title'])
        # NOTE - diagnostic print here to check results if you're creating duplicates
        #print results['song_hits']
        #for r in goog_songs:
        #for r in results['song_hits']:
        for s in all_songs:
            #if r['trackId'] == s['id']:
            if self.tag_compare(s, tag):
                # TODO: add rough time check to make sure its "close"
                print "Found match\t%s\t%s\t%s" % (
                    s['title'].encode('cp1252'), s['artist'].encode('cp1252'),
                    s['album'].encode('cp1252'))
                return s
        return None

    def tag_compare(self, g_song, tag):
        # If a google result has no track, google doesn't return a field for it
        if 'title' not in g_song:
            g_song['title'] = ""
        if 'artist' not in g_song:
            g_song['artist'] = ""
        if 'album' not in g_song:
            g_song['album'] = ""
        if 'track' not in g_song:
            g_song['track'] = 0
        if (g_song['title'].lower() == tag['title'].lower() and g_song['artist'].lower() == tag['artist'].lower()) or\
           (g_song['album'].lower() == tag['album'].lower() and g_song['title'].lower() == tag['title'].lower()) or\
           (g_song['artist'].lower() == tag['artist'].lower() and g_song['album'].lower() == tag['album'].lower() and g_song['track'] == tag['track']):
            print "Partial match\t%s\t%s\t%s" % (
                g_song['title'].encode('cp1252'),
                g_song['artist'].encode('cp1252'),
                g_song['album'].encode('cp1252'))
        return g_song['title'].lower() == tag['title'].lower() and\
               g_song['artist'].lower() == tag['artist'].lower() and\
               g_song['album'].lower() == tag['album'].lower() #and\
        #g_song['track'] == tag['track']

    def delete_song(self, sid):
        self.mc.delete_songs(sid)
        print "Deleted song by id [%s]" % sid

    def get_platform_path(self, full_path):
        # Try to avoid messing with the path if possible
        if os.sep == '/' and '\\' not in full_path:
            return full_path
        if os.sep == '\\' and '\\' in full_path:
            return full_path
        if '\\' not in full_path:
            return full_path
        return os.path.normpath(full_path.replace('\\', '/'))
Example #12
0
print(len(album_to_ids), "albums in library")

# Find songs with duplicate (title, trackNumber) in same album
total_dups, albums_with_dups = 0, 0

for album, ids in album_to_ids.items():

    #if "HSBC" not in album:     # exclude certain albums
    #    continue

    songs = set()
    album_dups = 0

    for id in ids:
        all_song_info = track_ids[id]
        song_tuple = (all_song_info["title"], all_song_info["trackNumber"])

        if song_tuple in songs:
            #print("Deleting duplicate", song_tuple, album)
            api.delete_songs(id)    # delete dupliacted
            album_dups +=1
        songs.add(song_tuple)

    if album_dups > 0:
        total_dups += album_dups
        albums_with_dups += 1
        print("Deleted", album_dups, "duplicates in", album, ", cumulative:", total_dups)

print("Total duplicates deleted:", total_dups)
print("Albums with duplicates:", albums_with_dups)
Example #13
0
import io
import os

api = Mobileclient()
logged_in = api.login('*****@*****.**', os.environ['PASSWORD'], Mobileclient.FROM_MAC_ADDRESS)
if logged_in:
    print "Logged in successfully"
else:
    print "Error logging in"
    sys.exit(1)

delete = os.getenv('DELETE_BAD_SONGS', False)

bad_ids = []
with io.open('/tmp/zero_length.csv', 'w') as csv:
    csv.write(u"Artist,Album,Title\n")
    library = api.get_all_songs()
    for track in library:
        millis = int(track['durationMillis'])
        if millis < 1000:
            line = u"{albumArtist:s},{album:s},{title:s}\n".format(**track)
            csv.write(line)
            bad_ids.append(track['id'])

print "Found %d bad songs" % len(bad_ids)
if delete:
    print "Deleting bad songs"
    api.delete_songs(bad_ids)

api.logout()
class MusicSync(object):
    def __init__(self, email=None, password=None):
        self.mm = Musicmanager()
        self.wc = Webclient()
        self.mc = Mobileclient()
        if not email:
            email = raw_input("Email: ")
        if not password:
            password = getpass()

        self.email = email
        self.password = password

        self.logged_in = self.auth()

        print "Fetching playlists from Google..."
        self.playlists = self.mc.get_all_user_playlist_contents()
        #self.playlists = self.mc.get_all_playlists()
        #self.playlists = self.wc.get_all_playlist_ids(auto=False)
        self.all_songs = self.mc.get_all_songs()
        #print "Got %d playlists." % len(self.playlists['user'])
        print "Got %d playlists containing %d songs." % (len(self.playlists), len(self.all_songs))
        print ""


    def auth(self):
        self.logged_in = self.mc.login(self.email, self.password)
        #self.logged_in = self.wc.login(self.email, self.password)
        if not self.logged_in:
            print "Login failed..."
            exit()

        print ""
        print "Logged in as %s" % self.email
        print ""

        if not os.path.isfile(OAUTH_FILEPATH):
            print "First time login. Please follow the instructions below:"
            self.mm.perform_oauth()
        self.logged_in = self.mm.login()
        if not self.logged_in:
            print "OAuth failed... try deleting your %s file and trying again." % OAUTH_FILEPATH
            exit()

        print "Authenticated"
        print ""


    def sync_playlist(self, filename, remove_missing):
    #def sync_playlist(self, filename, remove_missing=False):
        filename = self.get_platform_path(filename)
        os.chdir(os.path.dirname(filename))
        title = os.path.splitext(os.path.basename(filename))[0]
        print "Syncing playlist: %s" % filename
        #if title not in self.playlists['user']:
            #print "   didn't exist... creating..."
            #self.playlists['user'][title] = [self.wc.create_playlist(title)]
        print ""

        plid = ""

        for pl in self.playlists:
            if pl['name'] == title:
                plid = pl['id']
                goog_songs = pl['tracks']

        if plid == "":
            print "   didn't exist... creating..."
            plid = self.mc.create_playlist(self, title)

        #plid = self.playlists['user'][title][0]
        #goog_songs = self.wc.get_playlist_songs(plid)
        print "%d songs already in Google Music playlist" % len(goog_songs)
        pc_songs = self.get_files_from_playlist(filename)
        print "%d songs in local playlist" % len(pc_songs)
        print ""

        # Sanity check max 1000 songs per playlist
        if len(pc_songs) > MAX_SONGS_IN_PLAYLIST:
            print "    Google music doesn't allow more than %d songs in a playlist..." % MAX_SONGS_IN_PLAYLIST
            print "    Will only attempt to sync the first %d songs." % MAX_SONGS_IN_PLAYLIST
            del pc_songs[MAX_SONGS_IN_PLAYLIST:]

        existing_files = 0
        added_files = 0
        failed_files = 0
        removed_files = 0
        fatal_count = 0

        for fn in pc_songs:
            if self.file_already_in_list(fn, goog_songs, self.all_songs):
                existing_files += 1
                continue
            print ""
            print "Adding: %s" % os.path.basename(fn).encode('cp1252')
            #print "Adding: %s" % os.path.basename(fn)
            #online = False
            online = self.find_song(fn, goog_songs, self.all_songs)
            #online = self.find_song(fn)
            song_id = None
            if online:
                song_id = online['id']
                print "   already uploaded [%s]" % song_id
            else:
                attempts = 0
                result = []
                while not result and attempts < MAX_UPLOAD_ATTEMPTS_PER_FILE:
                    print "   uploading... (may take a while)"
                    attempts += 1
                    try:
                        result = self.mm.upload(fn)
                    except (BadStatusLine, CannotSendRequest):
                        # Bail out if we're getting too many disconnects
                        if fatal_count >= MAX_CONNECTION_ERRORS_BEFORE_QUIT:
                            print ""
                            print "Too many disconnections - quitting. Please try running the script again."
                            print ""
                            exit()

                        print "Connection Error -- Reattempting login"
                        fatal_count += 1
                        self.wc.logout()
                        self.mc.logout()
                        self.mm.logout()
                        result = []
                        time.sleep(STANDARD_SLEEP)

                    except:
                        result = []
                        time.sleep(STANDARD_SLEEP)

                try:
                    if result[0]:
                        song_id = result[0].itervalues().next()
                    else:
                        song_id = result[1].itervalues().next()
                    print "   upload complete [%s]" % song_id
                except:
                    print "      upload failed - skipping"
                    tag = self.get_id3_tag(fn)
                    print "      failed song:\t%s\t%s\t%s" % (tag['title'].encode('cp1252'), tag['artist'].encode('cp1252'), tag['album'].encode('cp1252'))

            if not song_id:
                failed_files += 1
                continue

            added = self.mc.add_songs_to_playlist(plid, song_id)
            time.sleep(.3) # Don't spam the server too fast...
            print "   done adding to playlist"
            added_files += 1

        if remove_missing:
            for g in goog_songs:
                for s in self.all_songs:
                    if g['trackId'] == s['id']:
                        print ""
                        print "Removing: %s" % s['title'].encode('cp1252')
                        self.mc.remove_entries_from_playlist(g['id'])
                        #self.wc.remove_songs_from_playlist(plid, s.id)
                        time.sleep(.3) # Don't spam the server too fast...
                        removed_files += 1

        print ""
        print "---"
        print "%d songs unmodified" % existing_files
        print "%d songs added" % added_files
        print "%d songs failed" % failed_files
        print "%d songs removed" % removed_files


    def get_files_from_playlist(self, filename):
        files = []
        f = codecs.open(filename, encoding='cp1252')
        #f = codecs.open(filename, encoding='utf-8')
        for line in f:
            line = line.rstrip().replace(u'\ufeff',u'')
            if line == "" or line[0] == "#":
                continue
            path  = os.path.abspath(self.get_platform_path(line))
            if not os.path.exists(path):
                print "File not found: %s" % line
                continue
            files.append(path)
        f.close()
        return files

    def file_already_in_list(self, filename, goog_songs, all_songs):
        tag = self.get_id3_tag(filename)
        print "Searching for\t%s\t%s\t%s" % (tag['title'].encode('cp1252'), tag['artist'].encode('cp1252'), tag['album'].encode('cp1252'))
        i = 0
        while i < len(goog_songs):
            for s in all_songs:
                if goog_songs[i]['trackId'] == s['id']:
                    if self.tag_compare(s, tag):
                        print "Found match\t%s\t%s\t%s" % (s['title'].encode('cp1252'), s['artist'].encode('cp1252'), s['album'].encode('cp1252'))
                        goog_songs.pop(i)
                        return True
            i += 1
        return False

    def get_id3_tag(self, filename):
        data = mutagen.File(filename, easy=True)
        r = {}
        if 'title' not in data:
            title = os.path.splitext(os.path.basename(filename))[0]
            print 'Found song with no ID3 title, setting using filename:'
            print '  %s' % title
            print '  (please note - the id3 format used (v2.4) is invisible to windows)'
            data['title'] = [title]
            data.save()
        r['title'] = data['title'][0]
        r['track'] = int(data['tracknumber'][0].split('/')[0]) if 'tracknumber' in data else 0
        # If there is no track, try and get a track number off the front of the file... since thats
        # what google seems to do...
        # Not sure how google expects it to be formatted, for now this is a best guess
        if r['track'] == 0:
            m = re.match("(\d+) ", os.path.basename(filename))
            if m:
                r['track'] = int(m.group(0))
        r['artist'] = data['artist'][0] if 'artist' in data else ''
        r['album'] = data['album'][0] if 'album' in data else ''
        return r

    def find_song(self, filename, goog_songs, all_songs):
        tag = self.get_id3_tag(filename)
        print "Searching for\t%s\t%s\t%s" % (tag['title'].encode('cp1252'), tag['artist'].encode('cp1252'), tag['album'].encode('cp1252'))
        #results = self.wc.search(tag['title'])
        # NOTE - diagnostic print here to check results if you're creating duplicates
        #print results['song_hits']
        #for r in goog_songs:
        #for r in results['song_hits']:
        for s in all_songs:
            #if r['trackId'] == s['id']:
            if self.tag_compare(s, tag):
                # TODO: add rough time check to make sure its "close"
                print "Found match\t%s\t%s\t%s" % (s['title'].encode('cp1252'), s['artist'].encode('cp1252'), s['album'].encode('cp1252'))
                return s
        return None

    def tag_compare(self, g_song, tag):
        # If a google result has no track, google doesn't return a field for it
        if 'title' not in g_song:
            g_song['title'] = ""
        if 'artist' not in g_song:
            g_song['artist'] = ""
        if 'album' not in g_song:
            g_song['album'] = ""
        if 'track' not in g_song:
            g_song['track'] = 0
        if (g_song['title'].lower() == tag['title'].lower() and g_song['artist'].lower() == tag['artist'].lower()) or\
           (g_song['album'].lower() == tag['album'].lower() and g_song['title'].lower() == tag['title'].lower()) or\
           (g_song['artist'].lower() == tag['artist'].lower() and g_song['album'].lower() == tag['album'].lower() and g_song['track'] == tag['track']):
            print "Partial match\t%s\t%s\t%s" % (g_song['title'].encode('cp1252'), g_song['artist'].encode('cp1252'), g_song['album'].encode('cp1252'))
        return g_song['title'].lower() == tag['title'].lower() and\
               g_song['artist'].lower() == tag['artist'].lower() and\
               g_song['album'].lower() == tag['album'].lower() #and\
               #g_song['track'] == tag['track']

    def delete_song(self, sid):
        self.mc.delete_songs(sid)
        print "Deleted song by id [%s]" % sid

    def get_platform_path(self, full_path):
        # Try to avoid messing with the path if possible
        if os.sep == '/' and '\\' not in full_path:
            return full_path
        if os.sep == '\\' and '\\' in full_path:
            return full_path
        if '\\' not in full_path:
            return full_path
        return os.path.normpath(full_path.replace('\\', '/'))
Example #15
0
def main():
    #### Requests user specifies update library and/or playlists
    if len(sys.argv) != 3:
        print('Specify T/F arguments for uploading and updating playlists')
        print('e.g. python ' + sys.argv[0] + ' 1 0')
        print('which would:\n--> upload new songs\n--> NOT update playlists')
        sys.exit(0)

    #### Parameters
    music_dir = '/home/conor/Music/'
    print('Local music directory set to:', music_dir)
    accepted = input('Type "y" if this is correct directory: ')
    if accepted.lower() != 'y':
        print('Edit music_dir variable in source to run script...')
        print('Ending music management.')
        sys.exit(0)

    #### Some general information needed for both tasks is collected here

    # Get mp3 file names from music folder
    local_song_paths = glob.glob(music_dir + '*.mp3')

    # Get individual song names
    local_song_names = set()
    for p in local_song_paths:
        _, song_name = os.path.split(p)
        local_song_names.add(song_name)

    # Authenticate
    mc = Mobileclient()
    mc.oauth_login('38e42c4b00ca0a10')  # Authenticates using on-disk token
    print('Mobile client authentication complete...')

    # Create dict of gpm 'song'': 'id' pairs
    song_ids = {}
    gpm_songs = mc.get_all_songs()
    for song in gpm_songs:
        song_ids[song['title']] = song['id']

    #### Manage upload/deletion of songs
    uploading = sys.argv[1]
    if uploading == '1':
        mm = Musicmanager()
        mm.login(uploader_id='EE:20:80:B4:17:A9'
                 )  # Authenticates using on-disk token
        print('Music manager authentication complete...')

        # Delete songs that no longer exist locally
        to_delete = set()
        for song in song_ids:
            if song not in local_song_names:
                to_delete.add(song)
        if len(to_delete) == 0:
            print('No songs to delete.')
        else:
            print('{} songs to delete:'.format(len(to_delete)))
            print([s for s in to_delete])
            # delete_songs() method requires a list as input
            to_delete_ids = []
            for s in to_delete:
                song_id = song_ids[s]
                to_delete_ids.append(song_id)
            mc.delete_songs(to_delete_ids)
            print('Deleted songs.')

        #### Uploading
        to_upload = []
        for s in local_song_names:
            if s not in song_ids:
                to_upload.append(music_dir + s)
        print('{} songs to upload.'.format(len(to_upload)))
        if len(to_upload) != 0:
            accepted = input('Type "y" to commence upload now: ')
            if accepted.lower() != 'y':
                print('Ending music management.')
                sys.exit(0)
            mm.upload(to_upload)

    #### Create and edit playlists as required
    # Works by deleting all playlists and then re-creating from scratch
    playlisting = sys.argv[2]
    if playlisting == '1':
        # Refresh song list
        # (since we have uploaded new songs since original list generated)
        song_ids = {}
        gpm_songs = mc.get_all_songs()
        for song in gpm_songs:
            song_ids[song['title']] = song['id']

        # Flush old playlists
        print('Deleting old playlists...')
        gpm_playlists = mc.get_all_playlists()
        for pl in gpm_playlists:
            mc.delete_playlist(pl['id'])
        print('Playlists deleted.')

        # Keep a dictionary of created playlists to prevent duplication
        playlist_ids = {}
        total = len(song_ids)
        completed = 0

        # Create and update playlists
        print('Organising songs:')
        for s in song_ids:
            sid = song_ids[s]
            req_pls = required_playlists(s)
            for pl in req_pls:
                if pl in playlist_ids:
                    pid = playlist_ids[pl]
                else:
                    pid = mc.create_playlist(name=pl)
                    playlist_ids[pl] = pid

                mc.add_songs_to_playlist(pid, sid)
            completed += 1
            # Console output for number of songs sorted
            sys.stdout.write("\r{}/{} processed".format(completed, total))
            sys.stdout.flush()
        print()
	if song.get('trackNumber') is None:
		tracknum = 0
	else: 
		tracknum = song.get('trackNumber')
	
	#key = "%s: %d-%02d %s" % ( song.get('album'), discnum, tracknum, song.get('title') )
	key = "%s - %s" % ( song.get('title'), song.get('artist') )

	if key in new_songs:
		if new_songs[key]['timestamp'] < timestamp:
			old_songs[key] = new_songs[key]
			new_songs[key] = { 'id': song_id, 'timestamp': timestamp }
		else:
			old_songs[key] = { 'id': song_id, 'timestamp': timestamp }
	
	new_songs[key] = { 'id': song_id, 'timestamp': timestamp }

if len( old_songs ):
	print('Duplicate songs')
	
	old_song_ids = []
	
	for key in sorted( old_songs.keys() ):
		old_song_ids.append( old_songs[key]['id'] )
		print('	   ' + str(key))
	
	if input('Delete duplicate songs? (y, n): ') is 'y':
		print('Deleting songs ...')
		client.delete_songs( old_song_ids )
else:
	print('Finally. No duplicate songs.')