//Creates a new playlist and populates it public async Task <ActionResult> NewPlaylist(AdvTrack playCreate) { //TODO populate playlist //Like previous spotify app //Based on genre BPM, etc //https://api.spotify.com/v1/recommendations/available-genre-seeds //https://api.spotify.com/v1/recommendations //using artists/genre seeds //get tracks to populate the playlist with var tracks = await PopulatePlaylist(playCreate); //create playlist after getting tracks in case there is an error var playlist = await CreateNewPlaylist(playCreate); var p = await AddTracksToPlaylist(tracks, playlist.Id); return(RedirectToAction("ViewPlaylist", "Home", new { href = playlist.Id })); }
//TODO Doesn't work with songs same artist public AdvTrack RecommendationEngine(List <AdvTrack> advTracks) { var math = new AdvTrack() { Instrumentalness = 0, Liveness = 0, Loudness = 0, Energy = 0, Danceability = 0, Speechiness = 0, Valence = 0 }; //Figure out how to analyze the tracks //would need standard deviation, mean, etc //Would pass in advtrack data foreach (var adv in advTracks) { math.Instrumentalness += adv.Instrumentalness; math.Liveness += adv.Liveness; math.Loudness += adv.Loudness; math.Energy += adv.Energy; math.Danceability += adv.Danceability; math.Speechiness += adv.Speechiness; math.Valence += adv.Valence; } //start with averages //then add in some noticeable outliers math.Instrumentalness /= advTracks.Count; math.Liveness /= advTracks.Count; math.Loudness /= advTracks.Count; math.Energy /= advTracks.Count; math.Danceability /= advTracks.Count; math.Speechiness /= advTracks.Count; math.Valence /= advTracks.Count; return(math); }
//TODO public async Task <ActionResult> GetRecsFromPlaylist(string href) { var trackRec = new AdvTrack(); var tempList = new List <Track>(); var user = await GetCurrentUser(); var listTracks = await GetPlaylistTracks("https://api.spotify.com/v1/playlists/2Ql3TagKirJQJDG93GqMiE"); var trackIds = new List <string>(); //analyze each track in a playlist foreach (var t in listTracks) { trackIds.Add(t.Track.Id); } var audioFeat = await GetAudioFeatures(trackIds.ToArray()); //GetAudioFeatures //send to recommendationengine trackRec = RecommendationEngine(audioFeat); //would be running Populateplaylist multiple times foreach (var t in listTracks) { trackRec.Artist = t.Track.Artists.FirstOrDefault().Name; //need to think of a better way to do this, want to prevent duplicates somehow tempList.AddRange(await PopulatePlaylist(trackRec, 1)); } var pc = new PlayCreate() { Name = "Test 4", UserId = user.Id }; var newList = await CreateNewPlaylist(pc); var x = await AddTracksToPlaylist(tempList, newList.Id); return(RedirectToAction("ViewPlaylist", "Home", new { href = newList.Id }));; }
//Playlist creation from Audio Features public async Task <ActionResult> NewAdvPlaylist(AdvTrackCalc adv) { var g = adv.GenreList.ToArray(); var user = await GetCurrentUser(); var advTrack = new AdvTrack() { Acousticness = adv.AvgAcousticness, Danceability = adv.AvgDance, Energy = adv.AvgEnergy, Instrumentalness = adv.AvgInstrumentalness, Liveness = adv.AvgLiveness, Loudness = adv.AvgLoudness, Speechiness = adv.AvgSpeechiness, Tempo = adv.AvgTempo, Valence = adv.AvgValence, UserId = user.Id, GenreList = adv.GenreList, Genre = string.Join(",", g) }; return(PartialView("_AdvTrackFeatures", advTrack)); }
//Helper functions for Spotify API #region Helpers //Param Dictionary Builder, could also use max/min instead of target public async Task <Dictionary <string, string> > BuildRecParamDict(AdvTrack a) { var paramDict = new Dictionary <string, string>() { { "limit", "100" } }; if (a.Genre != null && a.Genre.Trim() != "") { paramDict.Add("seed_genres", a.Genre); } if (a.Artist != null && a.Artist.Trim() != "") { paramDict.Add("seed_artists", await GetSingleArtist(a.Artist)); } if (a.Acousticness != null) { paramDict.Add("target_acousticness", a.Acousticness.ToString()); } if (a.Danceability != null) { paramDict.Add("target_danceability", a.Danceability.ToString()); } if (a.Energy != null) { paramDict.Add("target_energy", a.Energy.ToString()); } if (a.Instrumentalness != null) { paramDict.Add("target_instrumentalness", a.Instrumentalness.ToString()); } if (a.Key != null) { paramDict.Add("target_key", a.Key.ToString()); } if (a.Liveness != null) { paramDict.Add("target_liveness", a.Liveness.ToString()); } if (a.Loudness != null) { paramDict.Add("target_loudness", a.Loudness.ToString()); } if (a.Mode != null) { paramDict.Add("target_mode", a.Mode.ToString()); } if (a.Speechiness != null) { paramDict.Add("target_speechiness", a.Speechiness.ToString()); } if (a.Tempo != null) { paramDict.Add("target_tempo", a.Tempo.ToString()); } if (a.Time_signature != null) { paramDict.Add("target_time_signature", a.Time_signature.ToString()); } if (a.Valence != null) { paramDict.Add("target_valence", a.Valence.ToString()); } return(paramDict); }
/// <summary> /// GETS tracks recommended by spotify based on the supplied criteria /// </summary> /// <param name="playCreate">Playlist info</param> /// <returns>List of track objects</returns> public async Task <List <Track> > PopulatePlaylist(AdvTrack playCreate, int limit = 0) { //set a default length var totalTime = playCreate.Length <= 0 ? 3600000 : 60000 * playCreate.Length; var trackList = new List <Track>(); var getString = "https://api.spotify.com/v1/recommendations"; //need to replace selected genres with closest match. //LoadAndReplaceGenres(playCreate.Genre) var genreString = "https://api.spotify.com/v1/recommendations/available-genre-seeds"; var response = await SpotApi(HttpMethod.Get, genreString); var j = JsonConvert.DeserializeObject <GenreList>(response).Genres.ToList <string>(); var splitGenre = playCreate.Genre.Replace(" ", "-").Split(',').ToList <string>(); var genreList = ""; foreach (var genre in j.ToList <string>()) { foreach (var g in splitGenre.ToList <string>()) { if (g == genre) { genreList += g + ","; } } } if (genreList == "") { foreach (var genre in j) { foreach (var g in splitGenre) { if (g.Contains(genre) && !genreList.Contains(genre)) { genreList += genre + ","; } } } } playCreate.Genre = genreList; var paramDict = await BuildRecParamDict(playCreate); //get recommended tracks based on seeds var res = await SpotApi(HttpMethod.Get, getString, paramDict); var list = JsonConvert.DeserializeObject <SeedResult>(res).Tracks.ToList(); if (limit == 0) { foreach (var l in list) { if (totalTime > 0) { trackList.Add(l); } totalTime -= l.Duration_Ms; } } else { foreach (var l in list) { if (limit > 0) { trackList.Add(l); } limit--; } } return(trackList); }