Repository: SathyaBhat/spotify-dl

Issue:
Add BPM to metadata of MP3 songs.
Since adding BPM to the metadata of the MP3 is an option provided with the Spotify API and mutagen supports it as well. This feature could help with many who are in the field of DJ Mixing.



Discussion:
krishsharma0413: All 12 tests are passing but I don't have any idea on how to reflect that here.
Please guide me with these checks.
SathyaBhat: > All 12 tests are passing but I don't have any idea on how to reflect that here. Please guide me with these checks.

I don't see any changes to the tests - are they committed?
krishsharma0413: I was waiting for your approval for creation of new tests for the BPM itself. I will create them ASAP
krishsharma0413: hey @SathyaBhat,
Does this work? Please tell me if there needs to be anymore changes.
SathyaBhat: > hey @SathyaBhat, Does this work? Please tell me if there needs to be anymore changes.

the tests seem to be still failing  https://github.com/SathyaBhat/spotify-dl/actions/runs/7364433669/job/20044825544?pr=368

```
                    track_album = track["album"]
>                   track_tempo = track["tempo"]
E                   KeyError: 'tempo'


```
krishsharma0413: oh it's because i didn't update those test cases. let me fix those as well
krishsharma0413: The code checks for the BPM in spotify.py and since the test cases are made custom rather than going through the library they don't have the tempo as a key-value pair. I can fetch the data of the music manually and assign the tempo to it.

Patch:
diff --git a/spotify_dl/spotify.py b/spotify_dl/spotify.py
index f777a59..448d5e4 100644
--- a/spotify_dl/spotify.py
+++ b/spotify_dl/spotify.py
@@ -42,8 +42,14 @@ def fetch_tracks(sp, item_type, item_id):
                         continue
                     track_album_info = track_info.get("album")
                     track_num = track_info.get("track_number")
-                    spotify_id = track_info.get("id")
                     track_name = track_info.get("name")
+                    spotify_id = track_info.get("id")
+                    try:
+                        track_audio_data = sp.audio_analysis(spotify_id)
+                        tempo = track_audio_data.get("track").get("tempo")
+                    except:
+                        log.error("Couldn't fetch audio analysis for %s", track_name)
+                        tempo = None
                     track_artist = ", ".join(
                         [artist["name"] for artist in track_info.get("artists")]
                     )
@@ -86,6 +92,7 @@ def fetch_tracks(sp, item_type, item_id):
                             "genre": genre,
                             "spotify_id": spotify_id,
                             "track_url": None,
+                            "tempo": tempo,
                         }
                     )
                     offset += 1
@@ -141,6 +148,12 @@ def fetch_tracks(sp, item_type, item_id):
                     )
                     track_num = item["track_number"]
                     spotify_id = item.get("id")
+                    try:
+                        track_audio_data = sp.audio_analysis(spotify_id)
+                        tempo = track_audio_data.get("track").get("tempo")
+                    except:
+                        log.error("Couldn't fetch audio analysis for %s", track_name)
+                        tempo = None
                     songs_list.append(
                         {
                             "name": track_name,
@@ -154,6 +167,7 @@ def fetch_tracks(sp, item_type, item_id):
                             "cover": cover,
                             "genre": genre,
                             "spotify_id": spotify_id,
+                            "tempo": tempo,
                         }
                     )
                     offset += 1
@@ -182,6 +196,12 @@ def fetch_tracks(sp, item_type, item_id):
             album_total = album_info.get("total_tracks")
         track_num = items["track_number"]
         spotify_id = items["id"]
+        try:
+            track_audio_data = sp.audio_analysis(spotify_id)
+            tempo = track_audio_data.get("track").get("tempo")
+        except:
+            log.error("Couldn't fetch audio analysis for %s", track_name)
+            tempo = None
         if len(items["album"]["images"]) > 0:
             cover = items["album"]["images"][0]["url"]
         else:
@@ -203,6 +223,7 @@ def fetch_tracks(sp, item_type, item_id):
                 "genre": genre,
                 "track_url": None,
                 "spotify_id": spotify_id,
+                "tempo": tempo,
             }
         )
 
diff --git a/spotify_dl/youtube.py b/spotify_dl/youtube.py
index e0f47f5..f3b6846 100644
--- a/spotify_dl/youtube.py
+++ b/spotify_dl/youtube.py
@@ -64,13 +64,13 @@ def write_tracks(tracks_file, song_dict):
         i = 0
         writer = csv.writer(file_out, delimiter=";")
         for url_dict in song_dict["urls"]:
-            # for track in url_dict['songs']:
             for track in url_dict["songs"]:
                 track_url = track["track_url"]  # here
                 track_name = track["name"]
                 track_artist = track["artist"]
                 track_num = track["num"]
                 track_album = track["album"]
+                track_tempo = track["tempo"]
                 track["save_path"] = url_dict["save_path"]
                 track_db.append(track)
                 track_index = i
@@ -81,6 +81,7 @@ def write_tracks(tracks_file, song_dict):
                     track_url,
                     str(track_num),
                     track_album,
+                    str(track_tempo),
                     str(track_index),
                 ]
                 try:
@@ -119,6 +120,8 @@ def set_tags(temp, filename, kwargs):
         )
 
     song_file["genre"] = song.get("genre")
+    if song.get("tempo") is not None:
+        song_file["bpm"] = str(song.get("tempo"))
     song_file.save()
     song_file = MP3(filename, ID3=ID3)
     cover = song.get("cover")


Test patch:
diff --git a/tests/test_spotify_fetch_tracks.py b/tests/test_spotify_fetch_tracks.py
index 89b2ed6..7addca4 100644
--- a/tests/test_spotify_fetch_tracks.py
+++ b/tests/test_spotify_fetch_tracks.py
@@ -32,6 +32,7 @@ def test_spotify_playlist_fetch_one():
         "track_url": None,
         "playlist_num": 1,
         "spotify_id": "2GpBrAoCwt48fxjgjlzMd4",
+        'tempo': 74.656,
     } == songs[0]
 
 
@@ -53,6 +54,7 @@ def test_spotify_playlist_fetch_more():
             "year": "2012",
             "playlist_num": 1,
             "spotify_id": "4rzfv0JLZfVhOhbSQ8o5jZ",
+            'tempo': 135.016,
         },
         {
             "album": "Wellness & Dreaming Source",
@@ -66,6 +68,7 @@ def test_spotify_playlist_fetch_more():
             "playlist_num": 2,
             "track_url": None,
             "spotify_id": "5o3jMYOSbaVz3tkgwhELSV",
+            'tempo': 137.805,
         },
         {
             "album": "This Is Happening",
@@ -79,6 +82,7 @@ def test_spotify_playlist_fetch_more():
             "year": "2010",
             "playlist_num": 3,
             "spotify_id": "4Cy0NHJ8Gh0xMdwyM9RkQm",
+            'tempo': 134.99,
         },
         {
             "album": "Glenn Horiuchi Trio / Gelenn Horiuchi Quartet: Mercy / Jump Start "
@@ -94,6 +98,7 @@ def test_spotify_playlist_fetch_more():
             "track_url": None,
             "playlist_num": 4,
             "spotify_id": "6hvFrZNocdt2FcKGCSY5NI",
+            'tempo': 114.767,
         },
         {
             "album": "All The Best (Spanish Version)",
@@ -107,6 +112,7 @@ def test_spotify_playlist_fetch_more():
             "year": "2007",
             "playlist_num": 5,
             "spotify_id": "2E2znCPaS8anQe21GLxcvJ",
+            'tempo': 122.318,
         },
     ] == songs
 
@@ -128,6 +134,7 @@ def test_spotify_track_fetch_one():
         "track_url": None,
         "playlist_num": 1,
         "spotify_id": "2GpBrAoCwt48fxjgjlzMd4",
+        'tempo': 74.656,
     } == songs[0]
 
 
@@ -148,6 +155,7 @@ def test_spotify_album_fetch_one():
         "year": "2012",
         "playlist_num": 1,
         "spotify_id": "5EoKQDGE2zxrTfRFZF52u5",
+        'tempo': 120.009,
     } == songs[0]
 
 
@@ -169,6 +177,7 @@ def test_spotify_album_fetch_more():
             "year": "1974",
             "playlist_num": 1,
             "spotify_id": "69Yw7H4bRIwfIxL0ZCZy8y",
+            'tempo': 120.955,
         },
         {
             "album": "Queen II (Deluxe Remastered Version)",
@@ -182,6 +191,7 @@ def test_spotify_album_fetch_more():
             "year": "1974",
             "playlist_num": 2,
             "spotify_id": "5GGSjXZeTgX9sKYBtl8K6U",
+            'tempo': 147.384,
         },
         {
             "album": "Queen II (Deluxe Remastered Version)",
@@ -195,6 +205,7 @@ def test_spotify_album_fetch_more():
             "year": "1974",
             "playlist_num": 3,
             "spotify_id": "0Ssh20fuVhmasLRJ97MLnp",
+            'tempo': 152.769,
         },
         {
             "album": "Queen II (Deluxe Remastered Version)",
@@ -208,6 +219,7 @@ def test_spotify_album_fetch_more():
             "year": "1974",
             "playlist_num": 4,
             "spotify_id": "2LasW39KJDE4VH9hTVNpE2",
+            'tempo': 115.471,
         },
         {
             "album": "Queen II (Deluxe Remastered Version)",
@@ -221,6 +233,7 @@ def test_spotify_album_fetch_more():
             "year": "1974",
             "playlist_num": 5,
             "spotify_id": "6jXrIu3hWbmJziw34IHIwM",
+            'tempo': 145.124,
         },
         {
             "album": "Queen II (Deluxe Remastered Version)",
@@ -234,6 +247,7 @@ def test_spotify_album_fetch_more():
             "track_url": None,
             "playlist_num": 6,
             "spotify_id": "5dHmGuUeRgp5f93G69tox5",
+            'tempo': 108.544,
         },
         {
             "album": "Queen II (Deluxe Remastered Version)",
@@ -247,6 +261,7 @@ def test_spotify_album_fetch_more():
             "year": "1974",
             "playlist_num": 7,
             "spotify_id": "2KPj0oB7cUuHQ3FuardOII",
+            'tempo': 159.156,
         },
         {
             "album": "Queen II (Deluxe Remastered Version)",
@@ -260,6 +275,7 @@ def test_spotify_album_fetch_more():
             "year": "1974",
             "playlist_num": 8,
             "spotify_id": "34CcBjL9WqEAtnl2i6Hbxa",
+            'tempo': 118.48,
         },
         {
             "album": "Queen II (Deluxe Remastered Version)",
@@ -273,6 +289,7 @@ def test_spotify_album_fetch_more():
             "year": "1974",
             "playlist_num": 9,
             "spotify_id": "1x9ak6LGIazLhfuaSIEkhG",
+            'tempo': 112.623,
         },
         {
             "album": "Queen II (Deluxe Remastered Version)",
@@ -286,6 +303,7 @@ def test_spotify_album_fetch_more():
             "year": "1974",
             "playlist_num": 10,
             "spotify_id": "4CITL18Tos0PscW1amCK4j",
+            'tempo': 145.497,
         },
         {
             "album": "Queen II (Deluxe Remastered Version)",
@@ -299,6 +317,7 @@ def test_spotify_album_fetch_more():
             "year": "1974",
             "playlist_num": 11,
             "spotify_id": "1e9Tt3nKBwRbuaU79kN3dn",
+            'tempo': 126.343,
         },
         {
             "album": "Queen II (Deluxe Remastered Version)",
@@ -312,6 +331,7 @@ def test_spotify_album_fetch_more():
             "year": "1974",
             "playlist_num": 12,
             "spotify_id": "0uHqoDT7J2TYBsJx6m4Tvi",
+            'tempo': 172.274,
         },
         {
             "album": "Queen II (Deluxe Remastered Version)",
@@ -325,6 +345,8 @@ def test_spotify_album_fetch_more():
             "year": "1974",
             "playlist_num": 13,
             "spotify_id": "3MIueGYoNiyBNfi5ukDgAK",
+            'tempo': 146.712,
+
         },
         {
             "album": "Queen II (Deluxe Remastered Version)",
@@ -338,6 +360,7 @@ def test_spotify_album_fetch_more():
             "year": "1974",
             "playlist_num": 14,
             "spotify_id": "34WAOFWdJ83a3YYrDAZTjm",
+            'tempo': 128.873,
         },
         {
             "album": "Queen II (Deluxe Remastered Version)",
@@ -351,6 +374,7 @@ def test_spotify_album_fetch_more():
             "year": "1974",
             "playlist_num": 15,
             "spotify_id": "2AFIPUlApcUwGEgOSDwoBz",
+            'tempo': 122.986,
         },
         {
             "album": "Queen II (Deluxe Remastered Version)",
@@ -364,6 +388,7 @@ def test_spotify_album_fetch_more():
             "year": "1974",
             "playlist_num": 16,
             "spotify_id": "4G4Sf18XkFvNTV5vAxiQyd",
+            'tempo': 169.166,
         },
     ] == songs
     assert (len(songs)) == 16
@@ -387,5 +412,6 @@ def test_spotify_playlist_fetch_local_file():
             "year": "",
             "playlist_num": 1,
             "spotify_id": None,
+            "tempo": None, 
         }
-    ] == songs
+    ] == songs
\ No newline at end of file
diff --git a/tests/test_youtube.py b/tests/test_youtube.py
index d3c29d6..38f0f38 100644
--- a/tests/test_youtube.py
+++ b/tests/test_youtube.py
@@ -28,6 +28,7 @@ def test_download_one_false_skip():
                         "cover": "https://i.scdn.co/image/ab67616d0000b27396d28597a5ae44ab66552183",
                         "genre": "album rock",
                         "spotify_id": "2GpBrAoCwt48fxjgjlzMd4",
+                        'tempo': 74.656,
                     }
                 ],
             }
@@ -83,6 +84,7 @@ def test_download_one_true_skip():
                         "cover": "https://i.scdn.co/image/ab67616d0000b27396d28597a5ae44ab66552183",
                         "genre": "album rock",
                         "spotify_id": "2GpBrAoCwt48fxjgjlzMd4",
+                        'tempo': 74.656,
                     }
                 ],
             }
@@ -120,6 +122,7 @@ def test_download_cover_none():
                         "cover": None,
                         "genre": "classic rock",
                         "spotify_id": "12LhScrlYazmU4vsqpRQNI",
+                        'tempo': 159.15,
                     }
                 ],
             }