def do_delete(args): logger.success("Logging in to Mobile Client") mc = google_music.mobileclient(args.username, device_id=args.device_id) if not mc.is_authenticated: sys.exit("Failed to authenticate Mobile Client") to_delete = filter_google_dates( get_google_songs(mc, filters=args.filters), created_in=args.get('created_in'), created_on=args.get('created_on'), created_before=args.get('created_before'), created_after=args.get('created_after'), modified_in=args.get('modified_in'), modified_on=args.get('modified_on'), modified_before=args.get('modified_before'), modified_after=args.get('modified_after')) if not to_delete: logger.success("No songs to delete") elif args.dry_run: logger.success(f"Found {len(to_delete)} songs to delete") for song in to_delete: title = song.get('title', "<empty>") artist = song.get('artist', "<empty>") album = song.get('album', "<empty>") song_id = song['id'] logger.success(f"{title} -- {artist} -- {album} ({song_id})") else: confirm = args.yes or input( f"\nAre you sure you want to delete {len(to_delete)} song(s) from Google Music? (y/n) " ) in ("y", "Y") if confirm: song_num = 0 total = len(to_delete) pad = len(str(total)) for song in to_delete: song_num += 1 title = song.get('title', "<empty>") artist = song.get('artist', "<empty>") album = song.get('album', "<empty>") song_id = song['id'] logger.info( f"Deleting {title} -- {artist} -- {album} ({song_id})") mc.song_delete(song) logger.success(f"Deleted {song_num:>{pad}}/{total}") else: logger.success("No songs deleted") mc.logout() logger.success("All done!")
def do_search(args): logger.log('NORMAL', "Logging in to Mobile Client") mc = google_music.mobileclient(args.username, device_id=args.device_id) if not mc.is_authenticated: sys.exit("Failed to authenticate Mobile Client") search_results = get_google_songs(mc, filters=args.filters) if any( args.get(option) for option in [ 'created_in', 'created_on', 'created_before', 'created_after', 'modified_in', 'modified_on', 'modified_before', 'modified_after', ]): search_results = filter_google_dates( search_results, created_in=args.get('created_in'), created_on=args.get('created_on'), created_before=args.get('created_before'), created_after=args.get('created_after'), modified_in=args.get('modified_in'), modified_on=args.get('modified_on'), modified_before=args.get('modified_before'), modified_after=args.get('modified_after')) search_results = natsorted(search_results, key=lambda song: (song.get('artist', ''), song.get('album', ''), song.get('trackNumber', 0))) if search_results: result_num = 0 total = len(search_results) pad = len(str(total)) confirm = (args.yes or input(f"\nDisplay {len(search_results)} results? (y/n) ") in ("y", "Y")) if confirm: for result in search_results: result_num += 1 title = result.get('title', "<empty>") artist = result.get('artist', "<empty>") album = result.get('album', "<empty>") song_id = result['id'] logger.log('NORMAL', "{:>{}}/{} {} -- {} -- {} ({})", result_num, pad, total, title, artist, album, song_id) else: logger.log('NORMAL', "No songs found matching query")
def do_delete(args): logger.log('NORMAL', "Logging in to Mobile Client") mc = google_music.mobileclient(args.username, device_id=args.device_id) if not mc.is_authenticated: sys.exit("Failed to authenticate Mobile Client") to_delete = filter_google_dates( get_google_songs(mc, filters=args.filters), created_in=args.get('created_in'), created_on=args.get('created_on'), created_before=args.get('created_before'), created_after=args.get('created_after'), modified_in=args.get('modified_in'), modified_on=args.get('modified_on'), modified_before=args.get('modified_before'), modified_after=args.get('modified_after')) logger.info("Found {} songs to delete", len(to_delete)) if not to_delete: logger.log('NORMAL', "No songs to delete") elif not args.dry_run: confirm = args.yes or input( f"\nAre you sure you want to delete {len(to_delete)} song(s) from Google Music? (y/n) " ) in ("y", "Y") if confirm: logger.log('NORMAL', "Deleting songs") song_num = 0 total = len(to_delete) pad = len(str(total)) for song in to_delete: song_num += 1 title = song.get('title', "<empty>") artist = song.get('artist', "<empty>") album = song.get('album', "<empty>") song_id = song['id'] logger.trace("Deleting {} -- {} -- {} ({})", title, artist, album, song_id) mc.song_delete(song) logger.info("Deleted {:>{}}/{}", song_num, pad, total) else: logger.info("No songs deleted") elif logger._min_level <= 15: for song in to_delete: title = song.get('title', "<empty>") artist = song.get('artist', "<empty>") album = song.get('album', "<empty>") song_id = song['id'] logger.log('ACTION_SUCCESS', "{} -- {} -- {} ({})", title, artist, album, song_id)
def do_download(args): logger.success("Logging in to Music Manager") mm = google_music.musicmanager(args.username, uploader_id=args.uploader_id) if not mm.is_authenticated: sys.exit("Failed to authenticate Music Manager") logger.success("Logging in to Mobile Client") mc = google_music.mobileclient(args.username, device_id=args.device_id) if not mc.is_authenticated: sys.exit("Failed to authenticate Mobile Client") logger.success("Loading Google songs") google_songs = get_google_songs(mm, filters=args.filters) base_path = template_to_base_path(args.output, google_songs) filepaths = [base_path] if args.include: filepaths.extend(args.include) mc_songs = get_google_songs(mc, filters=args.filters) if any( args.get(option) for option in [ 'created_in', 'created_on', 'created_before', 'created_after', 'modified_in', 'modified_on', 'modified_before', 'modified_after' ]): mc_songs = filter_google_dates( mc_songs, created_in=args.get('created_in'), created_on=args.get('created_on'), created_before=args.get('created_before'), created_after=args.get('created_after'), modified_in=args.get('modified_in'), modified_on=args.get('modified_on'), modified_before=args.get('modified_before'), modified_after=args.get('modified_after')) logger.success("Loading local songs") local_songs = get_local_songs(filepaths, filters=args.filters, max_depth=args.max_depth, exclude_paths=args.exclude_paths, exclude_regexes=args.exclude_regexes, exclude_globs=args.exclude_globs) missing_songs = [] if args.use_hash: logger.success("Comparing hashes") existing_songs = [] google_client_id_map = { song.get('clientId'): song for song in mc_songs } local_client_ids = {generate_client_id(song) for song in local_songs} for client_id, mc_song in google_client_id_map.items(): song = first_true( (song for song in google_songs), pred=lambda song: song.get('id') == mc_song.get('id')) if client_id not in local_client_ids: missing_songs.append(song) else: existing_songs.append(song) logger.success( f"Found {len(existing_songs)} songs already exist by audio hash") if logger._min_level <= 20: for song in existing_songs: title = song.get('title', "<title>") artist = song.get('artist', "<artist>") album = song.get('album', "<album>") song_id = song['id'] logger.info(f"{title} -- {artist} -- {album} ({song_id})") if args.use_metadata: if args.use_hash: google_songs = missing_songs if google_songs: logger.success("Comparing metadata") missing_songs = natsorted( gm_utils.find_missing_items( google_songs, local_songs, fields=['artist', 'album', 'title', 'tracknumber'], normalize_values=True)) existing_songs = natsorted( gm_utils.find_existing_items( google_songs, local_songs, fields=['artist', 'album', 'title', 'tracknumber'], normalize_values=True)) logger.success( f"Found {len(existing_songs)} songs already exist by metadata") if logger._min_level <= 20: for song in existing_songs: title = song.get('title', "<title>") artist = song.get('artist', "<artist>") album = song.get('album', "<album>") song_id = song['id'] logger.info(f"{title} -- {artist} -- {album} ({song_id})") if not args.use_hash and not args.use_metadata: missing_songs = google_songs to_download = natsorted(missing_songs) if not to_download: logger.success("No songs to download") elif args.dry_run: logger.success(f"Found {len(to_download)} songs to download") if logger._min_level <= 20: for song in to_download: title = song.get('title', "<title>") artist = song.get('artist', "<artist>") album = song.get('album', "<album>") song_id = song['id'] logger.info(f"{title} -- {artist} -- {album} ({song_id})") else: download_songs(mm, to_download, template=args.output) mc.logout() mm.logout() logger.success("All done!")
def do_upload(args): logger.success("Logging in to Music Manager") mm = google_music.musicmanager(args.username, uploader_id=args.uploader_id) if not mm.is_authenticated: sys.exit("Failed to authenticate Music Manager") logger.success("Logging in to Mobile Client") mc = google_music.mobileclient(args.username, device_id=args.device_id) if not mc.is_authenticated: sys.exit("Failed to authenticate Mobile Client") logger.success("Loading local songs") local_songs = get_local_songs(args.include, filters=args.filters, max_depth=args.max_depth, exclude_paths=args.exclude_paths, exclude_regexes=args.exclude_regexes, exclude_globs=args.exclude_globs) if any( args.get(option) for option in [ 'created_in', 'created_on', 'created_before', 'created_after', 'modified_in', 'modified_on', 'modified_before', 'modified_after' ]): local_songs = filter_local_dates( local_songs, created_in=args.get('created_in'), created_on=args.get('created_on'), created_before=args.get('created_before'), created_after=args.get('created_after'), modified_in=args.get('modified_in'), modified_on=args.get('modified_on'), modified_before=args.get('modified_before'), modified_after=args.get('modified_after')) missing_songs = [] if args.use_hash: logger.success("Comparing hashes") existing_songs = [] google_client_ids = { song.get('clientId', '') for song in get_google_songs(mc) } for song in local_songs: if generate_client_id(song) not in google_client_ids: missing_songs.append(song) else: existing_songs.append(song) logger.success( f"Found {len(existing_songs)} songs already exist by audio hash") if logger._min_level <= 20: for song in natsorted(existing_songs): logger.info(song) if args.use_metadata: if args.use_hash: local_songs = missing_songs if local_songs: logger.success("Comparing metadata") google_songs = get_google_songs(mm, filters=args.filters) missing_songs = natsorted( gm_utils.find_missing_items( local_songs, google_songs, fields=['artist', 'album', 'title', 'tracknumber'], normalize_values=True)) existing_songs = natsorted( gm_utils.find_existing_items( local_songs, google_songs, fields=['artist', 'album', 'title', 'tracknumber'], normalize_values=True)) logger.success( f"Found {len(existing_songs)} songs already exist by metadata") if logger._min_level <= 20: for song in existing_songs: logger.info(song) if not args.use_hash and not args.use_metadata: missing_songs = local_songs to_upload = natsorted(missing_songs) if not to_upload: logger.success("No songs to upload") elif args.dry_run: logger.success(f"Found {len(to_upload)} songs to upload") if logger._min_level <= 20: for song in to_upload: logger.info(song) else: upload_songs(mm, to_upload, album_art=args.album_art, no_sample=args.no_sample, delete_on_success=args.delete_on_success) mc.logout() mm.logout() logger.success("All done!")
def do_download(args): logger.log('NORMAL', "Logging in to Music Manager") mm = google_music.musicmanager(args.username, uploader_id=args.uploader_id) if not mm.is_authenticated: sys.exit("Failed to authenticate Music Manager") logger.log('NORMAL', "Logging in to Mobile Client") mc = google_music.mobileclient(args.username, device_id=args.device_id) if not mc.is_authenticated: sys.exit("Failed to authenticate Mobile Client") google_songs = get_google_songs(mm, filters=args.filters) base_path = template_to_base_path(args.output, google_songs) filepaths = [base_path, *args.include] mc_songs = get_google_songs(mc, filters=args.filters) if any( args.get(option) for option in [ 'created_in', 'created_on', 'created_before', 'created_after', 'modified_in', 'modified_on', 'modified_before', 'modified_after', ]): mc_songs = filter_google_dates( mc_songs, created_in=args.get('created_in'), created_on=args.get('created_on'), created_before=args.get('created_before'), created_after=args.get('created_after'), modified_in=args.get('modified_in'), modified_on=args.get('modified_on'), modified_before=args.get('modified_before'), modified_after=args.get('modified_after')) local_songs = get_local_songs(filepaths, filters=args.filters, max_depth=args.max_depth, exclude_paths=args.exclude_paths, exclude_regexes=args.exclude_regexes, exclude_globs=args.exclude_globs) missing_songs = [] existing_songs = [] if args.use_hash: if google_songs and local_songs: logger.log('NORMAL', "Comparing hashes") google_client_id_map = { mc_song.get('clientId'): mc_song for mc_song in mc_songs } local_client_ids = { generate_client_id(song) for song in local_songs } for client_id, mc_song in google_client_id_map.items(): song = first_true( (song for song in google_songs), pred=lambda song: song.get('id') == mc_song.get('id')) if song is not None: if client_id not in local_client_ids: missing_songs.append(song) else: existing_songs.append(song) logger.info("Found {} songs already exist by audio hash", len(existing_songs)) if logger._min_level <= 5: for song in existing_songs: title = song.get('title', "<title>") artist = song.get('artist', "<artist>") album = song.get('album', "<album>") song_id = song['id'] logger.trace("{} -- {} -- {} ({})", title, artist, album, song_id) else: missing_songs = google_songs if not google_songs and not local_songs: logger.log('NORMAL', "No songs to compare hashes.") elif not google_songs: logger.log('NORMAL', "No Google songs to compare hashes.") elif not local_songs: logger.log('NORMAL', "No local songs to compare hashes.") if args.use_metadata: if args.use_hash: google_songs = missing_songs if google_songs and local_songs: logger.log('NORMAL', "Comparing metadata") missing_songs = natsorted( gm_utils.find_missing_items( google_songs, local_songs, fields=['artist', 'album', 'title', 'tracknumber'], normalize_values=True)) existing_songs = natsorted( gm_utils.find_existing_items( google_songs, local_songs, fields=['artist', 'album', 'title', 'tracknumber'], normalize_values=True)) logger.info("Found {} songs already exist by metadata", len(existing_songs)) if logger._min_level <= 5: for song in existing_songs: title = song.get('title', "<title>") artist = song.get('artist', "<artist>") album = song.get('album', "<album>") song_id = song['id'] logger.trace("{} -- {} -- {} ({})", title, artist, album, song_id) else: if not google_songs and not local_songs: logger.log('NORMAL', "No songs to compare metadata.") elif not google_songs: logger.log('NORMAL', "No Google songs to compare metadata.") elif not local_songs: logger.log('NORMAL', "No local songs to compare metadata.") if not args.use_hash and not args.use_metadata: missing_songs = google_songs logger.log('NORMAL', "Sorting songs") to_download = natsorted(missing_songs) logger.info("Found {} songs to download", len(to_download)) if not args.dry_run: download_songs(mm, to_download, template=args.output) elif logger._min_level <= 15: for song in to_download: title = song.get('title', "<title>") artist = song.get('artist', "<artist>") album = song.get('album', "<album>") song_id = song['id'] logger.log('ACTION_SUCCESS', "{} -- {} -- {} ({})", title, artist, album, song_id)
def do_upload(args): logger.log('NORMAL', "Logging in to Music Manager") mm = google_music.musicmanager(args.username, uploader_id=args.uploader_id) if not mm.is_authenticated: sys.exit("Failed to authenticate Music Manager") logger.log('NORMAL', "Logging in to Mobile Client") mc = google_music.mobileclient(args.username, device_id=args.device_id) if not mc.is_authenticated: sys.exit("Failed to authenticate Mobile Client") local_songs = get_local_songs( args.include, filters=args.filters, max_depth=args.max_depth, exclude_paths=args.exclude_paths, exclude_regexes=args.exclude_regexes, exclude_globs=args.exclude_globs ) creation_dates = [ args[option] for option in [ 'created_in', 'created_on', 'created_before', 'created_after', ] if option in args ] modification_dates = [ args[option] for option in [ 'modified_in', 'modified_on', 'modified_before', 'modified_after', ] if option in args ] local_songs = filter_filepaths_by_dates( local_songs, creation_dates=creation_dates, modification_dates=modification_dates, ) missing_songs = [] if args.use_hash: logger.log('NORMAL', "Comparing hashes") existing_songs = [] google_client_ids = {song.get('clientId', '') for song in get_google_songs(mc)} for song in local_songs: if generate_client_id(song) not in google_client_ids: missing_songs.append(song) else: existing_songs.append(song) logger.info("Found {} songs already exist by audio hash", len(existing_songs)) if logger._core.min_level <= 5: for song in natsorted(existing_songs): logger.trace(song) if args.use_metadata: if args.use_hash: local_songs = missing_songs if local_songs: logger.log('NORMAL', "Comparing metadata") google_songs = get_google_songs(mm, filters=args.filters) missing_songs = natsorted( gm_utils.find_missing_items( local_songs, google_songs, fields=['artist', 'album', 'title', 'tracknumber'], normalize_values=True ) ) existing_songs = natsorted( gm_utils.find_existing_items( local_songs, google_songs, fields=['artist', 'album', 'title', 'tracknumber'], normalize_values=True ) ) logger.info("Found {} songs already exist by metadata", len(existing_songs)) if logger._core.min_level <= 5: for song in existing_songs: logger.trace(song) if not args.use_hash and not args.use_metadata: missing_songs = local_songs logger.log('NORMAL', "Sorting songs") to_upload = natsorted(missing_songs) logger.info("Found {} songs to upload", len(to_upload)) if not args.dry_run: upload_songs( mm, to_upload, album_art=args.album_art, no_sample=args.no_sample, delete_on_success=args.delete_on_success ) elif logger._core.min_level <= 15: for song in to_upload: logger.log( 'ACTION_SUCCESS', song )