def main():
	cli = dict((key.lstrip("-<").rstrip(">"), value) for key, value in docopt(__doc__).items())

	print_ = safe_print if not cli['quiet'] else lambda *args, **kwargs: None

	mcw = MobileClientWrapper()
	mcw.login(cli['user'], cli['pass'])

	print_("Scanning for songs...\n")
	search_songs = mcw.get_google_songs(filters=cli['filter'], filter_all=cli['all'])
	search_songs.sort(key=lambda song: (song['artist'], song['album'], song['trackNumber']))

	if search_songs:
		if cli['yes'] or raw_input("Display results? (y/n) ").lower() == "y":
			print_()

			for song in search_songs:
				title = song.get('title', "<empty>")
				artist = song.get('artist', "<empty>")
				album = song.get('album', "<empty>")

				print("{0} _by_ {1} _from_ {2} ({3})".format(title, artist, album, song['id']))

			print_()
	else:
		safe_print("\nNo songs found matching query")

	mcw.logout()
	print_("\nAll done!")
Exemple #2
0
def main():
    cli = dict((key.lstrip("-<").rstrip(">"), value)
               for key, value in docopt(__doc__).items())

    print_ = safe_print if not cli['quiet'] else lambda *args, **kwargs: None

    mcw = MobileClientWrapper()
    mcw.login(cli['user'], cli['pass'])

    print_("Scanning for songs...\n")
    search_songs = mcw.get_google_songs(filters=cli['filter'],
                                        filter_all=cli['all'])
    search_songs.sort(
        key=lambda song: (song['artist'], song['album'], song['trackNumber']))

    if search_songs:
        if cli['yes'] or raw_input("Display results? (y/n) ").lower() == "y":
            print_()

            for song in search_songs:
                title = song.get('title', "<empty>")
                artist = song.get('artist', "<empty>")
                album = song.get('album', "<empty>")

                print("{0} _by_ {1} _from_ {2} ({3})".format(
                    title, artist, album, song['id']))

            print_()
    else:
        safe_print("\nNo songs found matching query")

    mcw.logout()
    print_("\nAll done!")
    def unsubscribe_from_playlists(self,
                                   playlist_ids: list = [],
                                   contains: list = []):
        removed_subscriptions = False

        for playlist_id in playlist_ids:
            if playlist_id not in self.subscribed_playlists.keys():
                raise Exception(
                    "Cannot unsubscribe from playlist with id {}, because the user is not subscripted to it."
                    .format(playlist_id))

            playlist = self.subscribed_playlists[playlist_id]
            removed_subscriptions = True
            safe_print("Unsubscribed from playlist {} by {}".format(
                playlist.name, playlist.owner_id))
            del self.subscribed_playlists[playlist_id]

        # Lowercase pattern matching with the playlist name
        subscribed_playlists = list(self.subscribed_playlists.values())
        for pattern in contains:
            pattern = pattern.lower()
            for playlist in subscribed_playlists:
                if pattern in playlist.name.lower():
                    removed_subscriptions = True
                    safe_print("Unsubscribed from playlist {} by {}".format(
                        playlist.name, playlist.owner_id))
                    del self.subscribed_playlists[playlist.id]

        # Only save if we actually changed something. TODO: Save these in a separate file.
        if removed_subscriptions:
            self._save()
def main():
	cli = dict((key.lstrip("-<").rstrip(">"), value) for key, value in docopt(__doc__).items())

	print_ = safe_print if not cli['quiet'] else lambda *args, **kwargs: None

	if not cli['output']:
		cli['output'] = os.getcwd()

	mmw = MusicManagerWrapper(log=cli['log'])
	mmw.login(oauth_file=cli['cred'], uploader_id=cli['uploader-id'])

	download_songs = mmw.get_google_songs(filters=cli['filter'], filter_all=cli['all'])

	download_songs.sort(key=lambda song: (song['artist'], song['album'], song['track_number']))

	if cli['dry-run']:
		print_("Found {0} songs to download".format(len(download_songs)))

		if download_songs:
			safe_print("\nSongs to download:\n")
			for song in download_songs:
				safe_print("{0} by {1}".format(song['title'], song['artist']))
		else:
			safe_print("\nNo songs to download")
	else:
		if download_songs:
			print_("Downloading {0} songs from Google Music\n".format(len(download_songs)))
			mmw.download(download_songs, cli['output'])
		else:
			safe_print("\nNo songs to download")

	mmw.logout()
	print_("\nAll done!")
Exemple #5
0
    def __init__(
        self,
        spotify: Spotify,
        user_id: str,
        name: str = default_name,
        description: str = default_description,
    ):
        safe_print(
            "Creating playlist with name {} and the following description:\n\t'{}'"
            .format(name, description))
        feed = spotify.user_playlist_create(user_id,
                                            name=name,
                                            public=False,
                                            description=description)

        self.id = feed["id"]
        self.name = feed["name"]
        self.last_update = datetime.utcnow()
def main():
    cli = dict((key.lstrip("-<").rstrip(">"), value)
               for key, value in docopt(__doc__).items())

    print_ = safe_print if not cli['quiet'] else lambda *args, **kwargs: None

    mcw = MobileClientWrapper()
    mcw.login(cli['user'], cli['pass'])

    delete_songs = mcw.get_google_songs(filters=cli['filter'],
                                        filter_all=cli['all'])

    if cli['dry-run']:
        print_("Found {0} songs to delete".format(len(delete_songs)))

        if delete_songs:
            safe_print("\nSongs to delete:\n")

            for song in delete_songs:
                safe_print("{0} by {1}".format(song['title'], song['artist']))
        else:
            safe_print("\nNo songs to delete")
    else:
        if delete_songs:
            if cli['yes'] or raw_input(
                    "Are you sure you want to delete {0} songs from Google Music? (y/n) "
                    .format(len(delete_songs))).lower() == "y":
                print_("\nDeleting {0} songs from Google Music\n".format(
                    len(delete_songs)))

                songnum = 0
                total = len(delete_songs)
                pad = len(str(total))

                for song in delete_songs:
                    mcw.api.delete_songs(song['id'])
                    songnum += 1

                    print_(
                        "Deleted {num:>{pad}}/{total} songs from Google Music".
                        format(num=songnum, pad=pad, total=total),
                        end="\r")
                    sys.stdout.flush()

                print_()
            else:
                print_("No songs deleted.")
        else:
            safe_print("\nNo songs to delete")

    mcw.logout()
    print_("\nAll done!")
    def subscribe_to_playlists(self,
                               playlist_ids: list = [],
                               contains: list = []):
        new_subscriptions = False

        for playlist_id in playlist_ids:
            if playlist_id not in self.followed_playlists.keys():
                raise Exception(
                    "Cannot subscribe to playlist with id {}. Either the user owns it or does not follow it."
                    .format(playlist_id))

            if playlist_id not in self.subscribed_playlists.keys():
                playlist = self.followed_playlists[playlist_id]
                tracks = self._get_playlist_tracks(playlist["owner"]["id"],
                                                   playlist_id)
                self.subscribed_playlists[playlist_id] = SubscribedPlaylist(
                    playlist, tracks)
                new_subscriptions = True
                safe_print("Subscribed to playlist {} by {}".format(
                    playlist["name"], playlist["owner"]["id"]))

        # Lowercase pattern matching with the playlist name
        for pattern in contains:
            pattern = pattern.lower()
            for playlist in self.followed_playlists.values():
                if (pattern in playlist["name"].lower() and playlist["id"]
                        not in self.subscribed_playlists.keys()):
                    tracks = self._get_playlist_tracks(playlist["owner"]["id"],
                                                       playlist["id"])
                    self.subscribed_playlists[
                        playlist["id"]] = SubscribedPlaylist(playlist, tracks)
                    new_subscriptions = True
                    safe_print("Subscribed to playlist {} by {}".format(
                        playlist["name"], playlist["owner"]["id"]))

        # Only save if we actually changed something. TODO: Save these in a separate file.
        if new_subscriptions:
            self._save()
    def print_feed_log(self):
        if not os.path.exists(self._feed_log_path):
            print("No feed log exists yet!")
            return

        feed_log = pickle.load(open(self._feed_log_path, "rb"))
        num_tracks = len(feed_log["track_ids"])
        print("Found {} tracks in log.".format(num_tracks))

        batch_size = 50
        tracks = []
        start_idx = 0
        print("Requesting track info...")
        for start_idx in tqdm(range(0, num_tracks, batch_size)):
            end_idx = start_idx + batch_size
            track_ids = feed_log["track_ids"][start_idx:end_idx]
            tracks += self.sp.tracks(track_ids)["tracks"]
            start_idx = end_idx

        for track, timestamp in zip(tracks, feed_log["timestamps"]):
            safe_print("{} - {} - {} - {}".format(track["artists"][0]["name"],
                                                  track["name"], timestamp,
                                                  track["id"]))
Exemple #9
0
def messageWriter():
    time.sleep(random.random() * 10.0)
    output = []
    while 1:
        # Collect all available log messages.
        while 1:
            try:
                message = messageQ.get(False)
            except Queue.Empty:
                break
            output.append(message)
            messageQ.task_done()

        # Write all log messages in one call to minimize IO.
        if output:
            try:
                with open(logFileName, 'a') as fp:
                    fp.write(''.join(output))
                output = []
            except Exception as e:
                safe_print(e)

        # Sleep a random duration to minimize file write collisions between multiple RaceDB instances.
        time.sleep(60.0 + random.random() * 60.0 * 4.0)
def main():
	cli = dict((key.lstrip("-<").rstrip(">"), value) for key, value in docopt(__doc__).items())

	print_ = safe_print if not cli['quiet'] else lambda *args, **kwargs: None

	mcw = MobileClientWrapper()
	mcw.login(cli['user'], cli['pass'])

	delete_songs = mcw.get_google_songs(filters=cli['filter'], filter_all=cli['all'])

	if cli['dry-run']:
		print_("Found {0} songs to delete".format(len(delete_songs)))

		if delete_songs:
			safe_print("\nSongs to delete:\n")

			for song in delete_songs:
				safe_print("{0} by {1}".format(song['title'], song['artist']))
		else:
			safe_print("\nNo songs to delete")
	else:
		if delete_songs:
			if cli['yes'] or raw_input("Are you sure you want to delete {0} songs from Google Music? (y/n) ".format(len(delete_songs))).lower() == "y":
				print_("\nDeleting {0} songs from Google Music\n".format(len(delete_songs)))

				songnum = 0
				total = len(delete_songs)
				pad = len(str(total))

				for song in delete_songs:
					mcw.api.delete_songs(song['id'])
					songnum += 1

					print_("Deleted {num:>{pad}}/{total} songs from Google Music".format(num=songnum, pad=pad, total=total), end="\r")
					sys.stdout.flush()

				print_()
			else:
				print_("No songs deleted.")
		else:
			safe_print("\nNo songs to delete")

	mcw.logout()
	print_("\nAll done!")
Exemple #11
0
def main():
    cli = dict((key.lstrip("-<").rstrip(">"), value)
               for key, value in docopt(__doc__).items())

    print_ = safe_print if not cli['quiet'] else lambda *args, **kwargs: None

    if not cli['output']:
        cli['output'] = os.getcwd()

    mmw = MusicManagerWrapper(log=cli['log'])
    mmw.login(oauth_file=cli['cred'], uploader_id=cli['uploader-id'])

    download_songs = mmw.get_google_songs(filters=cli['filter'],
                                          filter_all=cli['all'])

    download_songs.sort(
        key=lambda song: (song['artist'], song['album'], song['track_number']))

    if cli['dry-run']:
        print_("Found {0} songs to download".format(len(download_songs)))

        if download_songs:
            safe_print("\nSongs to download:\n")
            for song in download_songs:
                safe_print("{0} by {1}".format(song['title'], song['artist']))
        else:
            safe_print("\nNo songs to download")
    else:
        if download_songs:
            print_("Downloading {0} songs from Google Music\n".format(
                len(download_songs)))
            mmw.download(download_songs, cli['output'])
        else:
            safe_print("\nNo songs to download")

    mmw.logout()
    print_("\nAll done!")
def main():
	cli = dict((key.lstrip("-<").rstrip(">"), value) for key, value in docopt(__doc__).items())

	print_ = safe_print if not cli['quiet'] else lambda *args, **kwargs: None

	if not cli['input']:
		cli['input'] = [os.getcwd()]

	mmw = MusicManagerWrapper(log=cli['log'])
	mmw.login(oauth_file=cli['cred'], uploader_id=cli['uploader-id'])

	excludes = "|".join(pattern.decode('utf8') for pattern in cli['exclude']) if cli['exclude'] else None

	upload_songs, exclude_songs = mmw.get_local_songs(cli['input'], exclude_patterns=excludes, filters=cli['filter'], filter_all=cli['all'])

	upload_songs.sort()
	exclude_songs.sort()

	if cli['dry-run']:
		print_("Found {0} songs to upload".format(len(upload_songs)))

		if upload_songs:
			safe_print("\nSongs to upload:\n")

			for song in upload_songs:
				safe_print(song)
		else:
			safe_print("\nNo songs to upload")

		if exclude_songs:
			safe_print("\nSongs to exclude:\n")

			for song in exclude_songs:
				safe_print(song)
		else:
			safe_print("\nNo songs to exclude")
	else:
		if upload_songs:
			print_("Uploading {0} songs to Google Music\n".format(len(upload_songs)))

			mmw.upload(upload_songs, enable_matching=cli['match'])
		else:
			safe_print("\nNo songs to upload")

	mmw.logout()
	print("\nAll done!")
def main():
    cli = dict((key.lstrip("-<").rstrip(">"), value)
               for key, value in docopt(__doc__).items())

    print_ = safe_print if not cli['quiet'] else lambda *args, **kwargs: None

    if not cli['input']:
        cli['input'] = [os.getcwd()]

    if not cli['output']:
        cli['output'] = os.getcwd()

    # Pre-compile regex for exclude option.
    excludes = "|".join(
        pattern.decode('utf8')
        for pattern in cli['exclude']) if cli['exclude'] else None

    mmw = MusicManagerWrapper(log=cli['log'], quiet=cli['quiet'])
    mmw.login(oauth_file=cli['cred'], uploader_id=cli['uploader-id'])

    if cli['down']:
        google_songs = mmw.get_google_songs(filters=cli['filter'],
                                            filter_all=cli['all'])

        cli['input'] = template_to_base_path(google_songs, cli['output'])

        local_songs, exclude_songs = mmw.get_local_songs(
            cli['input'], exclude_patterns=excludes)

        print_("Scanning for missing songs...")
        download_songs = compare_song_collections(google_songs, local_songs)

        download_songs.sort(key=lambda song: (song['artist'], song['album'],
                                              song['track_number']))

        if cli['dry-run']:
            print_("Found {0} songs to download".format(len(download_songs)))

            if download_songs:
                safe_print("\nSongs to download:\n")

                for song in download_songs:
                    safe_print("{0} by {1}".format(song['title'],
                                                   song['artist']))
            else:
                safe_print("\nNo songs to download")
        else:
            if download_songs:
                print_("Downloading {0} songs from Google Music\n".format(
                    len(download_songs)))
                mmw.download(download_songs, cli['output'])
            else:
                safe_print("\nNo songs to download")
    else:
        google_songs = mmw.get_google_songs()
        local_songs, exclude_songs = mmw.get_local_songs(
            cli['input'],
            exclude_patterns=excludes,
            filters=cli['filter'],
            filter_all=cli['all'])

        print_("Scanning for missing songs...")

        upload_songs = compare_song_collections(local_songs, google_songs)

        # Sort lists for sensible output.
        upload_songs.sort()
        exclude_songs.sort()

        if cli['dry-run']:
            print_("Found {0} songs to upload".format(len(upload_songs)))

            if upload_songs:
                safe_print("\nSongs to upload:\n")

                for song in upload_songs:
                    safe_print(song)
            else:
                safe_print("\nNo songs to upload")

            if exclude_songs:
                safe_print("\nSongs to exclude:\n")

                for song in exclude_songs:
                    safe_print(song)
            else:
                safe_print("\nNo songs to exclude")
        else:
            if upload_songs:
                print_("Uploading {0} songs to Google Music\n".format(
                    len(upload_songs)))

                mmw.upload(upload_songs, enable_matching=cli['match'])
            else:
                safe_print("\nNo songs to upload")

    mmw.logout()
    print_("\nAll done!")
def _eval_python(loop, context, params=None, add_boilerplate=False, namespace=None):
    """Convert the given loop to Python and emulate the loop directly in
    Python.

    @param loop (VBA_Object object) The loop for which to generate
    Python JIT code.

    @param context (Context object) The current program state.

    @param params (list) Any VB params used by the given loop.
    
    @param add_boilerplate (boolean) If True add setup boilerplate
    code (imports, etc.) to the start of the generated Python JIT
    code. Don't add boilerplate if False.

    @param namespace (dict) The Python namespace in which to evaluate
    the generated Python JIT code. If None the locals() namespace will
    be used.

    """
    params = params # pylint
    
    # Are we actually doing this?
    if (not context.do_jit):
        return False

    # Emulating full VB programs in Python is difficult, so for now skip loops
    # that Execute() dynamic VB.
    full_code_vba = safe_str_convert(loop).replace("\n", "\\n")
    code_vba = full_code_vba[:20]
    code_vba_lower = full_code_vba.lower()
    if (not context.throttle_logging):
        log.info("Starting JIT emulation of '" + code_vba + "...' ...")
    if (("Execute(".lower() in code_vba_lower) or
        ("ExecuteGlobal(".lower() in code_vba_lower) or
        ("Eval(".lower() in code_vba_lower)):
        log.warning("Loop Execute()s dynamic code. Not JIT emulating.")
        return False
    if (".Item(".lower() in code_vba_lower):
        log.warning("Loop references forms with .Item(). Not JIT emulating.")
        return False
            
    # Generate the Python code for the VB code and execute the generated Python code.
    # TODO: Remove dangerous functions from what can be exec'ed.
    code_python = ""
    try:

        # For JIT handling we modify the values of certain variables to
        # handle recursive python code generation, so make a copy of the
        # original context.
        tmp_context = Context(context=context, _locals=context.locals, copy_globals=True)
        
        # Get the Python code for the loop.
        if (not context.throttle_logging):
            log.info("Generating Python JIT code...")
        code_python = to_python(loop, tmp_context)
        if add_boilerplate:
            var_inits, _ = _loop_vars_to_python(loop, tmp_context, 0)
            func_defns = _called_funcs_to_python(loop, tmp_context, 0)
            code_python = _boilerplate_to_python(0) + "\n" + \
                          func_defns + "\n" + \
                          var_inits + "\n" + \
                          code_python + "\n" + \
                          _check_for_iocs(loop, tmp_context, 0) + "\n" + \
                          _updated_vars_to_python(loop, tmp_context, 0)
        if (log.getEffectiveLevel() == logging.DEBUG):
            safe_print("JIT CODE!!")
            safe_print(code_python)
            #print "REMOVE THIS!!!"
            #sys.exit(0)
        if (not context.throttle_logging):
            log.info("Done generating Python JIT code.")

        # Extended ASCII strings are handled differently in VBScript and VBA.
        # Punt if we are emulating VBA and we have what appears to be extended ASCII
        # strings. For performance we are not handling the MS VBA extended ASCII in the python
        # JIT code.
        if (not context.is_vbscript):
            
            # Look for non-ASCII strings.
            non_ascii_pat = r'"[^"]*[\x7f-\xff][^"]*"'
            non_ascii_pat1 = r'"[^"]*(?:\\x7f|\\x[89a-f][0-9a-f])[^"]*"'
            if ((re.search(non_ascii_pat1, code_python) is not None) or
                (re.search(non_ascii_pat, code_python) is not None)):
                log.warning("VBA code contains Microsoft specific extended ASCII strings. Not JIT emulating.")
                return False

        # Check for dynamic code execution in called functions.
        if (('"Execute", ' in code_python) or
            ('"ExecuteGlobal", ' in code_python) or
            ('"Eval", ' in code_python)):
            log.warning("Functions called by loop Execute() dynamic code. Not JIT emulating.")
            return False
        
        # Run the Python code.
        
        # Have we already run this exact loop?
        if (code_python in jit_cache):
            var_updates = jit_cache[code_python]
            if (not context.throttle_logging):
                log.info("Using cached JIT loop results.")
            if (var_updates == "ERROR"):
                log.error("Previous run of Python JIT loop emulation failed. Using fallback emulation for loop.")
                return False

        # No cached results. Run the loop.
        elif (namespace is None):

            # JIT code execution goes not involve emulating VB GOTOs.
            context.goto_executed = False
        
            # Magic. For some reason exec'ing in locals() makes the dynamically generated
            # code recognize functions defined in the dynamic code. I don't know why.
            if (not context.throttle_logging):
                log.info("Evaluating Python JIT code...")
            exec code_python in locals()
        else:

            # JIT code execution goes not involve emulating VB GOTOs.
            context.goto_executed = False

            # Run the JIT code in the given namespace.
            exec(code_python, namespace)
            var_updates = namespace["var_updates"]
        if (not context.throttle_logging):
            log.info("Done JIT emulation of '" + code_vba + "...' .")

        # Cache the loop results.
        jit_cache[code_python] = var_updates
        
        # Update the context with the variable values from the JIT code execution.
        try:
            for updated_var in var_updates.keys():
                if (updated_var == "__shell_code__"):
                    continue
                context.set(updated_var, var_updates[updated_var])
        except (NameError, UnboundLocalError):
            log.warning("No variables set by Python JIT code.")

        # Update shellcode bytes from the JIT emulation.
        import vba_context
        vba_context.shellcode = var_updates["__shell_code__"]

    except NotImplementedError as e:
        log.error("Python JIT emulation of loop failed. " + safe_str_convert(e) + ". Using fallback emulation method for loop...")
        #safe_print("REMOVE THIS!!")
        #raise e
        return False

    except Exception as e:

        # Cache the error.
        jit_cache[code_python] = "ERROR"
        
        # If we bombed out due to a potential infinite loop we
        # are done.
        if ("Infinite Loop" in safe_str_convert(e)):
            log.warning("Detected infinite loop. Terminating loop.")
            return True

        # We had some other error. Emulating the loop in Python failed.
        log.error("Python JIT emulation of loop failed. " + safe_str_convert(e) + ". Using fallback emulation method for loop...")
        if (log.getEffectiveLevel() == logging.DEBUG):
            traceback.print_exc(file=sys.stdout)
            safe_print("-*-*-*-*-\n" + code_python + "\n-*-*-*-*-")
        return False

    # Done.
    return True
def main():
	cli = dict((key.lstrip("-<").rstrip(">"), value) for key, value in docopt(__doc__).items())

	print_ = safe_print if not cli['quiet'] else lambda *args, **kwargs: None

	if not cli['input']:
		cli['input'] = [os.getcwd()]

	if not cli['output']:
		cli['output'] = os.getcwd()

	# Pre-compile regex for exclude option.
	excludes = "|".join(pattern.decode('utf8') for pattern in cli['exclude']) if cli['exclude'] else None

	mmw = MusicManagerWrapper(log=cli['log'], quiet=cli['quiet'])
	mmw.login(oauth_file=cli['cred'], uploader_id=cli['uploader-id'])

	if cli['down']:
		google_songs = mmw.get_google_songs(filters=cli['filter'], filter_all=cli['all'])

		cli['input'] = template_to_base_path(google_songs, cli['output'])

		local_songs, exclude_songs = mmw.get_local_songs(cli['input'], exclude_patterns=excludes)

		print_("Scanning for missing songs...")
		download_songs = compare_song_collections(google_songs, local_songs)

		download_songs.sort(key=lambda song: (song['artist'], song['album'], song['track_number']))

		if cli['dry-run']:
			print_("Found {0} songs to download".format(len(download_songs)))

			if download_songs:
				safe_print("\nSongs to download:\n")

				for song in download_songs:
					safe_print("{0} by {1}".format(song['title'], song['artist']))
			else:
				safe_print("\nNo songs to download")
		else:
			if download_songs:
				print_("Downloading {0} songs from Google Music\n".format(len(download_songs)))
				mmw.download(download_songs, cli['output'])
			else:
				safe_print("\nNo songs to download")
	else:
		google_songs = mmw.get_google_songs()
		local_songs, exclude_songs = mmw.get_local_songs(cli['input'], exclude_patterns=excludes, filters=cli['filter'], filter_all=cli['all'])

		print_("Scanning for missing songs...")

		upload_songs = compare_song_collections(local_songs, google_songs)

		# Sort lists for sensible output.
		upload_songs.sort()
		exclude_songs.sort()

		if cli['dry-run']:
			print_("Found {0} songs to upload".format(len(upload_songs)))

			if upload_songs:
				safe_print("\nSongs to upload:\n")

				for song in upload_songs:
					safe_print(song)
			else:
				safe_print("\nNo songs to upload")

			if exclude_songs:
				safe_print("\nSongs to exclude:\n")

				for song in exclude_songs:
					safe_print(song)
			else:
				safe_print("\nNo songs to exclude")
		else:
			if upload_songs:
				print_("Uploading {0} songs to Google Music\n".format(len(upload_songs)))

				mmw.upload(upload_songs, enable_matching=cli['match'])
			else:
				safe_print("\nNo songs to upload")

	mmw.logout()
	print_("\nAll done!")
Exemple #16
0
def main():
    cli = dict((key.lstrip("-<").rstrip(">"), value)
               for key, value in docopt(__doc__).items())

    print_ = safe_print if not cli['quiet'] else lambda *args, **kwargs: None

    if not cli['input']:
        cli['input'] = [os.getcwd()]

    mmw = MusicManagerWrapper(log=cli['log'])
    mmw.login(oauth_file=cli['cred'], uploader_id=cli['uploader-id'])

    excludes = "|".join(
        pattern.decode('utf8')
        for pattern in cli['exclude']) if cli['exclude'] else None

    upload_songs, exclude_songs = mmw.get_local_songs(
        cli['input'],
        exclude_patterns=excludes,
        filters=cli['filter'],
        filter_all=cli['all'])

    upload_songs.sort()
    exclude_songs.sort()

    if cli['dry-run']:
        print_("Found {0} songs to upload".format(len(upload_songs)))

        if upload_songs:
            safe_print("\nSongs to upload:\n")

            for song in upload_songs:
                safe_print(song)
        else:
            safe_print("\nNo songs to upload")

        if exclude_songs:
            safe_print("\nSongs to exclude:\n")

            for song in exclude_songs:
                safe_print(song)
        else:
            safe_print("\nNo songs to exclude")
    else:
        if upload_songs:
            print_("Uploading {0} songs to Google Music\n".format(
                len(upload_songs)))

            mmw.upload(upload_songs, enable_matching=cli['match'])
        else:
            safe_print("\nNo songs to upload")

    mmw.logout()
    print("\nAll done!")
    def print_playlists(self, own=False, follow=False, subscribed=True):
        if own:
            safe_print("Own playlists:")
            for playlist in self.user_playlists.values():
                safe_print(playlist["name"])

        if follow:
            safe_print("\nFollowed playlists:")
            for playlist in self.followed_playlists.values():
                safe_print(playlist["name"], playlist["owner"]["id"])

        if subscribed:
            safe_print("\nCurrently subscribed to the following playlists:")
            for playlist in self.subscribed_playlists.values():
                safe_print(playlist)

        safe_print()
    def update_feed(self, add_own=False):
        """
        Add_own denotes whether to add songs that the user added to a playlist themselves.
        This may happen for example in collaborative playlists.
        """

        last_update = self.subscription_feed.last_update

        track_ids = []
        num_added_tracks = 0

        for playlist_id, playlist in self.subscribed_playlists.items():
            # safe_print("Checking playlist {}".format(playlist.name))
            new_tracks, snapshot = self._get_playlist_tracks(
                playlist.owner_id,
                playlist_id,
                min_timestamp=last_update,
                compare_snapshot=playlist.snapshot_id,
                return_snapshot=True,
            )
            # Update the playlist snapshot so that we quickly know if it has changed next time
            playlist.snapshot_id = snapshot

            added = 0
            for track in new_tracks:
                if track.id == None:
                    print(f"Track with id None: {track}")
                if add_own or track.added_by != self.user_id:
                    try:
                        # Only add the track if it wasn't already in the list when we subbed
                        if track.id not in playlist.track_ids.keys():
                            track_ids.append(track.id)
                            # Add the ID to the track ID list so we know not to add it in the future
                            playlist.track_ids[track.id] = datetime.utcnow()
                            added += 1
                    # TODO: correctly upgrade objects if storage consists of SubscribedPlaylists without ID list.
                    except AttributeError:
                        track_ids.append(track.id)
                        added += 1

            if added > 0:
                safe_print("Obtained {} new tracks from playlist {}!".format(
                    added, playlist.name))

        if len(track_ids) > 0:
            unique_ids = np.unique(track_ids)

            # If a feed log exists, filter all track IDs that have already been added to the feed before.
            if os.path.exists(self._feed_log_path):
                feed_log = pickle.load(open(self._feed_log_path, "rb"))
                filtered_indices = np.where(
                    ~np.isin(unique_ids, feed_log["track_ids"]))
                unique_ids = unique_ids[filtered_indices]

            # We can add at most 100 tracks to a playlist in a single request.
            if unique_ids.size <= 100:
                self.sp.user_playlist_add_tracks(self.user_id,
                                                 self.subscription_feed.id,
                                                 unique_ids)
            else:
                # Split array into near-equal sections that are smaller than 100 tracks
                for id_array in np.array_split(
                        unique_ids,
                        np.ceil(unique_ids.size / 100).astype(int)):
                    self.sp.user_playlist_add_tracks(self.user_id,
                                                     self.subscription_feed.id,
                                                     id_array)

            num_added_tracks = unique_ids.size
            self._log_feed_updates(unique_ids)

        # Update the timestamp and save to file
        self.subscription_feed.last_update = datetime.utcnow()
        self._save()

        return num_added_tracks