Пример #1
0
class LastFM(Worker):
  _title = 'lastfm'
  _key = None
  _lfm = None
  # wait this amount of seconds between lookups
  _lookup_threshold = 10
  _last_lookup = None
  _history_filename = None
  _artist_history = {}
  _track_history = {}
  _db = None

  def __init__(self, config, no_cache=False):
    Worker.__init__(self)
    self._key = config.get('LastFM', 'key', None)
    self._lfm = Api(self._key, no_cache=no_cache)
    self._db = DB(config.get('LastFM', 'path',
        join(config.configdir(), 'echonest.db')))
    self._db.execute(
        u"CREATE TABLE IF NOT EXISTS artist_lookup "\
        "(artist text, timestamp integer, "\
        "PRIMARY KEY(artist))")
    self._db.execute(
        u"CREATE TABLE IF NOT EXISTS track_lookup "\
        "(artist text, title text, timestamp integer, "\
        "PRIMARY KEY(artist, title))")
    self._db.commit()

  def get_artist_timestamp(self, artist):
    self._db.execute(
        u"SELECT timestamp FROM artist_lookup "\
        "WHERE artist=?", (artist,))
    row = self._db.fetchone()
    if row: return row[0]
    return 0

  def set_artist_timestamp(self, artist, timestamp):
    self._db.execute(
        u"REPLACE INTO artist_lookup (artist, timestamp) "\
        "VALUES (?, ?)", (artist, timestamp))
    self._db.commit()

  def get_track_timestamp(self, artist, title):
    self._db.execute(
        u"SELECT timestamp FROM track_lookup "\
        "WHERE artist=? and title=?", (artist, title))
    row = self._db.fetchone()
    if row: return row[0]
    return 0

  def set_track_timestamp(self, artist, title, timestamp):
    self._db.execute(
        u"REPLACE INTO track_lookup (artist, title, timestamp) "\
        "VALUES (?, ?, ?)", (artist, title, timestamp))
    self._db.commit()

  def _delay(self):
    if self._last_lookup:
      last_lookup = (datetime.utcnow() - self._last_lookup)
      if last_lookup.seconds < self._lookup_threshold:
        self._logger.debug(u"sleeping %ss" % (
            self._lookup_threshold - last_lookup.seconds))
        sleep(self._lookup_threshold - last_lookup.seconds)
    self._last_lookup = datetime.utcnow()

  def _track(self, artist, title):
    self._delay()
    result = self._lfm.search_track(track=title, artist=artist)
    # for some tracks (e.g. 'after all' from 'the peacocks' lastfm doesn't
    # return the correct track as first result (for 'peacocks' it works?!))
    # so let's try to find the correct track
    for t in result:
      self._logger.info(u'{} {} vs {} {}'.format(artist, title,
          t.artist.name.lower() ,t.name.lower()))
      if t.artist.name.lower() == artist and t.name.lower() == title:
        return t
    if len(result) == 0:
      return None
    return result[0] # return the first

  def _artist(self, artist):
    self._delay()
    result = self._lfm.search_artist(artist=artist)
    for a in result:
      if a.name.lower() == artist:
        return a
    if len(result) == 0:
      return None
    return result[0]

  def similar_tracks(self, callback, artist, title, threshold):
    self.queue_callback(self._similar_tracks, callback, artist, title,
        threshold)

  def similar_tracks_low(self, callback, artist, title, threshold):
    self.queue_callback_low(self._similar_tracks, callback, artist, title,
        threshold)

  def _similar_tracks(self, callback, artist, title, threshold):
    """
      callback(artist, title, sim_artist, sim_title, match, source)
    """
    timestamp = now()
    diff = timestamp - self.get_track_timestamp(artist, title)
    if diff < threshold:
      self._logger.debug(u"similar_tracks[%s-%s]: looked up %d seconds ago" %
          (artist, title, diff))
      return
    self.set_track_timestamp(artist, title, timestamp)

    try:
      self._logger.debug(u"similar_tracks[%s-%s]: lookup" % (artist, title))
      t = self._track(artist, title)
      if t is None:
        self._logger.info(u"similar_tracks[%s-%s]: no result" % (artist, title))
        return
      else:
        p = t.similar
        self._logger.info(u"similar_tracks[%s-%s]: %d result(s)" % (artist,
            title, len(p)))
        for sim in p:
          callback(artist, title, sim.artist.name.lower(), sim.name.lower(),
              sim.stats.match, self._title)
    except URLError, e:
      self._logger.info(e)
      self._logger.info(u"similar_tracks[%s-%s]: no result" % (artist, title))
      return
    except Exception, e:
      self._logger.error(e)
      self._logger.info(u"similar_tracks[%s-%s]: no result" % (artist, title))
      return