Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 43 additions & 18 deletions beetsplug/spotify.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import re
import time
import webbrowser
from typing import TYPE_CHECKING, Any, Literal, Sequence, Union
from typing import TYPE_CHECKING, Any, Literal, Sequence, Tuple, Union

import confuse
import requests
Expand Down Expand Up @@ -293,7 +293,13 @@ def album_for_id(self, album_id: str) -> AlbumInfo | None:
if not (spotify_id := self._extract_id(album_id)):
return None

album_data = self._handle_response("get", self.album_url + spotify_id)
try:
album_data = self._handle_response(
"get", self.album_url + spotify_id
)
except APIError:
return None

Comment on lines +296 to +302
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach introduces a lot of duplication in the code. I think a cleaner approach would be to never raise APIError in the first place.

See _handle_response method - I'd suggest removing all lines that raise APIError and returning None instead.

if album_data["name"] == "":
self._log.debug("Album removed from Spotify: {}", album_id)
return None
Expand Down Expand Up @@ -324,7 +330,10 @@ def album_for_id(self, album_id: str) -> AlbumInfo | None:
tracks_data = album_data["tracks"]
tracks_items = tracks_data["items"]
while tracks_data["next"]:
tracks_data = self._handle_response("get", tracks_data["next"])
try:
tracks_data = self._handle_response("get", tracks_data["next"])
except APIError:
return None
tracks_items.extend(tracks_data["items"])

tracks = []
Expand Down Expand Up @@ -397,22 +406,29 @@ def track_for_id(self, track_id: str) -> None | TrackInfo:
self._log.debug("Invalid Spotify ID: {}", track_id)
return None

if not (
track_data := self._handle_response(
"get", f"{self.track_url}{spotify_id}"
)
):
self._log.debug("Track not found: {}", track_id)
try:
if not (
track_data := self._handle_response(
"get", f"{self.track_url}{spotify_id}"
)
):
self._log.debug("Track not found: {}", track_id)
return None
except APIError:
return None

track = self._get_track(track_data)

# Get album's tracks to set `track.index` (position on the entire
# release) and `track.medium_total` (total number of tracks on
# the track's disc).
album_data = self._handle_response(
"get", self.album_url + track_data["album"]["id"]
)
try:
# Get album's tracks to set `track.index` (position on the entire
# release) and `track.medium_total` (total number of tracks on
# the track's disc).
album_data = self._handle_response(
"get", self.album_url + track_data["album"]["id"]
)
except APIError:
return None

medium_total = 0
for i, track_data in enumerate(album_data["tracks"]["items"], start=1):
if track_data["disc_number"] == track.medium:
Expand Down Expand Up @@ -707,7 +723,10 @@ def _fetch_info(self, items, write, force):
self._log.debug("No track_id present for: {}", item)
continue

popularity, isrc, ean, upc = self.track_info(spotify_track_id)
maybe_track_info = self.track_info(spotify_track_id)
if maybe_track_info is None:
continue
popularity, isrc, ean, upc = maybe_track_info
item["spotify_track_popularity"] = popularity
item["isrc"] = isrc
item["ean"] = ean
Expand All @@ -726,9 +745,15 @@ def _fetch_info(self, items, write, force):
if write:
item.try_write()

def track_info(self, track_id: str):
def track_info(
self, track_id: str
) -> Tuple[Any | None, Any | None, Any | None, Any | None] | None:
Copy link
Preview

Copilot AI Jul 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The return type annotation is verbose and unclear. Consider creating a type alias or using a more descriptive approach, such as TrackInfoTuple | None where TrackInfoTuple = Tuple[Optional[Any], Optional[Any], Optional[Any], Optional[Any]].

Suggested change
) -> Tuple[Any | None, Any | None, Any | None, Any | None] | None:
) -> TrackInfoTuple | None:

Copilot uses AI. Check for mistakes.

"""Fetch a track's popularity and external IDs using its Spotify ID."""
track_data = self._handle_response("get", self.track_url + track_id)
try:
track_data = self._handle_response("get", self.track_url + track_id)
except APIError:
return None

external_ids = track_data.get("external_ids", {})
popularity = track_data.get("popularity")
self._log.debug(
Expand Down
Loading