def _process_youtube_upload(youtube_url, ydl_opts, music, metadata_opts, credential, uploader_name): manager = Musicmanager() manager.login(credential, uploader_name=uploader_name) with youtube_dl.YoutubeDL(ydl_opts) as ydl: info_dict = ydl.extract_info(youtube_url, download=False) if music.title == "": music.title = info_dict.get('title', None) music.save() metadata_opts = { i: getattr(music, i) for i in [ 'title', 'album', 'composer', 'genre', 'language', 'artist', 'album_artist' ] } logger.debug('New metadata opts: ' + str(metadata_opts)) ffmpeg_mp3_metadata_pp = FFmpegMP3MetadataPP(ydl, metadata_opts) ydl.add_post_processor(ffmpeg_mp3_metadata_pp) ydl.download([youtube_url]) music_filepath = ydl.prepare_filename(info_dict) if not music_filepath.endswith('.mp3'): music_filepath = music_filepath.replace( music_filepath[music_filepath.rfind('.'):], '.mp3') logger.debug("Music filepath: " + music_filepath) manager.upload(music_filepath, enable_matching=True, enable_transcoding=False) # Already transcoding. if os.path.isfile(music_filepath): os.remove(music_filepath)
def download(mode, url, num): print(f"Downloading for {mode}") #Options ydl_opts = { 'format': 'bestaudio/best', 'postprocessors': [{ 'key': 'FFmpegExtractAudio', 'preferredcodec': 'mp3', 'preferredquality': '320', }], 'logger': MyLogger(), 'progress_hooks': [my_hook], 'outtmpl': folders[num][mode][0] + '/%(title)s.%(ext)s' } #Download the video and extract all the metadata with youtube_dl.YoutubeDL(ydl_opts) as ydl: info_dict = ydl.extract_info(url, download=True) video_title = info_dict.get('title', None) video_filename = '.'.join( ydl.prepare_filename(info_dict).split('.')[:-1]) + '.mp3' print(video_filename) #Edit mp3 tag. try: print(f"Editing artist tag to {mode.capitalize()}...") mp3 = MP3File(video_filename) mp3.artist = mode.title() mp3.save() insert_album_art(video_filename, info_dict['id']) print("Done!\n") except Exception as e: print("Error at editing mp3 tag.\n") print(e) #Backup if num != 3: try: print(f"Making a backup of {video_title}...") copy(video_filename, folders[num][mode][1]) print("Done!\n") except: print("Error at doing backup.\n") #Upload to google if num != 3: try: print(f"Uploading {video_title} to Google Music\n") print(f'With url {url}') mm = Musicmanager() mm.login(uploader_id='D0:50:99:83:B0:0C') mm.upload(video_filename) print("Done!\n") except Exception as e: print("Error at uploading the song to google:\n" + e)
def upload(path): mm = Musicmanager() mm.login() for root, dirs, files in os.walk(path): for file_ in files: mp3FilePath = os.path.join(root, file_) print( "upload : " + file_ ) mm.upload(mp3FilePath) os.remove(mp3FilePath)
class GoogleMusicManager: def __init__(self): self.api = Musicmanager(debug_logging=False) refresh_token = json.loads( settings['google']['musicmanager'])['refresh_token'] credentials = session.credentials_from_refresh_token( refresh_token, session.Mobileclient.oauth) self.api.login(credentials) def upload_song(self, file): self.api.upload(file)
class GoogleMusicManager: def __init__(self): self.api = Musicmanager(debug_logging=False) with open(path + "oauth.cred", 'w+') as tmp: tmp.write(settings['google']['musicmanager']) tmp.close() self.api.login(tmp.name) os.remove(tmp.name) def upload_song(self, file): self.api.upload(file)
class GoogleMusic: def __init__(self): self.api = Musicmanager() try: print(os.getcwd()) if not self.api.login(oauth_credentials=config.auth): self.api.perform_oauth(storage_filepath=config.auth, open_browser=False) self.api.login(oauth_credentials=config.auth) except: pass def upload_audio(self, filepath, transcode_quality='320k'): self.api.upload(filepath, enable_matching=False, transcode_quality=transcode_quality) print("Upload finished")
def main(): args, config = init() if not os.path.isdir(config['PROG']['DownloadPath']): os.mkdir(config['PROG']['DownloadPath']) sc_download( config['SOUNDCLOUD']['PlaylistUrl'], config['PROG']['DownloadPath'] ) mc = Mobileclient() mc.login(config['GMUSIC']['User'], config['GMUSIC']['Password'], Mobileclient.FROM_MAC_ADDRESS) mm = Musicmanager() if not (os.path.exists(gmclients.OAUTH_FILEPATH) and mm.login(gmclients.OAUTH_FILEPATH)): mm.perform_oauth(gmclients.OAUTH_FILEPATH, open_browser=True) if not mm.login(gmclients.OAUTH_FILEPATH): sys.stderr.write('Musicmanager could not authenticate') if config['GMUSIC']['TargetPlaylist'] not in set([item['name'] for item in mc.get_all_playlists() if not item['deleted']]): mc.create_playlist(name=config['GMUSIC']['TargetPlaylist'], description='Tracks synchronized using {}'.format(__prog__), public=False) playlist_id, current_members = gm_get_current_pl_member(mc, config['GMUSIC']['TargetPlaylist']) for track in os.listdir(config['PROG']['DownloadPath']): print('Uploading {}'.format(track)) uploaded_id = gm_extract_id(mm.upload('{}{}'.format(config['PROG']['DownloadPath'], track))) mc.add_songs_to_playlist(playlist_id, uploaded_id)
def upload(directory='.', oauth=os.environ['HOME'] + '/oauth', remove=False, uploader_id=__DEFAULT_MAC__): logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) logger.info("Init Daemon - Press Ctrl+C to quit") api = Musicmanager() event_handler = MusicToUpload() event_handler.api = api event_handler.path = directory event_handler.willDelete = remove event_handler.logger = logger if not api.login(oauth, uploader_id): print("Error with oauth credentials") sys.exit(1) files = [file for file in glob.glob(directory + '/**/*', recursive=True)] for file_path in files: if os.path.isfile(file_path): logger.info("Uploading : " + file_path) uploaded, matched, not_uploaded = api.upload(file_path, True) if remove and (uploaded or matched): os.remove(file_path) observer = Observer() observer.schedule(event_handler, directory, recursive=True) observer.start() try: while True: time.sleep(1) except KeyboardInterrupt: observer.stop() observer.join()
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 upload(request): # TODO: Async status updates? if not request.user.is_authenticated: return HttpResponse('log in first') credential = request.user.profile.google_oauth if not credential: return HttpResponse('no creds') manager = Musicmanager() # TODO: Maybe change mac address for each user? login_success = manager.login( credential, uploader_name="GMusicManagerOnline - {}".format(request.user.username)) form = MusicUploadForm() args = {'can_login': login_success, 'form': form, 'success': False} if request.method == "POST": form = MusicUploadForm(request.POST, request.FILES) if form.is_valid(): music = form.save() music_file = request.FILES.get('music_file') ext = music_file.name[music_file.name.rfind('.'):] fs = FileSystemStorage() filename = fs.save("{0}{1}".format(request.user.username, ext), music_file) music_filepath = fs.path(filename) post_filepath = music_filepath + ".mp3" options = { i: getattr(music, i) for i in [ 'title', 'album', 'composer', 'genre', 'language', 'artist', 'album_artist' ] if getattr(music, i) != "" } logger.info("Transcoding metadata: " + str(options)) options.update({'quality': '320k'}) _transcode(music_filepath, options, post_filepath) if os.path.isfile(music_filepath): os.remove(music_filepath) success, _, _ = manager.upload( # Already transcoding. post_filepath, enable_matching=True, enable_transcoding=False) if os.path.isfile(post_filepath): os.remove(post_filepath) args.update({'success': True}) args.update({'form': form}) manager.logout() return render(request, 'core/upload.html', args)
def upload(file_path): storage = oauth2client.file.Storage(CREDENTIALS_PATH) credentials = storage.get() if not credentials or credentials.invalid: Musicmanager.perform_oauth(CREDENTIALS_PATH, open_browser=False) mm = Musicmanager() mm.login(CREDENTIALS_PATH) result = mm.upload(file_path, enable_matching=True) if result[1]: raise Exception('{}はアップロード済みです'.format(file_path)) elif result[2]: raise Exception('アップロード失敗 {}'.format(result[2][file_path])) os.remove(file_path)
class GoogleManager(): def __init__(self, directory): self.music_dir = directory self.Google = Musicmanager() self.Google.login() def upload(self): files = [] for dirpath, dirnames, filenames in walk(self.music_dir): for name in filenames: if name.endswith('.mp3'): files += [join(dirpath, name)] for f in files: ret = self.Google.upload(f) print(ret)
def main(): path = sys.argv[1] if not os.path.exists(path): print("Invalid file path. %s" % path) return api = Musicmanager() api.login() if not api.is_authenticated(): print("Login Error\n") return print("start upload...\n") upload_info = api.upload(path) print(upload_info) print("upload is completed.\n") api.logout()
class Kotone: def __init__(self, device_id: str, cred_mc: oauth2client.client.OAuth2Credentials, cred_mm: oauth2client.client.OAuth2Credentials): self._mc = Mobileclient() if not self._mc.oauth_login(device_id, cred_mc): raise RuntimeError('Mobileclient login failed') self._mm = Musicmanager() if not self._mm.login(cred_mm, _UPLOADER_ID, _UPLOADER_NAME): raise RuntimeError('Musicmanager login failed') def get_songs(self): return self._mc.get_all_songs() def download_song(self, song_id: str) -> Tuple[str, bytes]: return self._mm.download_song(song_id) def stream_url(self, song_id: str) -> str: return self._mc.get_stream_url(song_id) def upload(self, paths): return self._mm.upload(paths)
def songs_uploader(self): ip = urllib2.urlopen('http://ip.42.pl/raw').read() #Obtain your public IP address mac_binary = str(get_mac()) #Obtain binary MAC address temp = mac_binary.replace(':', '').replace('-', '').replace('.', '').upper() mac_h3x = temp[:2] + ":" + ":".join([temp[i] + temp[i+1] for i in range(2,12,2)]) #Convert MAC from 48bit int to hexadecimal string user = pwd.getpwuid(os.getuid())[0] #Get your system's username api = Musicmanager() hostname = '<' + ip + '>' + '' + '(gmusicapi-{2.0.0})' Musicmanager.perform_oauth(storage_filepath='/home/' + user + '/.config/gmusicapi/oauth.cred', open_browser=False) api.login(oauth_credentials='/home/' + user + '/.config/gmusicapi/oauth.cred', uploader_id=mac_h3x, uploader_name=hostname) gmusicapi.clients.Musicmanager(debug_logging=True, validate=True) #newWorkingDirectory = '../home' #os.path.join(os.path.abspath(sys.path[0]), newWorkingDirectory) #Change the working directory filepath = '/home/blackram/Scrivania/BRES_/UMM/ciao.mp3' uploading = api.upload(filepath, transcode_quality=3, enable_matching=False) print 'Uploading...' f = open('uploading.txt','w') #log f.write(str(uploading)) f.close() final = re.search("GetUploadSession error 200: this song is already uploaded", open('uploading.txt','r').read()) if final is None: print '\033[32mTrack uploaded!\033[0m' else: print '\033[31mTrack already exists in your library!\033[0m' choice = raw_input("Exit from uploader? [Y/N] ") if choice == 'y' or choice == 'Y': print 'Return to main menu.' Musicmanager.logout(revoke_oauth=False) UMM.read_information() elif choice == 'n' or choice == 'N': print 'Okay.' UMM.songs_uploader()
print "usage:" + sys.argv[0] + " filename [playlist name]" sys.exit() file = params[1] if len(params) == 3: plname = params[2] else: plname = None mm = Musicmanager() api = Mobileclient() mm.login() api.login('GoogleID', 'Password') track = mm.upload(file) track_id = track[0][file] if plname: playlist_id = None playlists = api.get_all_playlists() for playlist in playlists: if plname == playlist['name']: playlist_id = playlist['id'] break if playlist_id == None: playlist_id = api.create_playlist(plname) api.add_songs_to_playlist(playlist_id, track_id)
class Gmusic(BeetsPlugin): def __init__(self): super(Gmusic, self).__init__() # Checks for OAuth2 credentials, # if they don't exist - performs authorization self.m = Musicmanager() if os.path.isfile(gmusicapi.clients.OAUTH_FILEPATH): self.m.login() else: self.m.perform_oauth() def commands(self): gupload = Subcommand('gmusic-upload', help=u'upload your tracks to Google Play Music') gupload.func = self.upload search = Subcommand('gmusic-songs', help=u'list of songs in Google Play Music library' ) search.parser.add_option('-t', '--track', dest='track', action='store_true', help='Search by track name') search.parser.add_option('-a', '--artist', dest='artist', action='store_true', help='Search by artist') search.func = self.search return [gupload, search] def upload(self, lib, opts, args): items = lib.items(ui.decargs(args)) files = [x.path.decode('utf-8') for x in items] ui.print_(u'Uploading your files...') self.m.upload(filepaths=files) ui.print_(u'Your files were successfully added to library') def search(self, lib, opts, args): password = config['gmusic']['password'] email = config['gmusic']['email'] password.redact = True email.redact = True # Since Musicmanager doesn't support library management # we need to use mobileclient interface mobile = Mobileclient() try: mobile.login(email.as_str(), password.as_str(), Mobileclient.FROM_MAC_ADDRESS) files = mobile.get_all_songs() except NotLoggedIn: ui.print_( u'Authentication error. Please check your email and password.' ) return if not args: for i, file in enumerate(files, start=1): print(i, ui.colorize('blue', file['artist']), file['title'], ui.colorize('red', file['album'])) else: if opts.track: self.match(files, args, 'title') else: self.match(files, args, 'artist') @staticmethod def match(files, args, search_by): for file in files: if ' '.join(ui.decargs(args)) in file[search_by]: print(file['artist'], file['title'], file['album'])
translator = Translator() # Get file paths from node file_names = translator.get_input() # Returns path to system stored oauth oauth_path = clients.OAUTH_FILEPATH # Init Musicmanager and login mm = Musicmanager() is_logged_in = mm.login(oauth_path) if is_logged_in: # Matching set to false due to lack of ffmpeg or avconv upload_result = mm.upload(file_names, '320k', False) print json.dumps(upload_result) # return upload_result if upload_result[0] != {}: print('Something happened here!') else: sys.exit('Not logged in')
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()
class GoolgeMusicUploader(object): "Google music upload class" def __init__(self, config): self.credential_file = '' if len(config.googleplay_credential_file) > 0: self.credential_file = config.googleplay_credential_file else: self.credential_file = clients.OAUTH_FILEPATH self.mac_address = config.mac_address_for_gplay self.manager = Musicmanager(False) self.logger = logging.getLogger(__name__) def login(self): "Logs in" if not self.manager.login(self.credential_file, self.mac_address): raise AuthError( 'Could not authenticate music manager using {}'.format( self.credential_file)) def logout(self): "Logs out" if self.manager.is_authenticated: success = self.manager.logout() if success: self.logger.info('Logged out of Google Play Music') else: self.logger.warning('Failed to log out of Google Play Music') def upload(self, track_dir): "Does the upload." if not self.manager.is_authenticated: raise AuthError( "Music Manager not authenticated. Call 'login' first.") if not isdir(track_dir): raise DirectoryNotFoundError(track_dir) files = absolute_files(track_dir) info = TrackInfo() info.load(get_track_info_file(files)) track_file = get_track_file(files) result = UploadResult(track_dir, track_file, info.full_title) if track_file == DEFAULT_FILE_NAME: result.set_failure('MP3 Track file not found') return result locked = lock_file_exists(track_dir) if locked: result.set_failure('Lock file exists') return result metadata = AudioMetadata(track_file) metadata.apply_album_art(get_album_art_file(files)) metadata.apply_track_info(info) success, message = self.__upload_file__(track_file) if success: result.set_success(message) else: result.set_failure(message) return result def __upload_file__(self, track_file): self.logger.info('Uploading %s', track_file) upload_result = self.manager.upload(track_file) if upload_result[0] != {}: return True, upload_result[0] elif upload_result[1] != {}: return True, upload_result[2] elif upload_result[2] != {}: reason = list(upload_result[2].items())[0] return False, 'Couldn\'t upload {} because {}'.format( reason[0], reason[1])
from gmusicapi import Musicmanager from os import listdir from os.path import isfile, join import os import time import subprocess api = Musicmanager() api.login() mypath = '/home/pi/musics' while True: onlyfiles = [ f for f in listdir(mypath) if isfile(join(mypath,f)) ] if len(onlyfiles) > 0: time.sleep(5) for file in onlyfiles: print(file) api.upload(mypath + '/' + file) os.remove(mypath + '/' + file)
os.chdir('.yt_to_play') Path('download_temp').mkdir(exist_ok=True) os.chdir('download_temp') ydl_opts = { 'format': 'bestaudio/best', 'postprocessors': [{ 'key': 'FFmpegExtractAudio', 'preferredcodec': 'mp3', 'preferredquality': '192', }], 'outtmpl': '%(title)s.%(ext)s', 'download_archive': '../downloaded.txt', } with youtube_dl.YoutubeDL(ydl_opts) as ydl: ydl.download(args) print('Uploading...') mm = Musicmanager() oauth = Path('../oauth.cred') if not (oauth.exists() and oauth.is_file()): mm.perform_oauth(oauth) mm.login(oauth) mm.upload([os.path.join(os.getcwd(), f) for f in os.listdir(os.getcwd())]) for f in os.listdir(os.getcwd()): os.remove(f)
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('\\', '/'))
class Gmusic(BeetsPlugin): def __init__(self): super().__init__() self.m = Musicmanager() # OAUTH_FILEPATH was moved in gmusicapi 12.0.0. if hasattr(Musicmanager, 'OAUTH_FILEPATH'): oauth_file = Musicmanager.OAUTH_FILEPATH else: oauth_file = gmusicapi.clients.OAUTH_FILEPATH self.config.add({ 'auto': False, 'uploader_id': '', 'uploader_name': '', 'device_id': '', 'oauth_file': oauth_file, }) if self.config['auto']: self.import_stages = [self.autoupload] def commands(self): gupload = Subcommand('gmusic-upload', help='upload your tracks to Google Play Music') gupload.func = self.upload search = Subcommand('gmusic-songs', help='list of songs in Google Play Music library') search.parser.add_option('-t', '--track', dest='track', action='store_true', help='Search by track name') search.parser.add_option('-a', '--artist', dest='artist', action='store_true', help='Search by artist') search.func = self.search return [gupload, search] def authenticate(self): if self.m.is_authenticated(): return # Checks for OAuth2 credentials, # if they don't exist - performs authorization oauth_file = self.config['oauth_file'].as_filename() if os.path.isfile(oauth_file): uploader_id = self.config['uploader_id'] uploader_name = self.config['uploader_name'] self.m.login(oauth_credentials=oauth_file, uploader_id=uploader_id.as_str().upper() or None, uploader_name=uploader_name.as_str() or None) else: self.m.perform_oauth(oauth_file) def upload(self, lib, opts, args): items = lib.items(ui.decargs(args)) files = self.getpaths(items) self.authenticate() ui.print_('Uploading your files...') self.m.upload(filepaths=files) ui.print_('Your files were successfully added to library') def autoupload(self, session, task): items = task.imported_items() files = self.getpaths(items) self.authenticate() self._log.info('Uploading files to Google Play Music...', files) self.m.upload(filepaths=files) self._log.info('Your files were successfully added to your ' + 'Google Play Music library') def getpaths(self, items): return [x.path for x in items] def search(self, lib, opts, args): password = config['gmusic']['password'] email = config['gmusic']['email'] uploader_id = config['gmusic']['uploader_id'] device_id = config['gmusic']['device_id'] password.redact = True email.redact = True # Since Musicmanager doesn't support library management # we need to use mobileclient interface mobile = Mobileclient() try: new_device_id = (device_id.as_str() or uploader_id.as_str().replace(':', '') or Mobileclient.FROM_MAC_ADDRESS).upper() mobile.login(email.as_str(), password.as_str(), new_device_id) files = mobile.get_all_songs() except NotLoggedIn: ui.print_( 'Authentication error. Please check your email and password.') return if not args: for i, file in enumerate(files, start=1): print(i, ui.colorize('blue', file['artist']), file['title'], ui.colorize('red', file['album'])) else: if opts.track: self.match(files, args, 'title') else: self.match(files, args, 'artist') @staticmethod def match(files, args, search_by): for file in files: if ' '.join(ui.decargs(args)) in file[search_by]: print(file['artist'], file['title'], file['album'])
from gmusicapi import Mobileclient, Musicmanager, Webclient from subprocess import getoutput os.chdir('/tmp') yt_url = sys.argv[1] # download song print('Downloading Song..') output = getoutput('youtube-dl -x --audio-format m4a --audio-quality 0 ' + yt_url) song_file = [ l.replace('[ffmpeg] Destination: ', '') for l in output.splitlines() if '[ffmpeg] Destination: ' in l ][0] gm_manager = Musicmanager() print("Logging in...") gm_manager.login() print("Uploading " + song_file) (uploaded, matched, not_uploaded) = gm_manager.upload([song_file]) if len(uploaded): print("Success") else: print("Unsucessful!") if len(matched): print("Already Exists...")
print("\n\nConverting to Audio") for vidName in onlyfiles: #clip = mp.VideoFileClip(mypath + "/" + vidName) #audioName = vidName.split(".mp4")[0] #clip.audio.write_audiofile(mypath + "/" + audioName+".mp3") with mp.VideoFileClip(mypath + "/" + vidName) as clip: audioName = vidName.split(".mp4")[0][num_digits:] clip.audio.write_audiofile(mypath + "/" + audioName + ".mp3") os.remove(mypath + "/" + vidName) # Uploads songs print("\n\nUploading songs to Google Music") audiofiles = [f for f in listdir(mypath) if isfile(join(mypath, f))] audiofiles = [mypath + "/" + f for f in audiofiles] results = mm.upload(audiofiles, enable_matching=False, enable_transcoding=True) # Gets song IDs uploaded = {**results[0], **results[1]} not_uploaded = results[2] ids = [] for audiofile in audiofiles: try: ids.append(uploaded[audiofile]) except KeyError: ids.append(not_uploaded[audiofile].split('ALREADY_EXISTS')[1][1:-1]) # Creates new Playlist print("\n\nCreating new playlist") playlistID = api.create_playlist(
from gmusicapi import Musicmanager from os import listdir from os.path import isfile, join import os import time import subprocess api = Musicmanager() api.login() mypath = '/home/pi/musics' while True: onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f))] if len(onlyfiles) > 0: time.sleep(5) for file in onlyfiles: print(file) api.upload(mypath + '/' + file) os.remove(mypath + '/' + file)
"Since this is your first time using the app you will need to allow your google account to authenticate..." ) print( "If you would like to change the app settings please modify the config file." ) mm.perform_oauth() config = open("yt2gpmCONFIG.txt", "w") firstRun = False config.write( f"firstRun:{firstRun}\nYoutube-DL Args:{ytDlString}\nremoveDownloadedFile (saves storage space):{removeFile}" ) config.close() print("Attempting to login to GPM.") mm.login() print(f"Successfuly logged into GPM, Uploading file: {latestFile}...") c = mm.upload(latestFile) mm.logout() print("File upload complete! Finishing up.") # remove mp3 file from directory (default) # if you want to keep the file change config (removeFile to true) if "True" in removeFile: os.remove(latestFile) print("Removed downloaded file. Goodbye.") else: print("Goodbye.") time.sleep(1)
#!/usr/bin/env python2 # see http://unofficial-google-music-api.readthedocs.org/en/latest/usage.html#usage # arch linux 'pacman -S python2-pip && pip2 install gmusicapi' from gmusicapi import Musicmanager from gmusicapi.compat import my_appdirs import sys import os.path mm = Musicmanager() # TODO use generic path for credentials OAUTH_FILEPATH = os.path.join(my_appdirs.user_data_dir, 'oauth.cred') if not os.path.isfile(OAUTH_FILEPATH): mm.perform_oauth() mm.login() # TODO handle errors (existing tracks/duplicates ...) # TODO handle errors (existing tracks/duplicates ...) track = sys.argv[1] uploaded, matched, not_uploaded = mm.upload(track) if uploaded[track]: sys.exit(0) sys.exit(1)
class User: def __init__(self, email, app_data_dir): self.email = email self.app_data_dir = app_data_dir self.db_lock = threading.Lock() def init(self, oauth_credentials): # Initialize the database logger.info("%s: Initializing database" % self.email) self._data_init() # Create the Musicmanager logger.info("%s: Logging in to Google music" % self.email) self.mm = Musicmanager() if not self.mm.login(oauth_credentials): logger.info("%s: Error logging in to Google music" % self.email) return False # Check if we already have any watch paths configured config = self._read_config() watched_paths = config["watched_paths"] if "watched_paths" in config else [] logger.info("%s: Found previously watched paths %s" % (self.email, watched_paths)) # Create the FileWatcher logger.info("%s: Creating the FileWatcher" % (self.email)) self.fw = FileWatcher(self.email, self._finished_writing_callback, watched_paths) return True def logout(self): self.mm.logout() self.fw.stop_watching() def _read_config(self): return util.read_config(os.path.join(self.app_data_dir, self.email, CFG_FILE_NAME)) def _write_config(self, config): util.write_config(config, os.path.join(self.app_data_dir, self.email, CFG_FILE_NAME)) def get_watched_paths(self): logger.debug("reading config from %s" % os.path.join(self.app_data_dir, self.email)) config = self._read_config() logger.debug("read config: %s" % config) return config["watched_paths"] if "watched_paths" in config else [] def add_watch_path(self, path): # Add to file watcher self.fw.watch(path) # Add to the config config = self._read_config() if "watched_paths" not in config: config["watched_paths"] = [path] else: if path not in config["watched_paths"]: config["watched_paths"].append(path) self._write_config(config) def remove_watch_path(self, path): # Remove from the file watcher self.fw.remove_watch(path) # Remove from the config config = self._read_config() if "watched_paths" in config: if path in config["watched_paths"]: config["watched_paths"].remove(path) else: logger.info("%s trying to remove watch path %s that we weren't watching" % (self.email, path)) self._write_config(config) def set_default_action(self, default_action): config = self._read_config() config["default_action"] = default_action self._write_config(config) def get_default_action(self): config = self._read_config() return config.get("default_action", "scan_only") def scan_existing_files(self): watched_paths = self.get_watched_paths() logger.debug("%s Scanning existing files in these directories: %s" % (self.email, watched_paths)) for watched_path in watched_paths: logger.debug("Scanning existing files in %s" % watched_path) for root, subFolders, files in os.walk(watched_path): logger.debug("root: %s, subfolders: %s, files: %s" % (root, subFolders, files)) for file in files: filename, fileExtension = os.path.splitext(file) logger.debug("looking at file %s, filename = %s, file extension = %s" % (file, filename, fileExtension)) if fileExtension == ".mp3": logger.debug("Found file %s" % file); self._update_path(os.path.join(root, file), FileStatus.Scanned) logger.debug("scanning finished"); def upload_scanned(self): songs = self.get_all_songs() for song_path in songs.keys(): if songs[song_path]["status"] == FileStatus.Scanned: logger.debug("Uploading song %s" % song_path) self.upload(song_path) return def _data_init(self): with self.db_lock: con = sql.connect(os.path.join(self.app_data_dir, self.email, DB_NAME)) with con: cur = con.cursor() cur.execute('''CREATE TABLE IF NOT EXISTS songs(path TEXT PRIMARY KEY, id TEXT, status TEXT)''') def _update_path(self, path, status, id=None, override=False): logger.info("Updating path %s with id %s and status %s" % (path, id, status)) info = ((path, "" if not id else id, status) ) with self.db_lock: con = sql.connect(os.path.join(self.app_data_dir, self.email, DB_NAME)) with con: cur = con.cursor() if not override: # Check if the song is already in the data store and, if so, what its status is cur.execute('''SELECT status FROM songs WHERE path=(?)''', (path,)) res = cur.fetchone() if res: res = res[0] if res == FileStatus.Uploaded: # If it's already been uploaded, don't override that status with something else return cur.execute('''REPLACE INTO songs VALUES(?, ?, ?)''', info) def _finished_writing_callback(self, new_file_path): logger.debug("New file %s" % new_file_path) filename, file_extension = os.path.splitext(new_file_path) if file_extension != ".mp3": logger.debug("Skipping non-mp3 file") return self._update_path(new_file_path, FileStatus.Scanned) if self.get_default_action() == "auto_upload": logger.info("Uploading new file: %s" % new_file_path) self.upload(new_file_path) @staticmethod def _find_gmusic_song(scanned_song_tags, gmusic_songs): try: artist = scanned_song_tags.artist.lower() album = scanned_song_tags.album.lower() title = scanned_song_tags.title.lower() #logger.debug("Found scanned song %s - %s - %s" % (artist, album, title)) except: logger.debug("Error grabbing song meta data") return # Search for an uploaded song that matches for gmusic_song in gmusic_songs: up_artist = gmusic_song['artist'].lower() up_album = gmusic_song['album'].lower() up_title = gmusic_song['title'].lower() #logger.debug("Looking at song %s - %s - %s" % (up_artist, up_album, up_title)) if artist == up_artist and album == up_album and title == up_title: #logger.debug("Found match!") return gmusic_song return None def sync_library(self): logger.debug("Syncing") uploaded_songs = self.mm.get_all_songs() scanned_songs = self.get_all_songs() logger.debug("found %d scanned songs" % len(scanned_songs)) # Go through all songs marked 'scanned' and search for a matching that's already been uploaded. # If we find one, grab the id and mark the song as uploaded for song_path in scanned_songs.keys(): # Since we only have the path, scan its tags to get the meta data audioFile = eyed3.load(song_path) if audioFile and audioFile.tag: local_song = scanned_songs[song_path] gmusic_song = User._find_gmusic_song(audioFile.tag, uploaded_songs) # Now make sure our static is in sync, possibilities: # 1) We show it as uploaded but google doesn't -> mark it as 'scanned', remove our id # 2) Google shows it as uploaded but we don't -> mark it as 'uploaded', add the id # 3) We both show it as uploaded and the ids match -> do nothing # 4) Neither of us think it was uploaded -> do nothing # 5) Google has it but we don't at all -> TODO!! (option for download?) we'll need to detect this another way (currently searching only by scanned songs) if gmusic_song: # Google shows this song if local_song['status'] == FileStatus.Scanned: # Google shows it as uploaded but we don't. Mark it as uploaded and update the id #logger.debug("'%s - %s - %s' was already uploaded, updating its id to %s" % (gmusic_song['artist'], gmusic_song['album'], gmusic_song['title'], gmusic_song['id'])) self._update_path(song_path, FileStatus.Uploaded, gmusic_song['id'], override=True) elif local_song['status'] == FileStatus.Uploaded: # We both show it as uploaded, make sure ids match if local_song['id'] != gmusic_song['id']: #logger.debug("Ids differ! Updating to use google's id") self._update_path(song_path, FileStatus.Uploaded, gmusic_song['id'], override=True) else: pass #logger.debug("Ids match! No update needed") else: # No matching song on google found if local_song['status'] == FileStatus.Uploaded: #logger.debug("We show the song as uploaded but google doesn't, changing status to scanned and clearing id") self._update_path(song_path, FileStatus.Scanned, override=True) else: pass #logger.debug("Neither side thinks it's uploaded, no update needed") else: logger.debug("Error loading metadata for song %s" % song_path) def upload(self, file_path): uploaded, matched, not_uploaded = self.mm.upload(file_path, enable_matching=False) # async me! if uploaded: logger.info("Uploaded song %s with ID %s" % (file_path, uploaded[file_path])) self._update_path(file_path, FileStatus.Uploaded, uploaded[file_path]) if matched: logger.info("Matched song %s with ID %s" % (file_path, matched[file_path])) self._update_path(file_path, FileStatus.Uploaded, uploaded[file_path]) if not_uploaded: reason_string = not_uploaded[file_path] if "ALREADY_EXISTS" in reason_string: song_id = reason_string[reason_string.find("(") + 1 : reason_string.find(")")] logger.info("Song already exists with ID %s, updating database" % song_id) # The song ID is located within parentheses in the reason string self._update_path(file_path, FileStatus.Uploaded, song_id) else: logger.info("Unable to upload song %s because %s" % (file_path, reason_string)) self._update_path(file_path, FileStatus.Error, reason_string) def get_all_songs(self): songs = {} with self.db_lock: con = sql.connect(os.path.join(self.app_data_dir, self.email, DB_NAME)) with con: cur = con.cursor() for row in cur.execute('''SELECT * FROM songs'''): song_path = row[0] song_id = row[1] song_status = row[2] songs[song_path] = {'id': song_id, 'status': song_status} return songs
#!/usr/bin/env python import sys from gmusicapi import Musicmanager mm = Musicmanager() # second parameter is the MAC address of the machine that was used to generate the oauth.cred. mm.login('/config/oauth.cred', '9C:B6:D0:D0:CE:1D') mm.upload("/files/" + sys.argv[1])
class Gmusic(BeetsPlugin): def __init__(self): super(Gmusic, self).__init__() self.m = Musicmanager() self.config.add({ u'auto': False, u'uploader_id': '', u'uploader_name': '', u'device_id': '', u'oauth_file': gmusicapi.clients.OAUTH_FILEPATH, }) if self.config['auto']: self.import_stages = [self.autoupload] def commands(self): gupload = Subcommand('gmusic-upload', help=u'upload your tracks to Google Play Music') gupload.func = self.upload search = Subcommand('gmusic-songs', help=u'list of songs in Google Play Music library') search.parser.add_option('-t', '--track', dest='track', action='store_true', help='Search by track name') search.parser.add_option('-a', '--artist', dest='artist', action='store_true', help='Search by artist') search.func = self.search return [gupload, search] def authenticate(self): if self.m.is_authenticated(): return # Checks for OAuth2 credentials, # if they don't exist - performs authorization oauth_file = self.config['oauth_file'].as_str() if os.path.isfile(oauth_file): uploader_id = self.config['uploader_id'] uploader_name = self.config['uploader_name'] self.m.login(oauth_credentials=oauth_file, uploader_id=uploader_id.as_str().upper() or None, uploader_name=uploader_name.as_str() or None) else: self.m.perform_oauth(oauth_file) def upload(self, lib, opts, args): items = lib.items(ui.decargs(args)) files = self.getpaths(items) self.authenticate() ui.print_(u'Uploading your files...') self.m.upload(filepaths=files) ui.print_(u'Your files were successfully added to library') def autoupload(self, session, task): items = task.imported_items() files = self.getpaths(items) self.authenticate() self._log.info(u'Uploading files to Google Play Music...', files) self.m.upload(filepaths=files) self._log.info(u'Your files were successfully added to your ' + 'Google Play Music library') def getpaths(self, items): return [x.path for x in items] def search(self, lib, opts, args): password = config['gmusic']['password'] email = config['gmusic']['email'] uploader_id = config['gmusic']['uploader_id'] device_id = config['gmusic']['device_id'] password.redact = True email.redact = True # Since Musicmanager doesn't support library management # we need to use mobileclient interface mobile = Mobileclient() try: new_device_id = (device_id.as_str() or uploader_id.as_str().replace(':', '') or Mobileclient.FROM_MAC_ADDRESS).upper() mobile.login(email.as_str(), password.as_str(), new_device_id) files = mobile.get_all_songs() except NotLoggedIn: ui.print_( u'Authentication error. Please check your email and password.' ) return if not args: for i, file in enumerate(files, start=1): print(i, ui.colorize('blue', file['artist']), file['title'], ui.colorize('red', file['album'])) else: if opts.track: self.match(files, args, 'title') else: self.match(files, args, 'artist') @staticmethod def match(files, args, search_by): for file in files: if ' '.join(ui.decargs(args)) in file[search_by]: print(file['artist'], file['title'], file['album'])
class Gmusic(BeetsPlugin): def __init__(self): super(Gmusic, self).__init__() # Checks for OAuth2 credentials, # if they don't exist - performs authorization self.m = Musicmanager() if os.path.isfile(gmusicapi.clients.OAUTH_FILEPATH): self.m.login() else: self.m.perform_oauth() def commands(self): gupload = Subcommand('gmusic-upload', help=u'upload your tracks to Google Play Music') gupload.func = self.upload search = Subcommand('gmusic-songs', help=u'list of songs in Google Play Music library') search.parser.add_option('-t', '--track', dest='track', action='store_true', help='Search by track name') search.parser.add_option('-a', '--artist', dest='artist', action='store_true', help='Search by artist') search.func = self.search return [gupload, search] def upload(self, lib, opts, args): items = lib.items(ui.decargs(args)) files = [x.path.decode('utf-8') for x in items] ui.print_(u'Uploading your files...') self.m.upload(filepaths=files) ui.print_(u'Your files were successfully added to library') def search(self, lib, opts, args): password = config['gmusic']['password'] email = config['gmusic']['email'] password.redact = True email.redact = True # Since Musicmanager doesn't support library management # we need to use mobileclient interface mobile = Mobileclient() try: mobile.login(email.as_str(), password.as_str(), Mobileclient.FROM_MAC_ADDRESS) files = mobile.get_all_songs() except NotLoggedIn: ui.print_( u'Authentication error. Please check your email and password.') return if not args: for i, file in enumerate(files, start=1): print(i, ui.colorize('blue', file['artist']), file['title'], ui.colorize('red', file['album'])) else: if opts.track: self.match(files, args, 'title') else: self.match(files, args, 'artist') @staticmethod def match(files, args, search_by): for file in files: if ' '.join(ui.decargs(args)) in file[search_by]: print(file['artist'], file['title'], file['album'])
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('\\', '/'))
class GMusicSync(BeetsPlugin): def __init__(self): super(GMusicSync, self).__init__() self._mm = Musicmanager(debug_logging=False) self._library_paths = get_library_paths('default') if not os.path.exists(self._library_paths.base): os.mkdir(self._library_paths.base); self._db = open_db(self._library_paths.db_file) if not os.path.exists(self._library_paths.oauth_file): self._mm.perform_oauth(self._library_paths.oauth_file) else: self._mm.login(self._library_paths.oauth_file) sync_command = make_command('gmusic-sync', self.sync_library, help='Sync library with Google Play Music') sync_command.parser.add_option('-p', '--pretend', dest='pretend', action='store_true', default=False) self._commands = [ sync_command, ] self.config['password'].redact = True def commands(self): return self._commands def sync_library(self, lib, opts, args): total_count = 0 uploaded_count = 0 error_count = 0 for item in lib.items(query=args): total_count = total_count + 1 status = self.sync_track(item, pretend=opts.pretend) if status == 'uploaded': uploaded_count = uploaded_count + 1 elif status == 'error': error_count = error_count + 1 print 'Summary:' print ' {0} tracks analyzed.'.format(total_count) print ' {0} tracks {1}uploaded.'.format(uploaded_count, 'would be ' if opts.pretend else '') print ' {0} tracks errored.'.format(error_count) def sync_track(self, item, pretend=False): item_mtime = arrow.get(item.current_mtime()) track_row = self._db.get_track(item.id) upload_track = track_row is None or \ track_row.gmusic_sync_time is None or \ track_row.gmusic_sync_time < item_mtime if upload_track: try: track_id = self.upload_track(item, pretend) if not pretend: track_row = TrackRow(id=item.id, gmusic_track_id=track_id, gmusic_sync_time=arrow.utcnow()) self._db.update_track(track_row) return 'uploaded' except Exception as err: print '>>> track failed to upload: {0}'.format(err) return 'error' return 'ok' def upload_track(self, item, pretend=False): print u'Uploading track: {artist} - {album} - [{track}] {title}'.format(**item) track_id = None if not pretend: uploaded, matched, not_uploaded = self._mm.upload(item.path, enable_matching=True) if item.path in uploaded: track_id = uploaded[item.path] print '>>> track uploaded (gmusic_trackid: {track_id})'.format(track_id=track_id) elif item.path in matched: track_id = matched[item.path] print '>>> track matched (gmusic_trackid: {track_id})'.format(track_id=track_id) else: reason = not_uploaded[item.path] m = re.search('ALREADY_EXISTS\((.*)\)', reason) if not m: raise GMusicTrackError(reason) track_id = m.group(1) print '>>> track already exists (gmusic_trackid: {track_id}'.format(track_id=track_id) else: track_id = '?' print '>>> track would be uploaded' return track_id
class GMClient(object): 'Wrapper class of gmusicapi.Mobileclient' def __init__(self): # Aplying patch to session.Musicmanager session.Musicmanager.login = MethodType(patched_musicmanaer_login, None, session.Musicmanager) self.man = Musicmanager(verify_ssl=False) self.all_songs = None def login(self): if not os.path.exists(OAUTH_PATH): logging.error('No {} exists'.format(OAUTH_PATH)) raise Exception('No {} exists'.format(OAUTH_PATH)) else: self.man.login(oauth_credentials=OAUTH_PATH, uploader_name='raspi_home') logging.info('Success!') # These are required to change meta data. # raspi_home does not require it. # if ('GOOGLE_PLAY_MUSIC_PASS' in os.environ and # 'GOOGLE_PLAY_MUSIC_USER' in os.environ): # self.api = Mobileclient() # self.api.login(os.environ['GOOGLE_PLAY_MUSIC_USER'], # os.environ['GOOGLE_PLAY_MUSIC_PASS'], # Mobileclient.FROM_MAC_ADDRESS) # logging.info('Logged in to google music') # self.is_available = True # else: # logging.warn('environmental variable GOOGLE_PLAY_MUSIC_PASS or GOOGLE_PLAY_MUSIC_USER' # ' is not available') # self.api = None def oauth(self): 'Run oauth for uploading/downloading songs' oauth_dir = os.path.dirname(OAUTH_PATH) if not os.path.exists(oauth_dir): logging.info('No oauth directory, create it') os.makedirs(oauth_dir) self.man.perform_oauth(open_browser=False, storage_filepath=OAUTH_PATH) # methods communicating with google server def update_songs(self): # if self.api is not None: # self.all_songs = self.api.get_all_songs() # else: # self.all_songs = [] self.all_songs = self.man.get_uploaded_songs() def get_all_songs(self): if self.all_songs is None: self.update_songs() return self.all_songs def get_songs(self, artist=None): return [ song for song in self.get_all_songs() if song['artist'] == artist ] def upload(self, file): if not os.path.exists(file): logging.error('No {} exists'.format(file)) else: (uploaded, matched, not_uploaded) = self.man.upload([file], enable_matching=True) if not_uploaded: logging.error('not uploaded because {}'.format(not_uploaded)) def has_song(self, title): return title in [song['title'] for song in self.get_all_songs()]