@ -347,34 +347,83 @@ class iTunesClient:
return albums
def get_album ( self , album_id : str ) - > Optional [ Dict [ str , Any ] ] :
""" Get album information """
""" Get album information - normalized to Spotify format """
results = self . _lookup ( id = album_id )
for album_data in results :
if album_data . get ( ' wrapperType ' ) == ' collection ' :
return album_data
# Normalize to Spotify-compatible format
image_url = None
if album_data . get ( ' artworkUrl100 ' ) :
image_url = album_data [ ' artworkUrl100 ' ] . replace ( ' 100x100bb ' , ' 600x600bb ' )
# Build images array like Spotify (multiple sizes)
images = [ ]
if image_url :
images = [
{ ' url ' : image_url , ' height ' : 600 , ' width ' : 600 } ,
{ ' url ' : album_data [ ' artworkUrl100 ' ] . replace ( ' 100x100bb ' , ' 300x300bb ' ) , ' height ' : 300 , ' width ' : 300 } ,
{ ' url ' : album_data [ ' artworkUrl100 ' ] , ' height ' : 100 , ' width ' : 100 }
]
# Determine album type
track_count = album_data . get ( ' trackCount ' , 0 )
if track_count < = 3 :
album_type = ' single '
elif track_count < = 6 :
album_type = ' single ' # EP treated as single
else :
album_type = ' album '
return {
' id ' : str ( album_data . get ( ' collectionId ' , ' ' ) ) ,
' name ' : album_data . get ( ' collectionName ' , ' ' ) ,
' images ' : images ,
' artists ' : [ { ' name ' : album_data . get ( ' artistName ' , ' Unknown Artist ' ) , ' id ' : str ( album_data . get ( ' artistId ' , ' ' ) ) } ] ,
' release_date ' : album_data . get ( ' releaseDate ' , ' ' ) [ : 10 ] if album_data . get ( ' releaseDate ' ) else ' ' , # YYYY-MM-DD format
' total_tracks ' : track_count ,
' album_type ' : album_type ,
' external_urls ' : { ' itunes ' : album_data . get ( ' collectionViewUrl ' , ' ' ) } ,
' uri ' : f " itunes:album: { album_data . get ( ' collectionId ' , ' ' ) } " ,
' _source ' : ' itunes ' ,
' _raw_data ' : album_data
}
return None
def get_album_tracks ( self , album_id : str ) - > Optional [ Dict [ str , Any ] ] :
""" Get album tracks with all tracks included """
""" Get album tracks - normalized to Spotify format """
results = self . _lookup ( id = album_id , entity = ' song ' )
if not results :
return None
# First result is usually the album/collection info
# Remaining results are tracks
tracks = [ ]
for item in results :
if item . get ( ' wrapperType ' ) == ' track ' and item . get ( ' kind ' ) == ' song ' :
tracks . append ( item )
# Normalize each track to Spotify-compatible format
normalized_track = {
' id ' : str ( item . get ( ' trackId ' , ' ' ) ) ,
' name ' : item . get ( ' trackName ' , ' ' ) ,
' artists ' : [ { ' name ' : item . get ( ' artistName ' , ' Unknown Artist ' ) } ] , # List of dicts like Spotify
' duration_ms ' : item . get ( ' trackTimeMillis ' , 0 ) ,
' track_number ' : item . get ( ' trackNumber ' , 0 ) ,
' disc_number ' : item . get ( ' discNumber ' , 1 ) ,
' explicit ' : item . get ( ' trackExplicitness ' ) == ' explicit ' ,
' preview_url ' : item . get ( ' previewUrl ' ) ,
' uri ' : f " itunes:track: { item . get ( ' trackId ' , ' ' ) } " , # Synthetic URI
' external_urls ' : { ' itunes ' : item . get ( ' trackViewUrl ' , ' ' ) } ,
' _source ' : ' itunes '
}
tracks . append ( normalized_track )
# Sort by disc and track number
tracks . sort ( key = lambda t : ( t . get ( ' discNumber ' , 1 ) , t . get ( ' trackNumber ' , 0 ) ) )
tracks . sort ( key = lambda t : ( t . get ( ' disc _n umber' , 1 ) , t . get ( ' track _n umber' , 0 ) ) )
logger . info ( f " Retrieved { len ( tracks ) } tracks for album { album_id } " )
return {
' items ' : tracks ,
' total ' : len ( tracks ) ,
@ -399,20 +448,47 @@ class iTunesClient:
def get_artist ( self , artist_id : str ) - > Optional [ Dict [ str , Any ] ] :
"""
Get full artist details from iTunes API .
Get full artist details - normalized to Spotify format .
Args :
artist_id : iTunes artist ID
Returns :
Dictionary with artist data
Dictionary with artist data matching Spotify ' s format
"""
results = self . _lookup ( id = artist_id )
for artist_data in results :
if artist_data . get ( ' wrapperType ' ) == ' artist ' :
return artist_data
# Build images array - iTunes artist search doesn't reliably return images
# but we include the structure for compatibility
images = [ ]
if artist_data . get ( ' artworkUrl100 ' ) :
artwork_base = artist_data [ ' artworkUrl100 ' ]
images = [
{ ' url ' : artwork_base . replace ( ' 100x100bb ' , ' 600x600bb ' ) , ' height ' : 600 , ' width ' : 600 } ,
{ ' url ' : artwork_base . replace ( ' 100x100bb ' , ' 300x300bb ' ) , ' height ' : 300 , ' width ' : 300 } ,
{ ' url ' : artwork_base , ' height ' : 100 , ' width ' : 100 }
]
# Get genre
genres = [ ]
if artist_data . get ( ' primaryGenreName ' ) :
genres = [ artist_data [ ' primaryGenreName ' ] ]
return {
' id ' : str ( artist_data . get ( ' artistId ' , ' ' ) ) ,
' name ' : artist_data . get ( ' artistName ' , ' ' ) ,
' images ' : images ,
' genres ' : genres ,
' popularity ' : 0 , # iTunes doesn't provide this
' followers ' : { ' total ' : 0 } , # iTunes doesn't provide this
' external_urls ' : { ' itunes ' : artist_data . get ( ' artistViewUrl ' , ' ' ) } ,
' uri ' : f " itunes:artist: { artist_data . get ( ' artistId ' , ' ' ) } " ,
' _source ' : ' itunes ' ,
' _raw_data ' : artist_data
}
return None
def get_artist_albums ( self , artist_id : str , album_type : str = ' album,single ' , limit : int = 50 ) - > List [ Album ] :