spotify_actions/examples/label_playlist.py

105 lines
4.0 KiB
Python
Raw Normal View History

# pylint: disable=missing-module-docstring, missing-function-docstring
from argparse import ArgumentParser
from datetime import date, datetime, timedelta
2021-09-10 22:10:59 -04:00
from typing import Iterable
from spotipy import Spotify
from spotify_actions.album import (
album_filter_label,
album_filter_release,
album_from_ids,
album_sort_release,
album_to_tracks,
)
from spotify_actions.combinator import combinator_join
from spotify_actions.playlist import (
playlist_current_user_all,
playlist_current_user_assure,
playlist_replace,
playlist_tracks,
)
from spotify_actions.search import Query, search_albums
from spotify_actions.track import track_unique_albums
from spotify_actions.util import read_credentials_oauth
def label_playlist(client: Spotify, label_name: str, playlist_id: str) -> None:
# Given a label name, replace all songs in the provided `playlist_id` with the
# label's songs, ordered by descending release date.
albums_search = search_albums(client, Query(label=label_name))
albums_unfiltered = album_from_ids(client, albums_search)
albums_unsorted = album_filter_label(albums_unfiltered, label_name)
albums = album_sort_release(albums_unsorted, descending=True)
tracks = album_to_tracks(client, albums)
playlist_replace(client, playlist_id, tracks)
2021-09-10 22:10:59 -04:00
def label_recent(client: Spotify, label_playlist_ids: Iterable[str], playlist_id: str, released_after: date) -> None:
2021-09-10 22:10:59 -04:00
album_iterables = []
for label_playlist_id in label_playlist_ids:
# Get all albums in a playlist released after the provided date
tracks = playlist_tracks(client, [label_playlist_id])
album_ids = track_unique_albums(tracks)
albums = album_from_ids(client, album_ids)
2021-09-10 22:10:59 -04:00
# Because the playlists were created in descending release date order,
# `is_sorted=True` is enabled to reduce the number of API queries needed
album_iterables.append(album_filter_release(albums, released_after, is_sorted=True))
# Merge all the albums from each label playlist
recent_albums = combinator_join(*album_iterables)
recent_tracks = album_to_tracks(client, recent_albums)
# Create the recent releases playlist
2021-09-10 22:10:59 -04:00
playlist_replace(client, playlist_id, recent_tracks)
def main() -> None:
2021-09-10 22:10:59 -04:00
# Intentionally 6 days - if running on a Friday, we don't want to include last Friday's releases
one_week_ago = (datetime.now().date() - timedelta(days=6)).strftime("%Y-%m-%d")
parser = ArgumentParser()
parser.add_argument("-c", "--credentials", required=True)
parser.add_argument("-r", "--redirect-uri", required=True)
2021-09-10 22:10:59 -04:00
parser.add_argument("--recent-release", help='Name of the "recent releases" playlist constructed from all labels.')
parser.add_argument(
2021-09-10 22:10:59 -04:00
"--released-after", help="YYYY-MM-DD date that albums must be released after", default=one_week_ago
)
parser.add_argument("label", nargs="+")
cmdline = parser.parse_args()
client = read_credentials_oauth(
cmdline.credentials,
redirect_uri=cmdline.redirect_uri,
scopes=["playlist-read-private", "playlist-modify-private", "playlist-modify-public"],
)
# Get all user playlists; we'll be iterating over this a couple times
user_playlists = list(playlist_current_user_all(client))
# To simplify, this assumes that the label playlist name is unique for this user
def _locate_playlist(name: str) -> str:
assured = playlist_current_user_assure(client, user_playlists, name)
# The `str()` wrapper is technically unnecessary, but keeps mypy happy
return str(list(assured)[0].spotify_id)
2021-09-10 22:10:59 -04:00
label_ids = {name: _locate_playlist(name) for name in cmdline.label}
for label_name, playlist_id in label_ids.items():
label_playlist(client, label_name, playlist_id)
2021-09-10 22:10:59 -04:00
if cmdline.recent_release:
recent_release = _locate_playlist(cmdline.recent_release)
released_after = datetime.strptime(cmdline.released_after, "%Y-%m-%d").date()
label_recent(client, label_ids.values(), recent_release, released_after)
if __name__ == "__main__":
main()