def _get_audius_search_results(query, formats): Track = apps.get_model("music", "Track") Request = apps.get_model("networking", "Request") data = { "query": query, "app_name": "Jukebox Radio", } response = make_request( Request.TYPE_GET, "https://discoveryprovider.audius5.prod-us-west-2.staked.cloud/v1/tracks/search", data=data, ) response_json = response.json() cleaned_data = [] for item in response_json["data"]: cleaned_data.append( { "format": Track.FORMAT_TRACK, "provider": GLOBAL_PROVIDER_AUDIUS, "external_id": item["id"], "name": item["title"], "artist_name": item["user"]["name"], "album_name": "N/A", "img_url": (item["artwork"] and item["artwork"]["1000x1000"]) or "N/A", "duration_ms": item["duration"] * 1000, } ) return cleaned_data
def _get_youtube_search_results(query, formats): """ Get YouTube search results """ Track = apps.get_model("music", "Track") Request = apps.get_model("networking", "Request") formats = set(formats).intersection(set([Track.FORMAT_VIDEO])) if not formats: return [] data = { "part": "snippet", "q": query, "key": settings.GOOGLE_API_KEY, "type": ",".join(formats), "videoEmbeddable": True, "maxResults": 25, } response = make_request( Request.TYPE_GET, "https://www.googleapis.com/youtube/v3/search", data=data, headers={ "Content-Type": "application/json", }, ) response_json = response.json() cleaned_data = [] if "items" not in response_json: return cleaned_data for item in response_json["items"]: youtube_id = item["id"]["videoId"] youtube_channel_name = item["snippet"]["channelTitle"] youtube_video_name = item["snippet"]["title"] youtube_img_lg = item["snippet"]["thumbnails"]["high"]["url"] cleaned_data.append( { "format": Track.FORMAT_VIDEO, "provider": GLOBAL_PROVIDER_YOUTUBE, "external_id": youtube_id, "name": youtube_video_name, "artist_name": youtube_channel_name, "album_name": "", # TODO maybe this shouldn't just be an empty string "img_url": youtube_img_lg, } ) return cleaned_data
def post(self, request, **kwargs): """ Spotify redirects a user to this URL after the Spotify authorization process. """ Request = apps.get_model("networking", "Request") user = request.user code = self.param(request, "code") error = self.param(request, "error") if error: return self.http_response_400(error) data = { "grant_type": "authorization_code", "code": code, "redirect_uri": generate_redirect_uri(request), "client_id": settings.SPOTIFY_CLIENT_ID, "client_secret": settings.SPOITFY_CLIENT_SECRET, } response = make_request( Request.TYPE_POST, "https://accounts.spotify.com/api/token", data=data, user=user, ) response_json = response.json() cipher_suite = Fernet(settings.FERNET_KEY) token = response_json["access_token"] token_utf8 = token.encode("utf-8") encrypted_token_utf8 = cipher_suite.encrypt(token_utf8) encrypted_token = encrypted_token_utf8.decode("utf-8") user.encrypted_spotify_access_token = encrypted_token token = response_json["refresh_token"] token_utf8 = token.encode("utf-8") encrypted_token_utf8 = cipher_suite.encrypt(token_utf8) encrypted_token = encrypted_token_utf8.decode("utf-8") user.encrypted_spotify_refresh_token = encrypted_token user.spotify_scope = ",".join(settings.SPOTIFY_USER_DATA_SCOPES) user.save() return self.http_response_200()
def _refresh_track_spotify_data(track, user): Request = apps.get_model("networking", "Request") response = make_request( Request.TYPE_GET, f"https://api.spotify.com/v1/tracks/{track.spotify_id}", headers={ "Authorization": f"Bearer {user.spotify_access_token}", "Content-Type": "application/json", }, ) response_json = response.json() # TODO refresh more data points track.duration_ms = response_json["duration_ms"] track.save()
def _refresh_track_youtube_data(track): Request = apps.get_model("networking", "Request") data = { "part": "snippet,contentDetails", "id": track.youtube_id, "key": settings.GOOGLE_API_KEY, } response = make_request( Request.TYPE_GET, "https://www.googleapis.com/youtube/v3/videos", data=data, headers={"Content-Type": "application/json"}, ) response_json = response.json() # clean duration duration_raw = response_json["items"][0]["contentDetails"]["duration"] duration_ms = 0 mode = None val = "" for char in duration_raw: if char in ["P", "T"]: continue if char in ["H", "M", "S"]: if char == "H": duration_ms += int(val) * 60 * 60 * 1000 elif char == "M": duration_ms += int(val) * 60 * 1000 elif char == "S": duration_ms += int(val) * 1000 else: raise ValueError(f"Unexpected playtime character: {mode}") val = "" continue val += char # TODO refresh more data points track.duration_ms = duration_ms track.save()
def refresh_spotify_access_tokens(): Request = apps.get_model("networking", "Request") cipher_suite = Fernet(settings.FERNET_KEY) user_qs = User.objects.filter(encrypted_spotify_refresh_token__isnull=False) for user in user_qs: try: encrypted_token = user.encrypted_spotify_refresh_token encrypted_token_utf8 = encrypted_token.encode("utf-8") token_utf8 = cipher_suite.decrypt(encrypted_token_utf8) token = token_utf8.decode("utf-8") data = { "grant_type": "refresh_token", "refresh_token": token, "client_id": settings.SPOTIFY_CLIENT_ID, "client_secret": settings.SPOITFY_CLIENT_SECRET, } response = make_request( Request.TYPE_POST, "https://accounts.spotify.com/api/token", data=data, user=user, ) response_json = response.json() token = response_json["access_token"] token_utf8 = token.encode("utf-8") encrypted_token_utf8 = cipher_suite.encrypt(token_utf8) encrypted_token = encrypted_token_utf8.decode("utf-8") user.encrypted_spotify_access_token = encrypted_token user.save() except Exception: user.encrypted_spotify_access_token = None user.encrypted_spotify_refresh_token = None user.spotify_scope = None user.save()
def _get_apple_music_search_results(query, formats): Collection = apps.get_model("music", "Collection") Track = apps.get_model("music", "Track") Request = apps.get_model("networking", "Request") formats = set(formats).intersection( set([Track.FORMAT_TRACK, Collection.FORMAT_ALBUM, Collection.FORMAT_PLAYLIST]) ) if Track.FORMAT_TRACK in formats: formats.remove(Track.FORMAT_TRACK) formats.add("songs") if Collection.FORMAT_ALBUM in formats: formats.remove(Collection.FORMAT_ALBUM) formats.add("albums") if Collection.FORMAT_PLAYLIST in formats: formats.remove(Collection.FORMAT_PLAYLIST) formats.add("playlists") formats.add("music-videos") data = { "term": query, "types": ",".join(formats), } apple_music_token = generate_apple_music_token() response = make_request( Request.TYPE_GET, "https://api.music.apple.com/v1/catalog/us/search", data=data, headers={ "Authorization": f"Bearer {apple_music_token}", }, ) response_json = response.json() cleaned_data = [] if "albums" in response_json["results"].keys(): items = response_json["results"]["albums"]["data"] for item in items: cleaned_data.append( { "format": Collection.FORMAT_ALBUM, "provider": "apple_music", "external_id": item["id"], "name": item["attributes"]["name"], "artist_name": item["attributes"]["artistName"], "img_url": item["attributes"]["artwork"]["url"], } ) if "playlists" in response_json["results"].keys(): items = response_json["results"]["playlists"]["data"] for item in items: cleaned_data.append( { "format": Collection.FORMAT_PLAYLIST, "provider": "apple_music", "external_id": item["id"], "name": item["attributes"]["name"], "artist_name": item["attributes"]["curatorName"], "img_url": item["attributes"]["artwork"]["url"], } ) if "songs" in response_json["results"].keys(): items = response_json["results"]["songs"]["data"] for item in items: cleaned_data.append( { "format": Track.FORMAT_TRACK, "provider": "apple_music", "external_id": item["id"], "name": item["attributes"]["name"], "artist_name": item["attributes"]["artistName"], "album_name": item["attributes"]["albumName"], "img_url": item["attributes"]["artwork"]["url"], "duration_ms": item["attributes"]["durationInMillis"], } ) if "music-videos" in response_json["results"].keys(): items = response_json["results"]["music-videos"]["data"] for item in items: print(item) cleaned_data.append( { "format": Track.FORMAT_VIDEO, "provider": "apple_music", "external_id": item["id"], "name": item["attributes"]["name"], "artist_name": item["attributes"]["artistName"], "img_url": item["attributes"]["artwork"]["url"], "duration_ms": item["attributes"]["durationInMillis"], } ) return cleaned_data
def _get_spotify_search_results(query, formats, user): Collection = apps.get_model("music", "Collection") Track = apps.get_model("music", "Track") Request = apps.get_model("networking", "Request") formats = set(formats).intersection( set([Track.FORMAT_TRACK, Collection.FORMAT_ALBUM, Collection.FORMAT_PLAYLIST]) ) data = { "q": query, "type": ",".join(formats), } spotify_access_token = user.spotify_access_token response = make_request( Request.TYPE_GET, "https://api.spotify.com/v1/search", data=data, headers={ "Authorization": f"Bearer {spotify_access_token}", "Content-Type": "application/json", }, ) response_json = response.json() data = [] # Glue everything together # - - - - - - - - - - - - - if "albums" in response_json: items = response_json["albums"]["items"] for item in items: data.append( { "format": "album", "provider": "spotify", "external_id": item["uri"], "name": item["name"], "artist_name": ", ".join([a["name"] for a in item["artists"]]), "img_url": item["images"][0]["url"], } ) if "playlists" in response_json: items = response_json["playlists"]["items"] for item in items: data.append( { "format": "playlist", "provider": "spotify", "external_id": item["uri"], "name": item["name"], "artist_name": item["owner"]["display_name"], "img_url": item["images"][0]["url"], } ) if "tracks" in response_json: items = response_json["tracks"]["items"] for item in items: data.append( { "format": "track", "provider": "spotify", "external_id": item["uri"], "name": item["name"], "artist_name": ", ".join([a["name"] for a in item["artists"]]), "album_name": item["album"]["name"], "img_url": item["album"]["images"][0]["url"], } ) return data
def _refresh_collection_apple_music_playlist_data(collection, user): CollectionListing = apps.get_model("music", "CollectionListing") Track = apps.get_model("music", "Track") Request = apps.get_model("networking", "Request") apple_music_token = generate_apple_music_token() apple_music_id = collection.external_id response = make_request( Request.TYPE_GET, f"https://api.music.apple.com/v1/catalog/us/playlists/{apple_music_id}", headers={ "Authorization": f"Bearer {apple_music_token}", }, ) response_json = response.json() items = response_json["data"][0]["relationships"]["tracks"]["data"] data = [] for item in items: data.append({ # to be saved as Track instances "format": Track.FORMAT_TRACK, "provider": Track.PROVIDER_APPLE_MUSIC, "external_id": item["id"], "name": item["attributes"]["name"], "artist_name": item["attributes"]["artistName"], "album_name": item["attributes"]["albumName"], "duration_ms": item["attributes"]["durationInMillis"], "img_url": item["attributes"]["artwork"]["url"], # not saved in Track table "_disk_number": item["attributes"]["discNumber"], "_track_number": item["attributes"]["trackNumber"], }) # first sort by disk number, then by track number data = sorted(data, key=lambda d: (d["_disk_number"], d["_track_number"])) tracks = [] track_eids = [] for track_data in data: tracks.append( Track( format=track_data["format"], provider=track_data["provider"], external_id=track_data["external_id"], name=track_data["name"], artist_name=track_data["artist_name"], album_name=track_data["album_name"], img_url=track_data["img_url"], duration_ms=track_data["duration_ms"], )) track_eids.append(track_data["external_id"]) # create Track objects if they do not already exist # TODO refresh more data points Track.objects.bulk_create(tracks, ignore_conflicts=True) track_qs = Track.objects.filter(external_id__in=track_eids) # wipe old CollectionListing objects # TODO this is not ok cl_by_tracks_qs = CollectionListing.objects.filter( track__external_id__in=track_eids) cl_by_collection_qs = CollectionListing.objects.filter( collection=collection) now = time_util.now() cl_by_tracks_qs.update(deleted_at=now) cl_by_collection_qs.update(deleted_at=now) # sort tracks by order one more time track_map = {} for track in track_qs: track_map[track.external_id] = track tracks = [] for track_eid in track_eids: tracks.append(track_map[track_eid]) # create CollectionListing objects if they do not already exist collection_listings = [] for idx in range(len(tracks)): track = tracks[idx] collection_listings.append( CollectionListing( track=track, collection=collection, number=idx, )) CollectionListing.objects.bulk_create(collection_listings)
def _refresh_collection_spotify_playlist_data(collection, user): Track = apps.get_model("music", "Track") CollectionListing = apps.get_model("music", "CollectionListing") Request = apps.get_model("networking", "Request") response = make_request( Request.TYPE_GET, f"https://api.spotify.com/v1/playlists/{collection.spotify_id}", headers={ "Authorization": f"Bearer {user.spotify_access_token}", "Content-Type": "application/json", }, ) response_json = response.json() # items are pre-sorted here items = response_json["tracks"]["items"] data = [] for item in items: artist_names = map(lambda o: o["name"], item["track"]["artists"]) data.append({ "format": Track.FORMAT_TRACK, "provider": Track.PROVIDER_SPOTIFY, "external_id": item["track"]["uri"], "name": item["track"]["name"], "artist_name": ", ".join(artist_names), "album_name": collection.name, "duration_ms": item["track"]["duration_ms"], "img_url": item["track"]["album"]["images"][0]["url"], }) tracks = [] track_eids = [] for track_data in data: tracks.append( Track( format=track_data["format"], provider=track_data["provider"], external_id=track_data["external_id"], name=track_data["name"], artist_name=track_data["artist_name"], album_name=track_data["album_name"], img_url=track_data["img_url"], duration_ms=track_data["duration_ms"], )) track_eids.append(track_data["external_id"]) # create Track objects if they do not already exist # TODO refresh more data points Track.objects.bulk_create(tracks, ignore_conflicts=True) track_qs = Track.objects.filter(external_id__in=track_eids) # wipe old CollectionListing objects to force refresh playlist # TODO this is not ok cl_by_tracks_qs = CollectionListing.objects.filter( track__external_id__in=track_eids) cl_by_collection_qs = CollectionListing.objects.filter( collection=collection) now = time_util.now() cl_by_tracks_qs.update(deleted_at=now) cl_by_collection_qs.update(deleted_at=now) # sort tracks by order one more time track_map = {} for track in track_qs: track_map[track.external_id] = track tracks = [] for track_eid in track_eids: tracks.append(track_map[track_eid]) # create CollectionListing objects if they do not already exist collection_listings = [] for idx in range(len(tracks)): track = tracks[idx] collection_listings.append( CollectionListing( track=track, collection=collection, number=idx, )) CollectionListing.objects.bulk_create(collection_listings)