├── README.md ├── Screenshots ├── 1.png ├── 2.png └── 3.png ├── YoutubeToSpotify.py └── searched.csv /README.md: -------------------------------------------------------------------------------- 1 | # YoutubeToSpotify 2 | 3 | A Python script to fetch tracks of music channels on Youtube, find them on Spotify and add them to a playlist. 4 | The video titles from Youtube are filtered and then searched on Spotify for near perfect results. A CSV file keeps track of the tracks that are added to the Spotify playlist so the script can be executed when required. 5 | 6 | ![picture](Screenshots/1.png) 7 | ![picture](Screenshots/2.png) 8 | ![picture](Screenshots/3.png) 9 | ### Getting Started 10 | 11 | You need these before running the script: 12 | * Login to [Spotify Developer Dashboard](https://developer.spotify.com/dashboard/) and create a new app to obtain the Client ID and Client Secret. 13 | Add these to **CLIENT_ID** and **CLIENT_SECRET** in the code. 14 | After this, click edit settings of the newly created app and add http://localhost/ to Redirect URIs and save. 15 | 16 | * Get the Youtube API key [here](https://developers.google.com/youtube/v3/getting-started) and add it to **API_KEY** in the code. 17 | 18 | * [Spotipy](https://spotipy.readthedocs.io/en/latest/) : Spotipy is a lightweight Python library for the Spotify Web API. 19 | With Spotipy you get full access to all of the music data provided by the Spotify platform. 20 | 21 | * The [Google APIs Client Library](https://developers.google.com/youtube/v3/getting-started) for Python for making requests to Youtube 22 | 23 | * Create a new Spotify Playlist and name it anything you want. 24 | Get your spotify username and the playlist id. 25 | Copy the username and playlist ID and add them to **SPOTIFY_USERNAME** and **YOUTUBE_PLAYLIST_ID** in the code respectively. 26 | ``` 27 | Right click on the playlist in Spotify and go to Share > Copy Spotify URI. 28 | ``` 29 | You will receive a URI like this: 30 | ``` 31 | spotify:user:john:playlist:70WcLyhxyzRuSGf678MyPd 32 | ``` 33 | 34 | ### Installing 35 | 36 | To install Spotipy 37 | ``` 38 | pip install spotipy 39 | ``` 40 | 41 | To install Google API 42 | 43 | ``` 44 | pip install google-api-python-client 45 | ``` 46 | 47 | ## Adding channels 48 | 49 | To add channels from Youtube, simply access a channel page and examine the URL. If it is like: 50 | ``` 51 | https://www.youtube.com/user/ChillStepNation 52 | ``` 53 | Just add the part after https://www.youtube.com/user/ to **CHANNEL_USERNAMES** list in the code. In this case, ChillStepNation. 54 | 55 | If the URL is like: 56 | ``` 57 | https://www.youtube.com/channel/UCkjnVMHy 58 | ``` 59 | Add the part after https://www.youtube.com/channel/ (Here, UCkjnVMHy) to **CHANNEL_IDS** dictonary in the code in the format: 60 | ``` 61 | {'channelName' : 'channelID'} 62 | ``` 63 | To change the number of search results for each channel change **MAX_RESULT** in the code. Default is 20. 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /Screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkhilgaba/YoutubeToSpotify/15a0c69b666e6be1eafd02c92ddb2cf1e6e986cd/Screenshots/1.png -------------------------------------------------------------------------------- /Screenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkhilgaba/YoutubeToSpotify/15a0c69b666e6be1eafd02c92ddb2cf1e6e986cd/Screenshots/2.png -------------------------------------------------------------------------------- /Screenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkhilgaba/YoutubeToSpotify/15a0c69b666e6be1eafd02c92ddb2cf1e6e986cd/Screenshots/3.png -------------------------------------------------------------------------------- /YoutubeToSpotify.py: -------------------------------------------------------------------------------- 1 | # Readme: https://github.com/nikhilgaba001/YoutubeToSpotify/blob/master/README.md 2 | 3 | from apiclient.discovery import build 4 | import csv 5 | import spotipy 6 | import spotipy.util as util 7 | import sys 8 | 9 | # Number of titles to get from each channel 10 | MAX_RESULT = 20 11 | 12 | # Spotify credentials 13 | SPOTIFY_USERNAME = '' 14 | CLIENT_ID = '' 15 | CLIENT_SECRET = '' 16 | SCOPE = 'playlist-modify-private' 17 | 18 | # Spotify Playlist which will get the new tracks 19 | YOUTUBE_PLAYLIST_ID = '' 20 | 21 | # Youtube credentials 22 | API_KEY = '' 23 | 24 | # Add channel usernames from Youtube in this list 25 | CHANNEL_USERNAMES = [] 26 | 27 | # Add channel IDs from Youtube in this Dictionary like {'channelName' : 'channelID'} 28 | CHANNEL_IDS = {} 29 | 30 | # Words to delete from the fetched title name from Youtube 31 | IGNORE = ['(', '[', ' x', ')', ']', '&', 'lyrics', 'lyric', 32 | 'video', '/', ' proximity', ' ft', '.', ' edit', ' feat', ' vs', ','] 33 | 34 | 35 | # returns youtube client object 36 | def init_youtube_client(): 37 | try: 38 | print('Initialising Youtube Client....') 39 | client = build('youtube', 'v3', developerKey=API_KEY) 40 | print('\nClient initialised!\n') 41 | return client 42 | except: 43 | sys.exit('\nError initialising Youtube Client!\n') 44 | 45 | 46 | # for channel username 47 | def username_req(client, channel_username): 48 | req = client.channels().list(part='contentDetails', forUsername=channel_username) 49 | return req 50 | 51 | 52 | # for channel id 53 | def id_req(client, channel_id): 54 | req = client.channels().list(part='contentDetails', 55 | id=channel_id) 56 | return req 57 | 58 | 59 | # Takes a request object and youtube client object as input and returns a list of unfiltered titles of a channel 60 | def get_channel_uploads(req, youtube): 61 | print("\nGetting Channel's uploads...") 62 | r = req.execute() 63 | channel_uploads_id = r['items'][-1]['contentDetails']['relatedPlaylists']['uploads'] 64 | req = youtube.playlistItems().list( 65 | part='snippet', playlistId=channel_uploads_id, maxResults=MAX_RESULT) 66 | playlist = req.execute() 67 | videos_list = playlist['items'] 68 | return videos_list 69 | 70 | 71 | # Takes unfiltered list of channel's titles and returns a filtered list 72 | def filter_titles(videos_list): 73 | print('Filtering titles...') 74 | titles = [] 75 | for video in videos_list: 76 | title = (video['snippet']['title']).lower() 77 | for ch in IGNORE: 78 | if ch in title: 79 | title = title.replace(ch, '') 80 | artist = (title.rsplit('-')[0]).strip() 81 | track = (title[len(artist) + 2:]).strip() 82 | title = (artist + ' ' + track).strip() 83 | titles.append(title) 84 | return titles 85 | 86 | 87 | # Returns a spotify client object 88 | def init_spotify_client(): 89 | try: 90 | print('Initialising Spotify Client....') 91 | token = util.prompt_for_user_token(SPOTIFY_USERNAME, SCOPE, 92 | client_id=CLIENT_ID, 93 | client_secret=CLIENT_SECRET, 94 | redirect_uri='http://localhost/') 95 | spotify_client = spotipy.Spotify(auth=token) 96 | print('\nClient initialised!\n') 97 | return spotify_client 98 | except: 99 | sys('\nError initialising Spotify Client!\n') 100 | 101 | 102 | # Takes filtered list of all the titles of channels, search them on spotify, if not found, 103 | # reduces string by a word each time until found or len = 20, 104 | # prints total no. of tracks found, not found and returns a list of all the ids of the tracks found 105 | def search_spotify(spotify_client, titles_list): 106 | print('\nSearching....\n') 107 | add_tracks_id_list = [] 108 | not_found = [] 109 | 110 | for title in titles_list: 111 | print(f'Searching Track: {title}') 112 | with open('searched.csv', mode='a') as searched_file: 113 | searched_writer = csv.writer(searched_file) 114 | searched_writer.writerow([title]) 115 | result = spotify_client.search( 116 | title, limit=1, offset=0, type='track', market=None) 117 | if result['tracks']['total'] == 0: 118 | ntitle = title 119 | while(len(ntitle) >= 25): 120 | ntitle = ntitle.rsplit(' ', 1)[0] 121 | print(f'Searching: {ntitle}') 122 | result = spotify_client.search( 123 | ntitle, limit=1, offset=0, type='track', market=None) 124 | if result['tracks']['total']: 125 | result_id = result['tracks']['items'][-1]['id'] 126 | add_tracks_id_list.append(result_id) 127 | print(f'Track found : {ntitle} ') 128 | break 129 | if result['tracks']['total'] == 0: 130 | print(f'Not found: {title}') 131 | not_found.append(title) 132 | else: 133 | result_id = result['tracks']['items'][-1]['id'] 134 | add_tracks_id_list.append(result_id) 135 | print(f'Track found : {title}') 136 | print(f'\nTotal Tracks searched: {len(titles_list)}') 137 | # tracks_not_found = len(titles_list) - len(add_tracks_id_list) 138 | if not_found: 139 | print(f'{len(not_found)} Tracks not found: {not_found}') 140 | # print(f'Tracks not found: {tracks_not_found} - {not_found[(len(not_found)-tracks_not_found):]}') 141 | return add_tracks_id_list 142 | 143 | 144 | # Takes list of track ids to check for new addition, current playlist tracks ids list, 145 | # and adds new tracks to the playlist 146 | def add_tracks_spotify(spotify_client, add_tracks_id_list): 147 | new = [] 148 | for track in add_tracks_id_list: 149 | track_ = spotify_client.track(track) 150 | track_name = track_['name'] 151 | artist = track_['album']['artists'][0]['name'] 152 | name = artist + '-' + track_name 153 | new.append(name) 154 | print(f'Adding track: {name}') 155 | if add_tracks_id_list: 156 | spotify_client.user_playlist_add_tracks( 157 | SPOTIFY_USERNAME, YOUTUBE_PLAYLIST_ID, add_tracks_id_list, position=0) 158 | print(f'\n{len(new)} new tracks added: {new}\n') 159 | 160 | else: 161 | print('\n***************** No new tracks to add! *****************\n') 162 | 163 | 164 | # Takes youtube client object as input; for each channel, get channel uploads titles, filters them and returns a list of them 165 | def get_tracks_youtube(youtube): 166 | final_titles = [] 167 | titles_un = [] 168 | titles_id = [] 169 | SEARCHED_TITLES_LIST = [] 170 | if CHANNEL_USERNAMES: 171 | for username in CHANNEL_USERNAMES: 172 | req = username_req(youtube, username) 173 | videos_list = get_channel_uploads(req, youtube) 174 | titles = filter_titles(videos_list) 175 | titles_un += titles 176 | print(f'{username}, Total: {len(titles)}: {titles}\n') 177 | if CHANNEL_IDS: 178 | id_list = list(CHANNEL_IDS.items()) 179 | for item in id_list: 180 | ch_id = item[1] 181 | req = id_req(youtube, ch_id) 182 | videos_list = get_channel_uploads(req, youtube) 183 | titles = filter_titles(videos_list) 184 | titles_id += titles 185 | print(f'{item[0]}, Total: {len(titles)}: {titles}\n') 186 | total_titles = titles_un + titles_id 187 | 188 | with open('searched.csv') as searched: 189 | searched_reader = csv.reader(searched, delimiter=',') 190 | for line in searched_reader: 191 | SEARCHED_TITLES_LIST.append(line[0]) 192 | for title in total_titles: 193 | if title not in SEARCHED_TITLES_LIST: 194 | final_titles.append(title) 195 | return final_titles 196 | 197 | 198 | if __name__ == '__main__': 199 | spotify_client = init_spotify_client() 200 | youtube = init_youtube_client() 201 | youtube_titles = get_tracks_youtube(youtube) 202 | new_tracks = search_spotify(spotify_client, youtube_titles) 203 | add_tracks_spotify(spotify_client, new_tracks) 204 | -------------------------------------------------------------------------------- /searched.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nkhilgaba/YoutubeToSpotify/15a0c69b666e6be1eafd02c92ddb2cf1e6e986cd/searched.csv --------------------------------------------------------------------------------