def get_playlist_items(self, playlist_id):
     """
     Get full details of the tracks or episodes of a playlist owned by a Spotify user
     """
     print(f'\nFetching items from selected Spotify playlist...', end=" ")
     # build and execute request
     request_url = f"https://api.spotify.com/v1/playlists/{playlist_id}/tracks"
     headers = {
         "Content-Type": "application/json",
         "Authorization": f"Bearer {self.creds.access_token()}"
     }
     response = requests.get(request_url, headers=headers)
     if response.status_code != 200:
         raise RequestError(response.status_code, response.text)
     # handle responses that exceed item limit and require pagination
     response = response.json()
     nextURL = response['next']
     while nextURL is not None:
         nextResponse = requests.get(nextURL, headers=headers)
         if nextResponse.status_code != 200:
             raise RequestError(nextResponse.status_code, nextResponse.text)
         response['items'].extend(nextResponse.json()['items'])
         nextURL = nextResponse.json()['next']
     print(Fore.GREEN + 'Success.\n' + Style.RESET_ALL)
     return response['items']
 def add_songs_to_playlist(self, songs, playlist_id):
     """
     Add song(s) from list by URI to playlist by playlist_id/
     Per spotify API limitation, can only add up to 100 at at time
     """
     print(f"Adding selected songs to playlist...", end=" ")
     # split list of songs into URI lists of 100 each max
     URI_lists = list(self._partition_URI_list(songs, 100))
     for URI_list in URI_lists:
         # set body payload as comma separated list
         payload = {'uris': URI_list}
         headers = {
             "Content-Type": "application/json",
             "Authorization": f"Bearer {self.creds.access_token()}"
         }
         request_url = f"https://api.spotify.com/v1/playlists/{playlist_id}/tracks"
         response = requests.post(request_url,
                                  json=payload,
                                  headers=headers)
         if response.status_code not in [200, 201]:
             print(Back.RED + Fore.WHITE + "FAILED" + Style.RESET_ALL)
             raise RequestError(response.status_code, response.text)
     print(Fore.GREEN + 'Success.' + Style.RESET_ALL)
     # return the newly created playlist URL
     return f"https://open.spotify.com/playlist/{playlist_id}"
 def add_videos_to_playlist(self, video_list, playlist_id):
     """
     Add videos iteratively to playlist by its id
     Return URL to playlist
     """
     try:
         for video in video_list:
             request = self.client.playlistItems().insert(
                 part="snippet",
                 body={
                 "snippet": {
                     "playlistId": playlist_id,
                     "resourceId": {
                         "kind": video['id']['kind'],
                         "videoId": video['id']['videoId']
                     }
                 }
                 }
             )
             request.execute()
             time.sleep(0.2)                                                   # wait 1 sec to not exceed youtube quota
     except googleapiclient.errors.HttpError as err:
         print(Style.BRIGHT + Back.RED + Fore.WHITE + 'ERROR' + Style.RESET_ALL)
         raise RequestError(err.resp.status, err.content.decode('utf-8'))
     return f"https://www.youtube.com/playlist?list={playlist_id}"
 def create_playlist(self, playlist_name):
     """
     Creates a new empty playlist and returns its playlist_id
     Defaults privacy status to public
     """
     try:
         request = self.client.playlists().insert(
         part="snippet,status",
         body={
         "snippet": {
                 "title": playlist_name,
                 "description": "Created via YouTubeSpotifyMigration.",
                 "tags": [
                     "YouTubeSpotifyMigration",
                     "API call",
                     "music"
                 ],
                 "defaultLanguage": "en"
             },
             "status": {
                 "privacyStatus": "public"
             }
             }
         )
         response = request.execute()
     except googleapiclient.errors.HttpError as err:
         print(Style.BRIGHT + Back.RED + Fore.WHITE + 'ERROR' + Style.RESET_ALL)
         raise RequestError(err.resp.status, err.content.decode('utf-8'))
     else:
         return response['id']
 def get_playlist_items(self, id):    
     """get full details of videos for a given playlist id"""
     print(f'\nFetching items from selected Youtube playlist...', end=" ")
     try: 
         request = self.client.playlistItems().list(
             part="snippet,contentDetails",
             maxResults=50,
             playlistId=id
         )
         response = request.execute()
         nextPageToken = response.get('nextPageToken', None)
         while nextPageToken is not None:
             time.sleep(1)
             request = self.client.playlistItems().list(
                 part="snippet,contentDetails",
                 maxResults=50,
                 playlistId=id,
                 pageToken=nextPageToken
             )
             nextResponse = request.execute()
             response['items'].extend(nextResponse['items'])
             nextPageToken = nextResponse.get('nextPageToken', None)
     except googleapiclient.errors.HttpError as err:
         print(Style.BRIGHT + Back.RED + Fore.WHITE + 'ERROR' + Style.RESET_ALL)
         raise RequestError(err.resp.status, err.content.decode('utf-8'))
     else:
         print(Fore.GREEN + 'Success.\n' + Style.RESET_ALL)
         return response
 def get_all_playlists(self):
     """get all playlists for authenticated user"""
     # print('Fetching your Youtube playlists...', end=" ")
     print('\nFetching your Youtube playlists...', end=" ")
     time.sleep(1)
     try:
         request = self.client.playlists().list(
             part="snippet,contentDetails",
             maxResults=50,
             mine=True
         )
         response = request.execute()
         nextPageToken = response.get('nextPageToken', None)
         while nextPageToken is not None:
             time.sleep(1)
             request = self.client.playlists().list(
                 part="snippet,contentDetails",
                 maxResults=50,
                 playlistId=id,
                 pageToken=nextPageToken
             )
             nextResponse = request.execute()
             response['items'].extend(nextResponse['items'])
             nextPageToken = nextResponse.get('nextPageToken', None)
     except googleapiclient.errors.HttpError as err:
         print(Style.BRIGHT + Back.RED + Fore.WHITE + 'ERROR:' + Style.RESET_ALL)
         raise RequestError(err.resp.status, err.content.decode('utf-8'))
     else:
         print(Fore.GREEN + 'Success.\n' + Style.RESET_ALL)
         return response
 def get_search_result(self, track, artist):
     """
     Search for song by track and artist name.
     Return first result object or None
     """
     payload = {
         'query': f"track:{track} artist:{artist}",
         'type': "track",
         'limit': 1
     }
     request_url = "https://api.spotify.com/v1/search?" + urlencode(payload)
     payload = {
         'query': f"track:{artist} artist:{track}",
         'type': "track",
         'limit': 1
     }
     request_url_artist_as_track = "https://api.spotify.com/v1/search?" + urlencode(
         payload)
     headers = {
         "Content-Type": "application/json",
         "Authorization": f"Bearer {self.creds.access_token()}"
     }
     # make get request to spotify api for track and artist name
     response = requests.get(request_url, headers=headers)
     # check for good request
     if response.status_code != 200:
         raise RequestError(response.status_code, response.text)
     results_json = response.json()['tracks']['items']
     # check if search results contained any songs
     if len(results_json) == 0:
         # One last attempt, using the artist var as track
         # (in case where youtube title is Track - Artist instead of Artist - Track)
         response = requests.get(request_url_artist_as_track,
                                 headers=headers)
         if response.status_code != 200:
             raise RequestError(response.status_code, response.text)
         results_json = response.json()['tracks']['items']
         if len(results_json) == 0:
             return None
         else:
             return results_json[0]
     else:
         return results_json[0]
 def create_playlist(self, playlist_name):
     """Create new empty playlist and return playlist_id"""
     payload = {'name': playlist_name}
     headers = {
         "Content-Type": "application/json",
         "Authorization": f"Bearer {self.creds.access_token()}"
     }
     request_url = f"https://api.spotify.com/v1/users/{self.get_user_id()}/playlists"
     response = requests.post(request_url, json=payload, headers=headers)
     if response.status_code not in [200, 201]:
         raise RequestError(response.status_code, response.text)
     self.user_id = response.json()['id']
     return response.json()['id']
 def get_all_playlists(self):
     """Fetch and return all playlists associated with authenticated user"""
     print(f"\nFetching your Spotify playlists...", end=" ")
     # build and execute request
     request_url = f"https://api.spotify.com/v1/me/playlists?limit=50"
     headers = {
         "Content-Type": "application/json",
         "Authorization": f"Bearer {self.creds.access_token()}"
     }
     response = requests.get(request_url, headers=headers)
     if response.status_code != 200:
         raise RequestError(response.status_code, response.text)
     # handle responses that exceed item limit and require pagination
     response = response.json()
     nextURL = response['next']
     while nextURL is not None:
         nextResponse = requests.get(nextURL, headers=headers)
         if nextResponse.status_code != 200:
             raise RequestError(nextResponse.status_code, nextResponse.text)
         response['items'].extend(nextResponse.json()['items'])
         nextURL = nextResponse.json()['next']
     print(Fore.GREEN + 'Success.\n' + Style.RESET_ALL)
     return response['items']
 def get_user_id(self):
     """fetch spotify user id based on the access token"""
     if self.user_id is not None:
         return self.user_id
     else:
         headers = {
             "Content-Type": "application/json",
             "Authorization": f"Bearer {self.creds.access_token()}"
         }
         response = requests.get("https://api.spotify.com/v1/me",
                                 headers=headers)
         if response.status_code != 200:
             raise RequestError(response.status_code, response.text)
         return response.json()['id']
 def get_search_result(self, query_str):
     """
     Get first youtube search result for given query string.
     Return first video object or None
     """
     try:
         request = self.client.search().list(
             part="snippet",
             type="video",
             maxResults=1,
             q=query_str
         )
         response = request.execute()
     except googleapiclient.errors.HttpError as err:
         print(Style.BRIGHT + Back.RED + Fore.WHITE + 'ERROR' + Style.RESET_ALL)
         raise RequestError(err.resp.status, err.content.decode('utf-8'))
     else:
         if len(response['items']) == 0:
             return None                                                     # no results
         else:
             return response['items'][0]                                     # return first video