Пример #1
0
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.wc.get_all_playlist_ids(auto=False)
        print "Got %d playlists." % len(self.playlists["user"])
        print ""

    def auth(self):
        self.logged_in = self.wc.login(self.email, self.password)
        self.logged_in = self.mc.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 add_rhapsody_playlist(self, filename, remove_missing=False):
        filename = self.get_platform_path(filename)
        os.chdir(os.path.dirname(filename))
        # playlist_title = os.path.splitext(os.path.basename(filename))[0]
        print "Synching File: %s" % filename

        print "Parsing Songs from %s" % filename
        pc_songs = self.get_songs_from_file(filename)
        # print (pc_songs)
        print "%d songs in local file: %s" % (len(pc_songs), filename)

        # 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 song in pc_songs:
            playlist_title = song["playlist"]
            if playlist_title not in self.playlists["user"]:
                self.playlists["user"][playlist_title] = [self.mc.create_playlist(playlist_title)]
                time.sleep(0.7)
        print "Starting Playlist Sync with Google music..."
        for song in pc_songs:
            # print song

            plid = ""

            print "--------------------------------"
            print ""
            print "Playlist: %s" % song["playlist"]
            print "Artist: %s" % song["artist"]
            print "Song: %s" % song["title"]
            print "Album: %s" % song["album"]

            playlist_title = song["playlist"]

            plid = self.playlists["user"][playlist_title][0]

            goog_songs = self.wc.get_playlist_songs(plid)

            if self.song_already_in_list(song, goog_songs):
                existing_files += 1
                print "Result: Song Already Added"
                continue
            print "Total %d songs in Google playlist: %s" % (len(goog_songs), playlist_title)
            print "%s - %s,   didn't exist...Will try to add..." % (song["artist"], song["title"])

            print ""
            print "--------------------------------"
            results = self.mc.search_all_access(song["title"], max_results=50)
            nid = self.filter_search_results(results, song)
            print "AA nId: %s " % nid
            if nid:
                song_id = self.mc.add_aa_track(nid)
                added = self.wc.add_songs_to_playlist(plid, song_id)

                print "Playlist UUid: %s" % plid
                print "Song ID: %s" % song_id
                time.sleep(0.3)  # Don't spam the server too fast...
                print "Result: done adding to playlist"
                added_files += 1
                continue
            else:
                query = "%s %s" % (song["artist"], song["title"].split(" ")[0])
                print "Query %s" % query
                results = self.mc.search_all_access(query, max_results=50)
                nid = self.filter_search_results(results, song)
                if nid:
                    song_id = self.mc.add_aa_track(nid)
                    added = self.wc.add_songs_to_playlist(plid, song_id)

                    print "Playlist UUid: %s" % plid
                    print "Song ID: %s" % song_id
                    time.sleep(0.3)  # Don't spam the server too fast...
                    print " -- done adding to playlist"
                    added_files += 1
                    continue
            print "Result: NID Blank, Song not Found in All Access"

        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_songs_from_file(self, filename):
        songs = []
        f = codecs.open(filename, encoding="utf-8")
        for line in f:
            line = line.rstrip().replace(u"\ufeff", u"")
            if line == "" or line[0] == "#":
                continue
            la = line.split("\\")
            regex_filter = "[^A-Za-z0-9\,\-\.\ \(\)'\!\?\$\/ \& \:]"

            artist = re.sub(regex_filter, "", la[1])
            playlist = re.sub(regex_filter, "", la[0])
            album = re.sub(regex_filter, "", la[2])
            title = re.sub(regex_filter, "", la[3])

            # print "Filtered Strings:"
            # print "Artist: %s" % artist
            # print "Playlist: %s" % playlist
            # print "Song: %s" % title
            # print "Album: %s" % album

            dt = {"playlist": playlist, "artist": artist, "album": album, "title": title}
            # print (dt)

            songs.append(dt)
        f.close()
        return songs

    def get_songs_from_playlist(self, filename):
        songs = []
        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
            songs.append(path)
        f.close()
        return songs

    def get_files_from_playlist(self, filename):
        files = []
        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 song_already_in_list(self, song, goog_songs):
        # tag = self.get_id3_tag(filename)
        i = 0
        while i < len(goog_songs):
            # print goog_songs
            if self.tag_compare(goog_songs[i], song):
                goog_songs.pop(i)
                return True
            i += 1
        return False

    def file_already_in_list(self, filename, goog_songs):
        tag = self.get_id3_tag(filename)
        i = 0
        while i < len(goog_songs):
            if self.tag_compare(goog_songs[i], tag):
                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, plid):
        tag = self.get_id3_tag(filename)
        print "Song Tag: %s " % tag
        print "Filename: %s" % filename
        ws_plids = []
        ws_plids.append(plid)
        print (ws_plids)
        playlists = self.wc.get_all_playlist_ids()
        print (playlists)
        results = self.wc.get_playlist_songs(ws_plids)
        # NOTE - dianostic print here to check results if you're creating duplicates
        print results
        print "%s ][ %s ][ %s ][ %s" % (tag["title"], tag["artist"], tag["album"], tag["track"])
        for r in results:
            if self.tag_compare(r, tag):
                # TODO: add rough time check to make sure its "close"
                return r
        return None

    def filter_search_results(self, results, song):
        # Try Exact Matching
        for g_song in results["song_hits"]:
            if self.tag_compare(g_song["track"], song):
                return g_song["track"]["nid"]
            elif self.song_compare(g_song["track"], song, "artist"):
                # try just the artist
                return g_song["track"]["nid"]
            elif self.song_compare(g_song["track"], song, "part-song"):
                # try part of song and artist
                return g_song["track"]["nid"]
        return None

    def song_compare(self, g_song, tag, type):
        if "track" not in g_song:
            g_song["track"] = 0
        title_parts = tag["title"].split("(")  # removing shit like (featuring wiz)
        tp = title_parts[0].split(" ")  # First word maybe
        if "artist" in type:
            return g_song["artist"].lower() == tag["artist"].lower()
        if "part-song" in type:
            return g_song["title"].find(tp[0]) and g_song["artist"].lower() == tag["artist"].lower()

        return None

    def tag_compare(self, g_song, tag):
        if "track" not in g_song:
            g_song["track"] = 0

        return (
            g_song["title"].split(" ")[0].lower() == tag["title"].split(" ")[0].lower()
            and g_song["artist"].lower() == tag["artist"].lower()
        )

    def delete_song(self, sid):
        self.wc.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("\\", "/"))
class MusicSync(object):
    def __init__(self, email=None, password=None):
        self.mm = Musicmanager()
        self.wc = Webclient()
        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.wc.get_all_playlist_ids(auto=False)
        print "Got %d playlists." % len(self.playlists['user'])
        print ""


    def auth(self):
        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, artist_title_array, playlist_title = -99):
        if playlist_title == -99:
            title = "GMusicSync Playlist %3d"%time.time()
        else: title = str(playlist_title)
        print "Synching playlist: %s" % title
        if title not in self.playlists['user']:
            print "   didn't exist... creating..."
            self.playlists['user'][title] = [self.wc.create_playlist(title)]
        print ""

        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 = artist_title_array
        print "%d songs in local playlist" % len(pc_songs)

        # 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):
                existing_files += 1
                continue
            print ""
            try:
                print "Adding: %s - %s"%(fn[0],fn[1])
            except:
                print "Incorrect format for %r, expecting ('artist','title')"%fn
                continue
            online = self.find_song(fn)
            song_id = None
            if online:
                song_id = online['id']
                print "   already uploaded [%s]" % song_id
            else:
                print "   Sorry, can't find song."

            if not song_id:
                failed_files += 1
                continue

            added = self.wc.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

        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='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, artist_title, goog_songs):
        i = 0
        while i < len(goog_songs):
            if self.song_compare(goog_songs[i], artist_title):
                goog_songs.pop(i)
                return True
            i += 1
        return False

    def find_song(self, artist_title):
        try:
            artist = artist_title[0]
            title = artist_title[1]
        except: return None
        results = self.wc.search(title)
        print "\t",results[:2]
        for r in results['song_hits']:
            if self.song_compare(r, artist_title):
                return r
        return None

    def song_compare(self, g_song, artist_title):
        try:
            artist = artist_title[0]
            title = artist_title[1]
        except: return False
        # TODO: add fuzzy matching
        return g_song['title'].lower() == title.lower() and\
               g_song['artist'].lower() == artist.lower()

    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('\\', '/'))
Пример #3
0
class MusicSync(object):
    def __init__(self, email=None, password=None):
        self.mm = Musicmanager()
        self.wc = Webclient()
        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.wc.get_all_playlist_ids(auto=False)
        print "Got %d playlists." % len(self.playlists['user'])
        print "Fetching songs from Google..."
        self.songs = self.wc.get_all_songs()
        print "Got %d songs." % len(self.songs)        

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

        print "Logged in as %s" % self.email
        
        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"
        
    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 in self.playlists['user']:
            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)
            
            # 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
            # print "Google songs: %s" % goog_songs
            for fn in pc_songs:
                if self.file_already_in_list(fn, goog_songs):
                    existing_files += 1
                    continue
                print "  adding: %s" % os.path.basename(fn)
                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 "Too many disconnections - quitting. Please try running the script again."
                               exit()
    
                            print "Connection Error -- Reattempting login"
                            fatal_count += 1
                            self.wc.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"
    
                if not song_id:
                    failed_files += 1
                    continue
    
                added = self.wc.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 s in goog_songs:
                    print "  removing: %s" % s['title']
                    self.wc.remove_songs_from_playlist(plid, s.id)
                    time.sleep(.3) # Don't spam the server too fast...
                    removed_files += 1
                
            print "%d songs unmodified" % existing_files
            print "%d songs added" % added_files
            print "%d songs failed" % failed_files
            print "%d songs removed" % removed_files
            print "Ended: %s" % filename
        else:
            print "  playlist %s didn't exist - skipping" % filename

    def get_files_from_playlist(self, filename):
        files = []
        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):
        tag = self.get_id3_tag(filename)
        i = 0
        while i < len(goog_songs):
            # print "checking: %s" % tag
            # print "against: %s" % goog_songs[i]
            if self.tag_compare(goog_songs[i], tag):
                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):
        tag = self.get_id3_tag(filename)
        
        # NOTE - diagnostic print here to check results if you're creating duplicates
        print "  [%s][%s][%s][%s]" % (tag['title'], tag['artist'], tag['album'], tag['track'])
        for s in self.songs:
            if self.tag_compare(s, tag):
                print "  %s matches %s" % (s['title'], tag['title']) 
                return s
        print "  No matches for %s" % tag['title']
        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 'track' not in g_song:
            g_song['track'] = 0
        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.wc.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('\\', '/'))
class MusicSync(object):
    def __init__(self, email=None, password=None):
        self.mm = Musicmanager()
        self.wc = Webclient()
        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.wc.get_all_playlist_ids(auto=False)
        print "Got %d playlists." % len(self.playlists['user'])
        print ""

    def auth(self):
        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, artist_title_array, playlist_title=-99):
        if playlist_title == -99:
            title = "GMusicSync Playlist %3d" % time.time()
        else:
            title = str(playlist_title)
        print "Synching playlist: %s" % title
        if title not in self.playlists['user']:
            print "   didn't exist... creating..."
            self.playlists['user'][title] = [self.wc.create_playlist(title)]
        print ""

        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 = artist_title_array
        print "%d songs in local playlist" % len(pc_songs)

        # 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):
                existing_files += 1
                continue
            print ""
            try:
                print "Adding: %s - %s" % (fn[0], fn[1])
            except:
                print "Incorrect format for %r, expecting ('artist','title')" % fn
                continue
            online = self.find_song(fn)
            song_id = None
            if online:
                song_id = online['id']
                print "   already uploaded [%s]" % song_id
            else:
                print "   Sorry, can't find song."

            if not song_id:
                failed_files += 1
                continue

            added = self.wc.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

        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='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, artist_title, goog_songs):
        i = 0
        while i < len(goog_songs):
            if self.song_compare(goog_songs[i], artist_title):
                goog_songs.pop(i)
                return True
            i += 1
        return False

    def find_song(self, artist_title):
        try:
            artist = artist_title[0]
            title = artist_title[1]
        except:
            return None
        results = self.wc.search(title)
        print "\t", results[:2]
        for r in results['song_hits']:
            if self.song_compare(r, artist_title):
                return r
        return None

    def song_compare(self, g_song, artist_title):
        try:
            artist = artist_title[0]
            title = artist_title[1]
        except:
            return False
        # TODO: add fuzzy matching
        return g_song['title'].lower() == title.lower() and\
               g_song['artist'].lower() == artist.lower()

    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('\\', '/'))
Пример #5
0
class MusicSync(object):
    def __init__(self, email=None, password=None):
        self.mm = Musicmanager()
        self.wc = Webclient()
        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.wc.get_all_playlist_ids(auto=False)
        print "Got %d playlists." % len(self.playlists['user'])
        print ""


    def auth(self):
        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=False):
        filename = self.get_platform_path(filename)
        os.chdir(os.path.dirname(filename))
        title = os.path.splitext(os.path.basename(filename))[0]
        print "Synching 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 = 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)

        # 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):
                existing_files += 1
                continue
            print ""
            print "Adding: %s" % os.path.basename(fn)
            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.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"

            if not song_id:
                failed_files += 1
                continue

            added = self.wc.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 s in goog_songs:
                print ""
                print "Removing: %s" % s['title']
                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='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):
        tag = self.get_id3_tag(filename)
        i = 0
        while i < len(goog_songs):
            if self.tag_compare(goog_songs[i], tag):
                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):
        tag = self.get_id3_tag(filename)
        results = self.wc.search(tag['title'])
        # NOTE - dianostic print here to check results if you're creating duplicates
        #print results['song_hits']
        #print "%s ][ %s ][ %s ][ %s" % (tag['title'], tag['artist'], tag['album'], tag['track'])
        for r in results['song_hits']:
            if self.tag_compare(r, tag):
                # TODO: add rough time check to make sure its "close"
                return r
        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 'track' not in g_song:
            g_song['track'] = 0
        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.wc.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('\\', '/'))