HugoLAMOUREUX / Spotidata

Web application linked to Spotify that shows some statistics about your music tastes and miscellaneous informations
MIT License
4 stars 5 forks source link

New backend functionnality : GetAlbumDetails #8

Open SharkyFlou opened 1 year ago

SharkyFlou commented 1 year ago

Start Date

No response

Implementation PR

No response

Reference Issues

No response

Summary

Get album mood (energetic, sad etc), top genre (and percentages), top subgenre (percentages), most repeated artist, average tempo and length.

Average popularity, hapiness, speechiness etc More info here : https://developer.spotify.com/documentation/web-api/reference/#/operations/get-several-audio-features

Basic Example

Would be called like that : GetPlaylistDetails(string:acces_token,string:paylist_id){...} Return a JSON with everything.

Drawbacks

Would allows to display mutliple interesting information about albums.

Unresolved questions

No response

AlexLunaP commented 1 year ago

Hi! Can I start contributing to this issue?

SharkyFlou commented 1 year ago

If you have any questions, don't hesitate to ask, I or someone else will answer them.

AlexLunaP commented 1 year ago

Hi, do I also need to give you my email, so I can use my spotify account to test the web? alexluna2001@hotmail.com

HugoLAMOUREUX commented 1 year ago

I've added you to the authorized users so you should be able to use it now ! I'm also gonna send you the content of the .env file

AlexLunaP commented 1 year ago

Hi, I think I managed to write a working code for this issue. I haven't tested it yet though, because the method getAlbumDetails needs to call getAlbumTracks method, and that issue is assigned to another contributor (#7) but it isn't done yet.

I also created other necessary files for this method to work:

getAlbumDetails code:

const getAlbumDetails = async (req, res) => {
    if (!req.query.access_token || !req.query.album_id) {
      res.status(400);
    }
    const spotifyApi = new SpotifyWebApi({
      accessToken: req.query.access_token,
    });

    //set all default values to 0
    let return_value = {};
    return_value.mean_danceability = 0.0;
    return_value.mean_energy = 0.0;
    return_value.mean_loudness = 0;
    return_value.mean_speechiness = 0;
    return_value.mean_acousticness = 0;
    return_value.mean_instrumentalness = 0;
    return_value.mean_liveness = 0;
    return_value.mean_valence = 0;
    return_value.mean_tempo = 0;
    return_value.mean_time_signature = 0;
    return_value.mean_key = 0;
    return_value.mean_mode = 0;
    return_value.mean_duration_ms = 0;
    return_value.mean_popularity = 0;
    return_value.nbr_tracks_audio_ft = 0;
    return_value.nbr_tracks_get_norm = 0;
    return_value.nbr_tracks = 0;
    return_value.genres = {};
    return_value.artists = {};

    //is used to store the total number of tracks in the album
    let total;
    //is used to store temporarly the ids of the tracks that will be used to get the details of the tracks
    let tracks_ids = [];

    //get the first 50 track's ids of the album
    await getAlbumTracks(spotifyApi, req.query.album_id, 50, 0).then(
      function (data) {
        total = data.total;
        data.items.forEach((item) => {
          tracks_ids.push(item.track_id);
        });
      }
    );

    //get the details of the tracks
    return_value = await getTracksDetails(spotifyApi, tracks_ids, return_value);

    //if there are more tracks to get, get them
    if (total > 50) {
        for (let i = 0; i < (total - 50) / 50; i++) {
            tracks_ids = [];
            await getAlbumTracks(
                spotifyApi,
                req.query.album_id,
                50,
                (i + 1) * 50
            ).then(function (data) {
                data.items.forEach((item) => {
                tracks_ids.push(item.track_id);
                });
            });

            return_value = await getTracksDetails(
                spotifyApi,
                tracks_ids,
                return_value,
                res
            );
        }
    }

    //calculate the mean of each feature
    return_value.mean_danceability = (
        return_value.mean_danceability / return_value.nbr_tracks_audio_ft
    ).toFixed(2);

    return_value.mean_energy = (
        return_value.mean_energy / return_value.nbr_tracks_audio_ft
    ).toFixed(2);

    return_value.mean_loudness = (
        return_value.mean_loudness / return_value.nbr_tracks_audio_ft
    ).toFixed(2);

    return_value.mean_speechiness = (
        return_value.mean_speechiness / return_value.nbr_tracks_audio_ft
    ).toFixed(2);

    return_value.mean_acousticness = (
        return_value.mean_acousticness / return_value.nbr_tracks_audio_ft
    ).toFixed(2);

    return_value.mean_instrumentalness = (
        return_value.mean_instrumentalness / return_value.nbr_tracks_audio_ft
    ).toFixed(2);

    return_value.mean_liveness = (
        return_value.mean_liveness / return_value.nbr_tracks_audio_ft
    ).toFixed(2);

    return_value.mean_valence = (
        return_value.mean_valence / return_value.nbr_tracks_audio_ft
    ).toFixed(2);

    return_value.mean_tempo = (
        return_value.mean_tempo / return_value.nbr_tracks_audio_ft
    ).toFixed(2);

    return_value.mean_time_signature = (
        return_value.mean_time_signature / return_value.nbr_tracks_audio_ft
    ).toFixed(2);

    return_value.mean_key = (
        return_value.mean_key / return_value.nbr_tracks_audio_ft
    ).toFixed(2);

    return_value.mean_mode = (
        return_value.mean_mode / return_value.nbr_tracks_audio_ft
    ).toFixed(2);

    return_value.mean_duration_s = (
        return_value.mean_duration_ms /
        return_value.nbr_tracks_audio_ft /
        1000
    ).toFixed(2);

    return_value.mean_popularity = (
        return_value.mean_popularity / return_value.nbr_tracks_get_norm
    ).toFixed(2);

    //delete for more clarity
    delete return_value.nbr_tracks_audio_ft;
    delete return_value.nbr_tracks_get_norm;

    return_value.nbr_tracks = total;

    //sort artists by number of tracks they appear in
    let sorted_artists = Object.keys(return_value.artists).sort(function (a, b) {
        return return_value.artists[b].nbr - return_value.artists[a].nbr;
    });

    //get the 10 most common artists
    return_value.common_artists = [];
    for (let i = 0; i < 10; i++) {
        if (sorted_artists[i] != undefined) {
        return_value.artists[sorted_artists[i]].id = i + 1;
        return_value.common_artists.push(return_value.artists[sorted_artists[i]]);
        }
    }
    delete return_value.artists;

    res.status(200).json(return_value);
};

Check the rest of the code files: https://github.com/AlexLunaP/Spotidata

AlexLunaP commented 1 year ago

Hi, as the method getAlbumTracks is already implemented, now I can test my code for getAlbumDetails, any help on this? Not sure how to test it.

SharkyFlou commented 1 year ago

Ok, to test your function, you need to use Postman, which allows you to easily make requests to the back-end and see what the function returns.

To do this, :

Good luck !

And if I was not clear, don't hesitate to leave a message here, I'll be happy to help.

AlexLunaP commented 1 year ago

Hi. Thanks, it is well explained.

I am having some trouble testing some methods. Some of them give the correct response in Postman, but others are not working for me (maybe I'm using the wrong parameters?) Would you mind if I show you on Discord or similar? I think it can be faster to explain. My email is alexluna2001@hotmail.com

Thanks in advance

SharkyFlou commented 1 year ago

Yes, sorry for the delay, I'll send you my discord by mail.