def manage_playlist(): playlist_id = os.environ["PLAYLIST_ID"] refresh_token = os.environ["SPOTIFY_REFRESH_TOKEN"] scope = "playlist-modify-public" manager = SpotifyOAuth(scope=scope) manager.refresh_access_token(refresh_token) tracklist = [] sp = spotipy.Spotify(auth_manager=manager) with open("./_data/trash.yml") as file: trash_list = yaml.load(file, Loader=yaml.FullLoader) for title in trash_list: try: uri = title["spotify_uri"] tracklist.append(uri) api_res = sp.track(uri) print(api_res["name"]) except SpotifyException as error: print("Error with track: {}".format(title["songname"])) raise error results = sp.playlist_replace_items(playlist_id, tracklist)
class SpotifyAuthenticator: def __init__(self): self.scopes = 'user-read-private user-read-email playlist-read-private playlist-read-collaborative ' \ 'user-top-read user-library-read' self.sp_oauth = None self.sp = None self._init_oauth( "0.0.0.0" ) # init with stand in redirect_uri so update_token_info can be called def _init_oauth(self, redirect_uri): self.sp_oauth = SpotifyOAuth( scope=self.scopes, client_id=os.getenv("SPOTIFY_CLIENT_ID"), client_secret=os.getenv("SPOTIFY_CLIENT_SECRET"), redirect_uri=redirect_uri) return self.sp_oauth.get_authorize_url() def initialize_auth(self, request): redirect_uri = request.build_absolute_uri('/') + 'login' return HttpResponseRedirect(self._init_oauth(redirect_uri)) def get_initial_token_info(self, initial_token): return self.sp_oauth.get_access_token(initial_token) def update_token_info(self, token_info): if self.sp_oauth.is_token_expired(token_info): return self.sp_oauth.refresh_access_token( token_info["refresh_token"]) return token_info def connect_user(self, token_info): self.sp = Spotify(token_info['access_token']) return self.sp.current_user()
def get_token(session): token_valid = False token_info = session.get("token_info", {}) # Checking if the session already has a token stored if not (session.get('token_info', False)): token_valid = False return token_info, token_valid # Checking if token has expired now = int(time.time()) is_token_expired = session.get('token_info').get('expires_at') - now < 60 # Refreshing token if it has expired if (is_token_expired): # Don't reuse a SpotifyOAuth object because they store token info and you could leak user tokens if you reuse a SpotifyOAuth object sp_oauth = SpotifyOAuth(client_id=cid, client_secret=secret, redirect_uri=uri, cache_path=cache, scope=scope) token_info = sp_oauth.refresh_access_token( session.get('token_info').get('refresh_token')) token_valid = True return token_info, token_valid
def index(request): if not request.session.get('uuid'): request.session['uuid'] = str(uuid.uuid4()) auth_manager = SpotifyOAuth( scope='user-read-currently-playing', cache_path=session_cache_path(request.session), show_dialog=True) if request.method == 'GET': if request.GET.get("code"): # Step 3. Being redirected from Spotify auth page request.session['token_info'] = auth_manager.get_access_token( request.GET.get("code")) return redirect(index) if not auth_manager.get_cached_token(): auth_url = auth_manager.get_authorize_url() return HttpResponse(f'<h2><a href="{auth_url}">Sign in</a></h2>') if auth_manager.is_token_expired(request.session.get('token_info')): request.session['token_info'] = auth_manager.refresh_access_token( request.session.get('token_info')) spotify = Spotify(auth_manager=auth_manager) request.session['username'] = spotify.me()['id'] request.session['token'] = auth_manager.get_cached_token() return redirect(visview, request.session.get('username'))
def get_spotipy_objs(): # Returns two list array with ouath and sp object letters = string.ascii_lowercase random_string = ''.join(random.choice(letters) for i in range(10)) oauth = SpotifyOAuth(client_id="ca077b4c1b6b4ea7a33ed0069ec3eecb", client_secret="2d2baf7aa1ff4c9792822aefac0ef7e5", redirect_uri="https://favorable-mark-297715.uc.r.appspot.com/form/", state = random_string, scope="user-read-recently-played user-modify-playback-state user-read-private", cache_path=None) token_dict = oauth.get_cached_token() token = token_dict['access_token'] refresh_token = token_dict['refresh_token'] if oauth.is_token_expired(token_dict): oauth.refresh_access_token(refresh_token) sp = spotipy.Spotify(auth_manager=oauth) current_user_id = sp.current_user()['id'] return [oauth, sp]
def _renew_tokens(self): spotify_oauth = SpotifyOAuth(self._client_id, self._client_secret, redirect_uri=None, scope=RECENTLY_PLAYED_SCOPE) tokens = spotify_oauth.refresh_access_token(self._refresh_token) self._access_token = tokens['access_token'] self._refresh_token = tokens['refresh_token'] self._spotify = Spotify(auth=self._access_token) self._save_configuration() print('renewed tokens')
def authenticate(username): ''' authenticate with the spotify service ''' oauth_manager = SpotifyOAuth(username=username, scope=SCOPE) sp = spotipy.Spotify(oauth_manager=oauth_manager) user = sp.current_user() token_expired = oauth_manager.is_token_expired( oauth_manager.get_cached_token()) if token_expired: # refresh token logging.debug('Refreshing token for user {}'.format(user['id'])) oauth_manager.refresh_access_token( oauth_manager.get_cached_token()['refresh_token']) return oauth_manager, sp, user
def activate_integration(self): client_id = os.environ.get('SPOTIFY_KEY', '') client_secret = os.environ.get('SPOTIFY_SECRET', '') sp_oauth = SpotifyOAuth(client_id, client_secret, None) token_info = sp_oauth.refresh_access_token( self.integration.refresh_token) self.integration.access_token = token_info['access_token'] self.integration.refresh_token = token_info['refresh_token'] self.integration.save()
def refresh_spotify_tokens(user: User) -> dict: auth_manager = SpotifyOAuth( client_id=settings.SOCIAL_AUTH_SPOTIFY_KEY, client_secret=settings.SOCIAL_AUTH_SPOTIFY_SECRET, redirect_uri=settings.SPOTIFY_REDIRECT_URI, ) new_auth = auth_manager.refresh_access_token( _get_spotify_refresh_token(user)) spotify_social_auth = _get_spotify_social_auth(user) spotify_social_auth.extra_data = new_auth spotify_social_auth.save() return new_auth
def __init__(self, access_token=None, refresh_token=None): self._refreshed = False # case where an access_token is passed directly in (first # authentication, specific use cases maybe) if access_token: print("Authenticating Spotify with access token") self._spotify = spotipy.Spotify(auth=access_token) try: # try to ge the user's data with # the current access token -> # if the request fails, # fall back on the refresh token since # the access token might be expired result = self._spotify.current_user() except Exception as e: self._refreshed = True print('Access token failed. Reason: {}'.format(e)) print("Authenticating Spotify with refresh token") auth = SpotifyOAuth( client_id=os.environ.get("SPOTIFY_CLIENT_ID"), client_secret=os.environ.get("SPOTIFY_CLIENT_SECRET"), redirect_uri=os.environ.get("SPOTIFY_REDIRECT_URI")) tokens = auth.refresh_access_token(refresh_token) self._spotify = spotipy.Spotify(auth=tokens['access_token']) self._access_token = tokens['access_token'] self._refresh_token = tokens['refresh_token'] elif refresh_token: print("Authenticating Spotify with refresh token") auth = SpotifyOAuth( client_id=os.environ.get("SPOTIFY_CLIENT_ID"), client_secret=os.environ.get("SPOTIFY_CLIENT_SECRET"), redirect_uri=os.environ.get("SPOTIFY_REDIRECT_URI")) tokens = auth.refresh_access_token(refresh_token) self._spotify = spotipy.Spotify(auth=tokens['access_token']) self._access_token = tokens['access_token'] self._refresh_token = tokens['refresh_token']
class Spotify(): def __init__(self, client_id, client_secret, redirect_uri, username, scope): self.oauth = SpotifyOAuth(client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri, username=username, scope=scope) self.play_state = False with open('./secrets.json', 'r') as f: self.secrets = json.load(f) access_token = self.refresh_access_token() self.app = spotipy.Spotify(auth=access_token) def refresh_access_token(self): new_tokens = self.oauth.refresh_access_token( self.secrets['refresh_token']) if self.secrets['refresh_token'] != new_tokens['refresh_token']: with open('./secrets.json', 'w') as f: self.secrets['refresh_token'] = new_tokens['refresh_token'] json.dump(self.secrets, f) return new_tokens['access_token'] def find_device_id(self, device_name): devices = self.app.devices() target_device_id = None for dev in devices['devices']: if dev['name'].startswith(device_name): target_device_id = dev['id'] break return target_device_id def next_track(self, device_id): self.app.next_track(device_id=device_id) def prev_track(self, device_id): self.app.previous_track(device_id=device_id) def toggle_start_pause(self, device_id): if not self.play_state: self.app.start_playback(device_id=device_id) else: self.app.pause_playback(device_id=device_id) self.play_state = not self.play_state
def get_fresh_token() -> Optional[str]: if current_user is None: return None token_data = current_user.token_data if token_data["expires_at"] - int(time()) < 60: spotify_oauth = SpotifyOAuth( client_id=current_app.config["SPOTIFY_CLIENT_ID"], client_secret=current_app.config["SPOTIFY_CLIENT_SECRET"], redirect_uri=url_for("spotify.authorized", external=True), scope=",".join(SPOTIFY_SCOPES), ) token_data = spotify_oauth.refresh_access_token( token_data["refresh_token"]) current_user.token_data = token_data db.session.commit() return token_data["access_token"]
class CustomAuthManager: """ Code inspired from https://github.com/plamere/spotipy/issues/555#issuecomment-675099233 """ def __init__(self, scope, keys): self.auth = SpotifyOAuth(scope=scope, client_id=keys['client_id'], client_secret=keys['client_secret'], redirect_uri=keys['redirect_uri'], username=keys['username']) self.refresh_token = keys['refresh_token'] self.current_token = None def get_access_token(self): now = datetime.datetime.now() # if no token or token about to expire soon if not self.current_token or self.current_token['expires_at'] < now.timestamp() + 60: self.current_token = self.auth.refresh_access_token(self.refresh_token) return self.current_token['access_token']
def __init__(self, access_token=None, refresh_token=None): self._scope = '''ugc-image-upload user-top-read user-email-read playlist-modify-public playlist-read-collaborative ''' # case where an access_token is passed directly in (first # authentication, specific use cases maybe) if access_token: print("Authenticating Spotify with access token") self._spotify = spotipy.Spotify(auth=access_token) self._access_token = access_token self._refresh_token = None # typical use - refresh_token taken from a database and used to create # a new access_token elif refresh_token: print("Authenticating Spotify with refresh token") auth = SpotifyOAuth( client_id=os.environ.get("SPOTIFY_CLIENT_ID"), client_secret=os.environ.get("SPOTIFY_CLIENT_SECRET"), ) tokens = auth.refresh_access_token(refresh_token) self._spotify = spotipy.Spotify(auth=tokens['access_token']) self._access_token = tokens['access_token'] self._refresh_token = tokens['refresh_token'] # local and development environments # wont work headless server-side as it requires a user to log in else: print("Authenticating with local auth-flow") self._spotify = spotipy.Spotify(auth_manager=SpotifyOAuth( client_id=os.environ.get("SPOTIFY_CLIENT_ID"), client_secret=os.environ.get("SPOTIFY_CLIENT_SECRET"), scope=self._scope, redirect_uri='http://localhost:8080')) self._access_token = None self._refresh_token = None
def get_token(): """ returns a valid Spotify authentication token, refreshes the token if it is expired """ load_dotenv() # Get clientID and clientSecret from .env file client_id = os.getenv("CLIENT_ID") client_secret = os.getenv("CLIENT_SECRET") user_id = os.getenv("SPOTIFY_USERNAME") scope = 'user-read-private user-read-playback-state user-modify-playback-state user-top-read user-library-read playlist-modify-public playlist-modify-private' auth = SpotifyOAuth(client_id, client_secret, 'http://localhost:8050/callback', cache_path=f".cache-{user_id}", scope=scope) if auth._is_token_expired(auth.get_cached_token()): return auth.refresh_access_token( auth.get_cached_token()['refresh_token'])['access_token'] return auth.get_cached_token()['access_token']
# Fetch access token and cache it for reuse scope = 'playlist-read-collaborative playlist-read-private user-library-read' auth_manager = SpotifyOAuth(client_id=os.environ['SPOTIFY_CLIENT_ID'], client_secret=os.environ['SPOTIFY_CLIENT_SECRET'], redirect_uri=os.environ['SPOTIFY_REDIRECT_URI'], username=os.environ['SPOTIFY_USERNAME'], scope=scope) try: token_info = None # Refresh access token if we've saved it previously if os.path.exists('token-info.json'): with open('token-info.json', 'r') as f: old_token_info = json.load(f) token_info = auth_manager.refresh_access_token( old_token_info['refresh_token']) else: token_info = auth_manager.get_access_token() with open('token-info.json', 'w') as f: json.dump(token_info, f) # Login and attempt to fetch current user spotify = spotipy.Spotify(token_info['access_token']) spotify_user = spotify.current_user() except SpotifyException as e: logger.error( "[Spotify] Could not retrieve auth token or current user info.") logger.error(e) exit(1)
class SpotifyAuthManager(): """ A class used to handle Spotify Oauth. Refreshable user authentication. Owned by Playlist & ArtistManager. Args: Instance Attributes Class Attributes: """ username = os.environ['SPOTIFY_USERNAME'] client_id = os.environ['SPOTIFY_CLIENT_ID'] client_secret = os.environ['SPOTIFY_CLIENT_SECRET'] scope = os.environ['SPOTIFY_SCOPE'] redirect_uri = os.environ['SPOTIFY_REDIRECT_URI'] def __init__(self, session=None): self.token_info = None self.response_code = None self.client_mgr = None self.session = session # use same session # self.session = session def create_client_mgr(self): """ Create SpotifyOauth object. Args: client_id client_secret redirect_uri scope cache_path """ cache_path = WindowsPath("__pycache__") / fr'.cache-{self.username}' self.client_mgr = SpotifyOAuth(self.client_id, self.client_secret, self.redirect_uri, scope=self.scope, cache_path=cache_path) return self def get_auth_token(self): """ Get oauth token from cache or prompt for new token. """ try: self.token_info = self.client_mgr.get_cached_token() logger.info( f"Token expires at {time.strftime('%c', time.localtime(self.token_info['expires_at']))}" ) return self # TODO: add specific exceptions except Exception: logger.info("No token in cache. Getting new token.", exc_info=True) auth_url = self.client_mgr.get_authorize_url() user_auth = self.session.get(auth_url).url response_code = input(f'Use Browser to authenticate: {user_auth}') code = self.client_mgr.parse_response_code(response_code) self.token_info = self.client_mgr.get_access_token(code) with open(self.client_mgr.cache_path, 'w') as f: f.write(json.dumps(self.token_info)) def refresh_auth_token(self): """ Refresh authentication token. Same spotify obect used throughout. How to call from owning classes. """ self.client_mgr.refresh_access_token(self.token_info['refresh_token']) logger.info( f"Token refreshed, expires at: {time.strftime('%c', time.localtime(self.token_info['expires_at']))}" ) def create_spotify(self): """ Create Spotify object for Authorization Code flow. Args: token, session, client_mgr """ try: auth_info = { 'auth': self.token_info['access_token'], 'requests_session': self.session, 'client_credentials_manager': self.client_mgr } return catch(spotipy.Spotify, auth_info) except TypeError: # Why TypeError? logger.error("Token error.", exc_info=True)
def refresh_spotify(refresh_token: str, spotify_oauth: SpotifyOAuth): auth_creds = spotify_oauth.refresh_access_token(refresh_token) return auth_creds
def fetch(user_id): user = User.objects.get(pk=user_id) # get integration integration = user.integration_set.get(identifier='spotify') # refresh token client_id = os.environ.get('SPOTIFY_KEY', '') client_secret = os.environ.get('SPOTIFY_SECRET', '') sp_oauth = SpotifyOAuth(client_id, client_secret, None) token_info = sp_oauth.refresh_access_token(integration.refresh_token) integration.access_token = token_info['access_token'] integration.refresh_token = token_info['refresh_token'] integration.save() # load all artists artists = [] all_artists_loaded = False limit = 50 token = integration.access_token url = f"https://api.spotify.com/v1/me/following?type=artist&limit={limit}&access_token={token}" while not all_artists_loaded: response = requests.get(url).json()['artists'] current_request_artists = response['items'] artists += current_request_artists if response['next']: url = response['next'] + f"&access_token={token}" else: all_artists_loaded = True # save or update loaded artists for artist in artists: find_by = { "integration": integration, "integration_artist_id": artist["id"] } update = {"name": artist["name"]} if Artist.objects.filter(**find_by).exists(): Artist.objects.filter(**find_by).update(**update) else: Artist.objects.create(**update, **find_by) artists = integration.artist_set.all() for artist in artists: # load releases releases = [] all_releases_loaded = False limit = 50 artist_id = artist.integration_artist_id url = f"https://api.spotify.com/v1/artists/{artist_id}/albums?limit={limit}&access_token={token}" while not all_releases_loaded: response = requests.get(url).json() current_request_releases = response['items'] releases += current_request_releases if response['next']: url = response['next'] + f"&access_token={token}" else: all_releases_loaded = True time.sleep(1) # save or update releases for release in releases: find_by = { "artist": artist, "integration_release_id": release["id"] } release_date = release["release_date"] if release['release_date_precision'] == 'year': release_date += '-01-01' elif release['release_date_precision'] == 'month': release_date += '-01' update = { "title": release["name"], "cover_url": max(release['images'], key=lambda image: image['width'])['url'], "date": release_date, "release_type": release["album_type"], } if Release.objects.filter(**find_by).exists(): Release.objects.filter(**find_by).update(**update) else: Release.objects.create(**update, **find_by)
from jinja2 import Environment, FileSystemLoader from pytz import utc from spotipy import Spotify from spotipy.oauth2 import SpotifyOAuth with open('.authorization_code', 'r') as file: authorization_code = file.read() scope = 'user-read-currently-playing,user-read-recently-played,user-read-playback-state' sp_oauth = SpotifyOAuth(cache_path=environ['SPOTIPY_CACHE_PATH'], scope=scope) try: tokens = sp_oauth.get_access_token(code=authorization_code, as_dict=True) except Exception as e: tokens = sp_oauth.refresh_access_token( sp_oauth.get_cached_token()['refresh_token']) expires_at = tokens['expires_at'] artists_names = [] artists_urls = [] if expires_at > int(datetime.now().strftime('%s')): sp = Spotify(auth_manager=sp_oauth) last_updated = datetime.now(tz=utc).strftime('%Y-%m-%d %H:%M:%S %Z') currently_playing = sp.current_user_playing_track() if currently_playing: song_name = currently_playing['item']['name'] song_url = currently_playing['item']['external_urls']['spotify'] album_name = currently_playing['item']['album']['external_urls'][ 'spotify'] album_image_url = currently_playing['item']['album']['images'][0][
class Muzik: def __init__(self, public_api=False): self.__create_cache_dir() self.ids = self.__read_cached_ids() if public_api: self.__sp = self.__connect_spotify() self.__sp_user = self.__connect_spotify_user() self.__user_id = self.__sp_user.me()["id"] def __create_cache_dir(self): """ Create cache dir at `CACHE_DIR` if doesn't already exists """ if not os.path.isdir(CACHE_DIR): os.mkdir(CACHE_DIR) def __read_cached_ids(self) -> pd.Series: """ Read the cached already fetched ids from the cache folder either returns the cached pd.Series, or empty series if no file there """ path = CACHE_DIR + ACH_IDS if os.path.exists(path): print(f"Reading data from cache file {path}") df = pd.read_pickle(path) else: df = pd.Series() print(f"Local library contains {len(df)} songs") return df def __read_credentials(self): """ Opens and return the content of `CRED_PATH_SPOTIFY` as a python dict """ with open(CRED_PATH_SPOTIFY, 'r') as handle: data = json.load(handle) return data def __connect_spotify_user(self): """ Connect to the API using the spotify user credentials needs more informations than the other method, but can access to personnal informations (including playlists :o) of the user """ data = self.__read_credentials() # generate a unique random number to prevent csrf state = hashlib.sha256(os.urandom(1024)).hexdigest() self.__user_credentials = SpotifyOAuth( **data, state=state, ) return self.__get_spotify_user( self.__user_credentials.get_access_token(as_dict=(False))) def __get_spotify_user(self, token): """ Returns a Spotify client authentified with the provided token """ return spotipy.Spotify(auth=token) def __connect_spotify(self): """ Connect to the public API of Spotify, useful to fetch songs ids since the API limite rate is higher here, however not really useful to create playlists and stuff """ data = self.__read_credentials() auth = {} auth["client_id"] = data["client_id"] auth["client_secret"] = data["client_secret"] return spotipy.Spotify(auth_manager=SpotifyClientCredentials(**auth)) def __refresh_token(self): """ Refreshes the tokenn if it has expired or not and updates the sp_user Spotify Interface with the new token """ cached = self.__user_credentials.get_cached_token() refreshed = self.__user_credentials.refresh_access_token( cached["refresh_token"]) self.__sp_user = self.__get_spotify_user(refreshed["access_token"]) def __update_token(self): """ Updates the token if it has expired !! may not work flawlessely (probably not in fact), hard to test since the token lasts for 1 hour haha """ # built-in function, does not work always good cached = self.__user_credentials.get_cached_token() if self.__user_credentials.is_token_expired(cached): print("Token expired, refreshing now") self.__refresh_token() # handmade function just in case the one above fails try: _ = self.__sp_user.me() except SpotifyException as e: if e.http_status == UNAUTHORIZED_ST_CODE: print("Token expired, refreshing now") self.__refresh_token() def __search_strings(self, row): """ Creates the search string for the Spotify API based on the information of the row Returns a list of multiple strings, to take into account if there is special characters (like "'") or multiple markets input: - row : pd.Series with genre, artists, songs,... output: - searches : list of tuples (search string, market) """ search = "" # artists artists = list(map(str.strip, row.artist.split(","))) if len(artists) > 1: sep = '" AND "' search += f"artist:\"{sep.join(artists)}\"" else: search += f"artist:\"{artists[0]}\"" # album if row.album != "N/A": search += f" album:\"{row.album}\"" # track name search += f" track:\"{row.song}\"" # dealing with "'"" # sometimes it will work with the "'" and sometimes not if "'" in search: searches_s = [search, search.replace("'", "")] else: searches_s = [search] searches = [] for market in MARKETS: for search in searches_s: searches.append((search, market)) return searches def __fetch_id(self, df): """ Fetches the Spotify songs id for each provided songs If it cannot find ids for a song, it will be set to None input: - df : a pd.DataFrame with a random index and the song specific columns (genre, artist, ...) """ # small hack to access the data from the index & the columns indexs = pd.MultiIndex.from_frame(df) songs = pd.DataFrame(data=df.values, index=indexs, columns=df.columns) ids = pd.Series(index=indexs, dtype=str, name="ids") bad_formats = [] # chosing the endpoint # by default try to take the public one, if doesn't exists # (public_api = False), use the private one try: endpoint = self.__sp except AttributeError: endpoint = self.__sp_user # format string padding used for the debug output str_format = int(math.log(len(songs), 10)) + 1 for idx, (_, content) in enumerate(songs.iterrows()): searches = self.__search_strings(content) bad_format = [] for search, market in searches: try: res = endpoint.search(search, market=market) track = res['tracks']['items'][0] except IndexError: bad_format.append((search, market)) else: # succeed to fetch an id break else: # did not managed to find an id with all the search strings # provided, set the id of the song to None bad_formats.append(bad_format) ids.iloc[idx] = None print(f"{Color.FAIL}" f"{idx + 1:<{str_format}}/{len(df)}" f"{Color.ENDC}" f" : {search} not in Spotify") continue album = track['album']['name'] name = track['name'] artist = track['artists'][0]['name'] id = track['id'] ids.iloc[idx] = id print(f"{Color.OKGREEN}" f"{idx + 1:<{str_format}}/{len(df)}" f"{Color.ENDC}" f" : {id} {name} {artist} {album}") return ids def __update_missing_list(self): """ Create a csv file containing every tracks that were not available on spotify """ missing = self.ids[self.ids.isnull()] missing.index.to_frame(index=False).to_csv(CACHE_DIR + MISSING_IDS) def __create_user_playlist(self): """ Creates a new playlist using PLAYLIST_NAME, PLAYLIST_DESC also pushes playlist cover PLAYLIST_COVER return: - playlist_id : string containing the id of the playlist """ # create the playlist with name, description, visibility print(f"Creating {PLAYLIST_NAME}...") ret = self.__sp_user.user_playlist_create(user=self.__user_id, name=PLAYLIST_NAME, public=True, description=PLAYLIST_DESC) playlist_id = ret["id"] # most important, upload the playlist image print(f"Uploading playlist cover from {PLAYLIST_COVER}") with open(PLAYLIST_COVER, "rb") as image_file: cover = base64.b64encode(image_file.read()) ret = self.__sp_user.playlist_upload_cover_image(playlist_id, cover) return playlist_id def __get_playlist_id(self): """ Returns the playlist id of PLAYLIST_NAME if the playlist doesn't exists yet, it will create it and return the id of the newly created playlist """ # check if the playlist already exists user_playlists = self.__sp_user.user_playlists(self.__user_id) playlist_id = None if len(user_playlists["items"]) > 0: for user_pl in user_playlists["items"]: if user_pl["name"] == PLAYLIST_NAME: playlist_id = user_pl["id"] break # at this point, if the playlist exists, the id is stored in # playlist_id, otherwise we have still a None value if playlist_id is None: print(f"Playlist {PLAYLIST_NAME} doesn't exists yet") playlist_id = self.__create_user_playlist() print(f"Using playlist {PLAYLIST_NAME} : {playlist_id}") return playlist_id def update(self, ach): """ updates the known list of ids with the newer version of the ach musik sheet input: - ach : raw sheet from google with multiindex """ self.__update_token() # turn the index to DataFrame objects new_songs = ach.index.to_frame().reset_index(drop=True) if self.ids.empty: # in case the cached list was empty, simply fetch the whole # list self.ids = self.__fetch_id(new_songs) else: old_songs = self.ids.index.to_frame().reset_index(drop=True) # get the list of the common values common_songs = new_songs.merge(old_songs, how='inner') # remove the songs that are not anymore in the cached df depr = pd.concat([common_songs, old_songs]).drop_duplicates(keep=False) to_remove = pd.MultiIndex.from_frame(depr) if len(to_remove) > 0: self.ids = self.ids.drop(to_remove) # adds the new songs from the ach sheet news = pd.concat([common_songs, new_songs]).drop_duplicates(keep=False) if len(news) > 0: new_ids = self.__fetch_id(news) self.ids = pd.concat([self.ids, new_ids]) else: print("Local list already updated") # save updated list in cache self.ids.to_pickle(CACHE_DIR + ACH_IDS) # also updates missing ID list self.__update_missing_list() def create_playlist(self, playlist): """ Create (or replace) a playlist containing all the songs provided in the playlists DataFrame input: - playlist : pd.DataFrame indexed by a MultiIndex with genre, artist, song, ... """ self.__update_token() # get the playlist id of PLAYLIST_NAME playlist_id = self.__get_playlist_id() # get the tracks tracks_all = self.ids[playlist.index] tracks_results = tracks_all.isnull().value_counts() print(f"Adding {tracks_results[False]} tracks," f" Missing {tracks_results[True]} tracks") tracks_id = tracks_all.dropna().values print(f"Inserting {len(tracks_id)} songs in the playlist...") # spotify api "only" handles 100 tracks by requests # so here we split the data batch_size = int(len(tracks_id) / MAX_TRACK_PER_REQUESTS) + 1 batches = np.array_split(tracks_id, batch_size) str_format = int(math.log(len(batches), 10)) + 1 print(f"{0:<{str_format}}/{len(batches)} batch inserting...") # the first call `replace_tracks` clear the playlist AND # adds the supplied tracks self.__sp_user.user_playlist_replace_tracks(self.__user_id, playlist_id=playlist_id, tracks=batches[0]) if len(batches) > 1: for idx, batch in enumerate(batches[1:]): print(f"{idx+2:<{str_format}}/{len(batches)}" " batch inserting...") # add the rest of the tracks self.__sp_user.user_playlist_add_tracks( self.__user_id, playlist_id=playlist_id, tracks=batch) print("Playlist done") def get_playlists(self): self.__update_token() return self.__sp_user.user_playlists(self.__user_id)
def fetch(user_id): user = User.objects.get(pk=user_id) # get integration integration = user.integration_set.get(identifier='spotify') # refresh token client_id = os.environ.get('SPOTIFY_KEY', '') client_secret = os.environ.get('SPOTIFY_SECRET', '') sp_oauth = SpotifyOAuth(client_id, client_secret, None) token_info = sp_oauth.refresh_access_token(integration.refresh_token) integration.access_token = token_info['access_token'] integration.refresh_token = token_info['refresh_token'] integration.save() # load all artists artists = [] all_artists_loaded = False limit = 50 token = integration.access_token url = f"https://api.spotify.com/v1/me/following?type=artist&limit={limit}&access_token={token}" while not all_artists_loaded: response = requests.get(url).json()['artists'] current_request_artists = response['items'] artists += current_request_artists if response['next']: url = response['next'] + f"&access_token={token}" else: all_artists_loaded = True # save or update loaded artists for artist in artists: # find_by = {"integration": integration, "integration_artist_id": artist["id"]} # update = {"name": artist["name"]} cursor = connection.cursor() cursor.callfunc('artists.create_or_update_artist', cx_Oracle.STRING, [integration.id, artist["id"], artist["name"]]) # if Artist.objects.filter(**find_by).exists(): # Artist.objects.filter(**find_by).update(**update) # else: # Artist.objects.create(**update, **find_by) artists = integration.artist_set.all() for artist in artists: # load releases releases = [] all_releases_loaded = False limit = 50 artist_id = artist.integration_artist_id url = f"https://api.spotify.com/v1/artists/{artist_id}/albums?limit={limit}&access_token={token}" while not all_releases_loaded: response = requests.get(url).json() current_request_releases = response['items'] releases += current_request_releases if response['next']: url = response['next'] + f"&access_token={token}" else: all_releases_loaded = True time.sleep(1) # save or update releases for release in releases: # find_by = {"artist": artist, "integration_release_id": release["id"]} release_date = release["release_date"] if release_date == '0000': release_date = str(datetime.date.today().year) if release['release_date_precision'] == 'year': release_date += '-01-01' elif release['release_date_precision'] == 'month': release_date += '-01' cover_url = release['images'] if len(cover_url) > 0: cover_url = max(release['images'], key=lambda image: image['width'])['url'] else: cover_url = '' # update = { # "title": release["name"], # "cover_url": cover_url, # "date": release_date, # "release_type": release["album_type"], # } cursor = connection.cursor() cursor.callfunc( 'releases.create_or_update_release', cx_Oracle.STRING, [ artist.id, release["id"], release["name"], cover_url, release_date, release["album_type"] ])
# このファイルと同じディレクトリ内にsecrets.jsonを作成して以下を書き込む # {"refresh_token": <取得したrefresh_token>} # それ以降は書き込んだrefresh_tokenを使ってaccess_tokenを取得することができる """ with open('./secrets.json', 'r') as f: secrets = json.load(f) refresh_token = secrets['refresh_token'] oauth = SpotifyOAuth(client_id=os.environ['SPOTIPY_CLIENT_ID'], client_secret=os.environ['SPOTIPY_CLIENT_SECRET'], redirect_uri=os.environ['SPOTIPY_REDIRECT_URI'], username=username, scope=scope) new_token = oauth.refresh_access_token(refresh_token) sp = spotipy.Spotify(auth=new_token['access_token']) with open('./secrets.json', 'w') as f: secrets['refresh_token'] = new_token['refresh_token'] json.dump(secrets, f) # https://github.com/plamere/spotipy/issues/211#issuecomment-369863112 # 古いVersionのSpotipyだとdevices()がundefinedになるため、上記の方法でUpgradeする devices = sp.devices() target_device_id = None for dev in devices['devices']: if dev['name'].startswith('raspotify'): target_device_id = dev['id'] if target_device_id == None: raise Exception('target device not found')