Package pyechonest :: Module song
[hide private]
[frames] | no frames]

Source Code for Module pyechonest.song

  1  #!/usr/bin/env python 
  2  # encoding: utf-8 
  3   
  4  """ 
  5  Copyright (c) 2010 The Echo Nest. All rights reserved. 
  6  Created by Tyler Williams on 2010-04-25. 
  7   
  8  The Song module loosely covers http://developer.echonest.com/docs/v4/song.html 
  9  Refer to the official api documentation if you are unsure about something. 
 10  """ 
 11  import os 
 12  import util 
 13  from proxies import SongProxy 
 14   
 15  try: 
 16      import json 
 17  except ImportError: 
 18      import simplejson as json 
 19       
20 -class Song(SongProxy):
21 """ 22 A Song object 23 24 Attributes: 25 id (str): Echo Nest Song ID 26 27 title (str): Song Title 28 29 artist_name (str): Artist Name 30 31 artist_id (str): Artist ID 32 33 audio_summary (dict): An Audio Summary dict 34 35 song_hotttnesss (float): A float representing a song's hotttnesss 36 37 artist_hotttnesss (float): A float representing a song's parent artist's hotttnesss 38 39 artist_familiarity (float): A float representing a song's parent artist's familiarity 40 41 artist_location (dict): A dictionary of strings specifying a song's parent artist's location, lattitude and longitude 42 43 Create a song object like so: 44 45 >>> s = song.Song('SOPEXHZ12873FD2AC7') 46 47 """
48 - def __init__(self, id, buckets=None, **kwargs):
49 """ 50 Song class 51 52 Args: 53 id (str): a song ID 54 55 Kwargs: 56 buckets (list): A list of strings specifying which buckets to retrieve 57 58 Returns: 59 A Song object 60 61 Example: 62 63 >>> s = song.Song('SOPEXHZ12873FD2AC7', buckets=['song_hotttnesss', 'artist_hotttnesss']) 64 >>> s.song_hotttnesss 65 0.58602500000000002 66 >>> s.artist_hotttnesss 67 0.80329715999999995 68 >>> 69 70 """ 71 buckets = buckets or [] 72 super(Song, self).__init__(id, buckets, **kwargs)
73
74 - def __repr__(self):
75 return "<%s - %s>" % (self._object_type.encode('utf-8'), self.title.encode('utf-8'))
76
77 - def __str__(self):
78 return self.title.encode('utf-8')
79 80
81 - def get_audio_summary(self, cache=True):
82 """Get an audio summary of a song containing mode, tempo, key, duration, time signature, loudness, danceability, energy, and analysis_url. 83 84 Args: 85 86 Kwargs: 87 cache (bool): A boolean indicating whether or not the cached value should be used (if available). Defaults to True. 88 89 Returns: 90 A dictionary containing mode, tempo, key, duration, time signature, loudness, danceability, energy and analysis_url keys. 91 92 Example: 93 >>> s = song.Song('SOGNMKX12B0B806320') 94 >>> s.audio_summary 95 {u'analysis_url': u'https://echonest-analysis.s3.amazonaws.com:443/TR/TRCPUOG123E85891F2/3/full.json?Signature=wcML1ZKsl%2F2FU4k68euHJcF7Jbc%3D&Expires=1287518562&AWSAccessKeyId=AKIAIAFEHLM3KJ2XMHRA', 96 u'danceability': 0.20964757782725996, 97 u'duration': 472.63301999999999, 98 u'energy': 0.64265230549809549, 99 u'key': 0, 100 u'loudness': -9.6820000000000004, 101 u'mode': 1, 102 u'tempo': 126.99299999999999, 103 u'time_signature': 4} 104 >>> 105 106 """ 107 if not (cache and ('audio_summary' in self.cache)): 108 response = self.get_attribute('profile', bucket='audio_summary') 109 if response['songs'] and 'audio_summary' in response['songs'][0]: 110 self.cache['audio_summary'] = response['songs'][0]['audio_summary'] 111 else: 112 self.cache['audio_summary'] = {} 113 return self.cache['audio_summary']
114 115 audio_summary = property(get_audio_summary) 116
117 - def get_song_hotttnesss(self, cache=True):
118 """Get our numerical description of how hottt a song currently is 119 120 Args: 121 122 Kwargs: 123 cache (bool): A boolean indicating whether or not the cached value should be used (if available). Defaults to True. 124 125 Returns: 126 A float representing hotttnesss. 127 128 Example: 129 >>> s = song.Song('SOLUHKP129F0698D49') 130 >>> s.get_song_hotttnesss() 131 0.57344379999999995 132 >>> s.song_hotttnesss 133 0.57344379999999995 134 >>> 135 136 """ 137 if not (cache and ('song_hotttnesss' in self.cache)): 138 response = self.get_attribute('profile', bucket='song_hotttnesss') 139 self.cache['song_hotttnesss'] = response['songs'][0]['song_hotttnesss'] 140 return self.cache['song_hotttnesss']
141 142 song_hotttnesss = property(get_song_hotttnesss) 143
144 - def get_artist_hotttnesss(self, cache=True):
145 """Get our numerical description of how hottt a song's artist currently is 146 147 Args: 148 149 Kwargs: 150 cache (bool): A boolean indicating whether or not the cached value should be used (if available). Defaults to True. 151 152 Returns: 153 A float representing hotttnesss. 154 155 Example: 156 >>> s = song.Song('SOOLGAZ127F3E1B87C') 157 >>> s.artist_hotttnesss 158 0.45645633000000002 159 >>> s.get_artist_hotttnesss() 160 0.45645633000000002 161 >>> 162 163 """ 164 if not (cache and ('artist_hotttnesss' in self.cache)): 165 response = self.get_attribute('profile', bucket='artist_hotttnesss') 166 self.cache['artist_hotttnesss'] = response['songs'][0]['artist_hotttnesss'] 167 return self.cache['artist_hotttnesss']
168 169 artist_hotttnesss = property(get_artist_hotttnesss) 170
171 - def get_artist_familiarity(self, cache=True):
172 """Get our numerical estimation of how familiar a song's artist currently is to the world 173 174 Args: 175 cache (bool): A boolean indicating whether or not the cached value should be used (if available). Defaults to True. 176 177 Returns: 178 A float representing familiarity. 179 180 Example: 181 >>> s = song.Song('SOQKVPH12A58A7AF4D') 182 >>> s.get_artist_familiarity() 183 0.639626025843539 184 >>> s.artist_familiarity 185 0.639626025843539 186 >>> 187 """ 188 if not (cache and ('artist_familiarity' in self.cache)): 189 response = self.get_attribute('profile', bucket='artist_familiarity') 190 self.cache['artist_familiarity'] = response['songs'][0]['artist_familiarity'] 191 return self.cache['artist_familiarity']
192 193 artist_familiarity = property(get_artist_familiarity) 194
195 - def get_artist_location(self, cache=True):
196 """Get the location of a song's artist. 197 198 Args: 199 cache (bool): A boolean indicating whether or not the cached value should be used (if available). Defaults to True. 200 201 Returns: 202 An artist location object. 203 204 Example: 205 >>> s = song.Song('SOQKVPH12A58A7AF4D') 206 >>> s.artist_location 207 {u'latitude': 34.053489999999996, u'location': u'Los Angeles, CA', u'longitude': -118.24532000000001} 208 >>> 209 210 """ 211 if not (cache and ('artist_location' in self.cache)): 212 response = self.get_attribute('profile', bucket='artist_location') 213 self.cache['artist_location'] = response['songs'][0]['artist_location'] 214 return self.cache['artist_location']
215 216 artist_location = property(get_artist_location) 217
218 - def get_foreign_id(self, idspace='', cache=True):
219 """Get the foreign id for this song for a specific id space 220 221 Args: 222 223 Kwargs: 224 idspace (str): A string indicating the idspace to fetch a foreign id for. 225 226 Returns: 227 A foreign ID string 228 229 Example: 230 231 >>> s = song.Song('SOYRVMR12AF729F8DC') 232 >>> s.get_foreign_id('CAGPXKK12BB06F9DE9') 233 234 >>> 235 """ 236 if not (cache and ('foreign_ids' in self.cache) and filter(lambda d: d.get('catalog') == idspace, self.cache['foreign_ids'])): 237 response = self.get_attribute('profile', bucket=['id:'+idspace]) 238 rsongs = response['songs'] 239 if len(rsongs) == 0: 240 return None 241 foreign_ids = rsongs[0].get("foreign_ids", []) 242 self.cache['foreign_ids'] = self.cache.get('foreign_ids', []) + foreign_ids 243 cval = filter(lambda d: d.get('catalog') == idspace, self.cache.get('foreign_ids')) 244 return cval[0].get('foreign_id') if cval else None
245
246 - def get_tracks(self, catalog, cache=True):
247 """Get the tracks for a song given a catalog. 248 249 Args: 250 catalog (str): a string representing the catalog whose track you want to retrieve. 251 252 Returns: 253 A list of Track dicts. 254 255 Example: 256 >>> s = song.Song('SOWDASQ12A6310F24F') 257 >>> s.get_tracks('7digital')[0] 258 {u'catalog': u'7digital', 259 u'foreign_id': u'7digital:track:8445818', 260 u'id': u'TRJGNNY12903CC625C', 261 u'preview_url': u'http://previews.7digital.com/clips/34/8445818.clip.mp3', 262 u'release_image': u'http://cdn.7static.com/static/img/sleeveart/00/007/628/0000762838_200.jpg'} 263 >>> 264 265 """ 266 if not (cache and ('tracks' in self.cache) and (catalog in [td['catalog'] for td in self.cache['tracks']])): 267 kwargs = { 268 'bucket':['tracks', 'id:%s' % catalog], 269 } 270 271 response = self.get_attribute('profile', **kwargs) 272 if not 'tracks' in self.cache: 273 self.cache['tracks'] = [] 274 # don't blow away the cache for other catalogs 275 potential_tracks = response['songs'][0].get('tracks', []) 276 existing_track_ids = [tr['foreign_id'] for tr in self.cache['tracks']] 277 new_tds = filter(lambda tr: tr['foreign_id'] not in existing_track_ids, potential_tracks) 278 self.cache['tracks'].extend(new_tds) 279 return filter(lambda tr: tr['catalog']==catalog, self.cache['tracks'])
280 281
282 - def get_song_type(self, cache=True):
283 """Get the types of a song. 284 285 Args: 286 cache (boolean): A boolean indicating whether or not the cached value should be used (if available). Defaults to True. 287 288 Returns: 289 A list of strings, each representing a song type: 'christmas', for example. 290 291 Example: 292 >>> s = song.Song('SOQKVPH12A58A7AF4D') 293 >>> s.song_type 294 [u'christmas'] 295 >>> 296 297 """ 298 if not (cache and ('song_type' in self.cache)): 299 response = self.get_attribute('profile', bucket='song_type') 300 self.cache['song_type'] = response['songs'][0]['song_type'] 301 return self.cache['song_type']
302 303 song_type = property(get_song_type)
304
305 -def identify(filename=None, query_obj=None, code=None, artist=None, title=None, release=None, duration=None, genre=None, buckets=None, version=None, codegen_start=0, codegen_duration=30):
306 """Identify a song. 307 308 Args: 309 310 Kwargs: 311 filename (str): The path of the file you want to analyze (requires codegen binary!) 312 313 query_obj (dict or list): A dict or list of dicts containing a 'code' element with an fp code 314 315 code (str): A fingerprinter code 316 317 artist (str): An artist name 318 319 title (str): A song title 320 321 release (str): A release name 322 323 duration (int): A song duration 324 325 genre (str): A string representing the genre 326 327 buckets (list): A list of strings specifying which buckets to retrieve 328 329 version (str): The version of the code generator used to generate the code 330 331 codegen_start (int): The point (in seconds) where the codegen should start 332 333 codegen_duration (int): The duration (in seconds) the codegen should analyze 334 335 Example: 336 >>> qo 337 {'code': 'eJxlldehHSEMRFsChAjlAIL-S_CZvfaXXxAglEaBTen300Qu__lAyoJYhVQdXTvXrmvXdTsKZOqoU1q63QNydBGfOd1cGX3scpb1jEiWRLaPcJureC6RVkXE69jL8pGHjpP48pLI1m7r9oiEyBXvoVv45Q-5IhylYLkIRxGO4rp18ZpEOmpFPopwfJjL0u3WceO3HB1DIvJRnkQeO1PCLIsIjBWEzYaShq4pV9Z0KzDiQ8SbSNuSyBZPOOxIJKR7dauEmXwotxDCqllEAVZlrX6F8Y-IJ0e169i_HQaqslaVtTq1W-1vKeupImzrxWWVI5cPlw-XDxckN-3kyeXDm3jKmqv6PtB1gfH1Eey5qu8qvAuMC4zLfPv1l3aqviylJhytFhF0mzqs6aYpYU04mlqgKWtNjppwNKWubR2FowlHUws0gWmPi668dSHq6rOuPuhqgRcVKKM8s-fZS937nBe23iz3Uctx9607z-kLph1i8YZ8f_TfzLXseBh7nXy9nn1YBAg4Nwjp4AzTL23M_U3Rh0-sdDFtyspNOb1bYeZZqz2Y6TaHmXeuNmfFdTueLuvdsbOU9luvtIkl4vI5F_92PVprM1-sdJ_o9_Guc0b_WimpD_Rt1DFg0sY3wyw08e6jlqhjH3o76naYvzWqhX9rOv15Y7Ww_MIF8dXzw30s_uHO5PPDfUonnzq_NJ8J93mngAkIz5jA29SqxGwwvxQsih-sozX0zVk__RFaf_qyG9hb8dktZZXd4a8-1ljB-c5bllXOe1HqHplzeiN4E7q9ZRdmJuI73gBEJ_HcAxUm74PAVDNL47D6OAfzTHI0mHpXAmY60QNmlqjDfIPzwUDYhVnoXqtvZGrBdMi3ClQUQ8D8rX_1JE_In94CBXER4lrrw0H867ei8x-OVz8c-Osh5plzTOySpKIROmFkbn5xVuK784vTyPpS3OlcSjHpL16saZnm4Bk66hte9sd80Dcj02f7xDVrExjk32cssKXjmflU_SxXmn4Y9Ttued10YM552h5Wtt_WeVR4U6LPWfbIdW31J4JOXnpn4qhH7yE_pdBH9E_sMwbNFr0z0IW5NA8aOZhLmOh3zSVNRZwxiZc5pb8fikGzIf-ampJnCSb3r-ZPfjPuvLm7CY_Vfa_k7SCzdwHNg5mICTSHDxyBWmaOSyLQpPmCSXyF-eL7MHo7zNd668JMb_N-AJJRuMwrX0jNx7a8-Rj5oN6nyWoL-jRv4pu7Ue821TzU3MhvpD9Fo-XI', 338 'code_count': 151, 339 'low_rank': 0, 340 'metadata': {'artist': 'Harmonic 313', 341 'bitrate': 198, 342 'codegen_time': 0.57198400000000005, 343 'decode_time': 0.37954599999999999, 344 'duration': 226, 345 'filename': 'koln.mp3', 346 'genre': 'Electronic', 347 'given_duration': 30, 348 'release': 'When Machines Exceed Human Intelligence', 349 'sample_rate': 44100, 350 'samples_decoded': 661816, 351 'start_offset': 0, 352 'title': 'kln', 353 'version': 3.1499999999999999}, 354 'tag': 0} 355 >>> song.identify(query_obj=qo) 356 [<song - Köln>] 357 >>> 358 359 360 """ 361 post, has_data, data = False, False, False 362 363 if filename: 364 if os.path.exists(filename): 365 query_obj = util.codegen(filename, start=codegen_start, duration=codegen_duration) 366 if query_obj is None: 367 raise Exception("The filename specified: %s could not be decoded." % filename) 368 else: 369 raise Exception("The filename specified: %s does not exist." % filename) 370 if query_obj and not isinstance(query_obj, list): 371 query_obj = [query_obj] 372 373 if filename: 374 # check codegen results from file in case we had a bad result 375 for q in query_obj: 376 if 'error' in q: 377 raise Exception(q['error'] + ": " + q.get('metadata', {}).get('filename', '')) 378 379 if not (filename or query_obj or code): 380 raise Exception("Not enough information to identify song.") 381 382 kwargs = {} 383 if code: 384 has_data = True 385 kwargs['code'] = code 386 if title: 387 kwargs['title'] = title 388 if release: 389 kwargs['release'] = release 390 if duration: 391 kwargs['duration'] = duration 392 if genre: 393 kwargs['genre'] = genre 394 if buckets: 395 kwargs['bucket'] = buckets 396 if version: 397 kwargs['version'] = version 398 399 if query_obj and any(query_obj): 400 has_data = True 401 data = {'query':json.dumps(query_obj)} 402 post = True 403 404 if has_data: 405 result = util.callm("%s/%s" % ('song', 'identify'), kwargs, POST=post, data=data) 406 return [Song(**util.fix(s_dict)) for s_dict in result['response'].get('songs',[])]
407 408
409 -def search(title=None, artist=None, artist_id=None, combined=None, description=None, style=None, mood=None, \ 410 results=None, start=None, max_tempo=None, min_tempo=None, \ 411 max_duration=None, min_duration=None, max_loudness=None, min_loudness=None, \ 412 artist_max_familiarity=None, artist_min_familiarity=None, artist_max_hotttnesss=None, \ 413 artist_min_hotttnesss=None, song_max_hotttnesss=None, song_min_hotttnesss=None, mode=None, \ 414 min_energy=None, max_energy=None, min_danceability=None, max_danceability=None, \ 415 key=None, max_latitude=None, min_latitude=None, max_longitude=None, min_longitude=None, \ 416 sort=None, buckets = None, limit=False, test_new_things=None, rank_type=None, 417 artist_start_year_after=None, artist_start_year_before=None, artist_end_year_after=None, artist_end_year_before=None,song_type=None):
418 """Search for songs by name, description, or constraint. 419 420 Args: 421 422 Kwargs: 423 title (str): the name of a song 424 425 artist (str): the name of an artist 426 427 artist_id (str): the artist_id 428 429 combined (str): the artist name and song title 430 431 description (str): A string describing the artist and song 432 433 style (str): A string describing the style/genre of the artist and song 434 435 mood (str): A string describing the mood of the artist and song 436 437 results (int): An integer number of results to return 438 439 max_tempo (float): The max tempo of song results 440 441 min_tempo (float): The min tempo of song results 442 443 max_duration (float): The max duration of song results 444 445 min_duration (float): The min duration of song results 446 447 max_loudness (float): The max loudness of song results 448 449 min_loudness (float): The min loudness of song results 450 451 artist_max_familiarity (float): A float specifying the max familiarity of artists to search for 452 453 artist_min_familiarity (float): A float specifying the min familiarity of artists to search for 454 455 artist_max_hotttnesss (float): A float specifying the max hotttnesss of artists to search for 456 457 artist_min_hotttnesss (float): A float specifying the max hotttnesss of artists to search for 458 459 song_max_hotttnesss (float): A float specifying the max hotttnesss of songs to search for 460 461 song_min_hotttnesss (float): A float specifying the max hotttnesss of songs to search for 462 463 max_energy (float): The max energy of song results 464 465 min_energy (float): The min energy of song results 466 467 max_dancibility (float): The max dancibility of song results 468 469 min_dancibility (float): The min dancibility of song results 470 471 mode (int): 0 or 1 (minor or major) 472 473 key (int): 0-11 (c, c-sharp, d, e-flat, e, f, f-sharp, g, a-flat, a, b-flat, b) 474 475 max_latitude (float): A float specifying the max latitude of artists to search for 476 477 min_latitude (float): A float specifying the min latitude of artists to search for 478 479 max_longitude (float): A float specifying the max longitude of artists to search for 480 481 min_longitude (float): A float specifying the min longitude of artists to search for 482 483 sort (str): A string indicating an attribute and order for sorting the results 484 485 buckets (list): A list of strings specifying which buckets to retrieve 486 487 limit (bool): A boolean indicating whether or not to limit the results to one of the id spaces specified in buckets 488 489 rank_type (str): A string denoting the desired ranking for description searches, either 'relevance' or 'familiarity 490 491 artist_start_year_before (int): Returned songs's artists will have started recording music before this year. 492 493 artist_start_year_after (int): Returned songs's artists will have started recording music after this year. 494 495 artist_end_year_before (int): Returned songs's artists will have stopped recording music before this year. 496 497 artist_end_year_after (int): Returned songs's artists will have stopped recording music after this year. 498 499 song_type (string): A string or list of strings specifiying the type of song to search for. 500 501 Returns: 502 A list of Song objects 503 504 Example: 505 506 >>> results = song.search(artist='shakira', title='she wolf', buckets=['id:7digital', 'tracks'], limit=True, results=1) 507 >>> results 508 [<song - She Wolf>] 509 >>> results[0].get_tracks('7digital')[0] 510 {u'catalog': u'7digital', 511 u'foreign_id': u'7digital:track:7854109', 512 u'id': u'TRTOBSE12903CACEC4', 513 u'preview_url': u'http://previews.7digital.com/clips/34/7854109.clip.mp3', 514 u'release_image': u'http://cdn.7static.com/static/img/sleeveart/00/007/081/0000708184_200.jpg'} 515 >>> 516 """ 517 518 limit = str(limit).lower() 519 kwargs = locals() 520 kwargs['bucket'] = buckets 521 del kwargs['buckets'] 522 523 result = util.callm("%s/%s" % ('song', 'search'), kwargs) 524 return [Song(**util.fix(s_dict)) for s_dict in result['response']['songs']]
525
526 -def profile(ids=None, track_ids=None, buckets=None, limit=False):
527 """get the profiles for multiple songs at once 528 529 Args: 530 ids (str or list): a song ID or list of song IDs 531 532 Kwargs: 533 buckets (list): A list of strings specifying which buckets to retrieve 534 535 limit (bool): A boolean indicating whether or not to limit the results to one of the id spaces specified in buckets 536 537 Returns: 538 A list of term document dicts 539 540 Example: 541 542 >>> song_ids = [u'SOGNMKX12B0B806320', u'SOLUHKP129F0698D49', u'SOOLGAZ127F3E1B87C', u'SOQKVPH12A58A7AF4D', u'SOHKEEM1288D3ED9F5'] 543 >>> songs = song.profile(song_ids, buckets=['audio_summary']) 544 [<song - chickfactor>, 545 <song - One Step Closer>, 546 <song - And I Am Telling You I'm Not Going (Glee Cast Version)>, 547 <song - In This Temple As In The Hearts Of Man For Whom He Saved The Earth>, 548 <song - Octet>] 549 >>> songs[0].audio_summary 550 {u'analysis_url': u'https://echonest-analysis.s3.amazonaws.com:443/TR/TRKHTDL123E858AC4B/3/full.json?Signature=sE6OwAzg6UvrtiX6nJJW1t7E6YI%3D&Expires=1287585351&AWSAccessKeyId=AKIAIAFEHLM3KJ2XMHRA', 551 u'danceability': None, 552 u'duration': 211.90485000000001, 553 u'energy': None, 554 u'key': 7, 555 u'loudness': -16.736999999999998, 556 u'mode': 1, 557 u'tempo': 94.957999999999998, 558 u'time_signature': 4} 559 >>> 560 561 """ 562 kwargs = {} 563 564 if ids: 565 if not isinstance(ids, list): 566 ids = [ids] 567 kwargs['id'] = ids 568 569 if track_ids: 570 if not isinstance(track_ids, list): 571 track_ids = [track_ids] 572 kwargs['track_id'] = track_ids 573 574 buckets = buckets or [] 575 if buckets: 576 kwargs['bucket'] = buckets 577 578 if limit: 579 kwargs['limit'] = 'true' 580 581 result = util.callm("%s/%s" % ('song', 'profile'), kwargs) 582 return [Song(**util.fix(s_dict)) for s_dict in result['response']['songs']]
583