示例#1
0
文件: lastfm.py 项目: Jille/maried
 def run(self):
         self.cond.acquire()
         while self.running:
                 playing = self.desk.get_playing()[0]
                 self.cond.release()
                 try:
                         scrobbler.now_playing(playing.artist,
                                               playing.title,
                                               length=int(playing.length))
                 except URLError as e:
                         self.l.exception("Error while scrobbler.now_playing")
                 self.cond.acquire()
                 while len(self.queue) > 0:
                         m, r, end_time = self.queue.pop()
                         if not r is None:
                                 self.cond.release()
                                 try:
                                         self.scrobble(m, end_time)
                                 except URLError as e:
                                         self.l.exception("Error "+
                                                 "while scrobbler.submit")
                                 self.cond.acquire()
                 if not self.running:
                         break
                 self.cond.wait()
         self.cond.release()
示例#2
0
    def run(self):
        # well this is just fugly.  call it "experimental"
        while Config.running:
            try:
                scrobble_item = self.queue.get(0)
                try:
                    song = scrobble_item.song
                    type = scrobble_item.type
                    error = scrobble_item.error
                    etime = scrobble_item.etime

                    try:
                        (tracknumber, artist, album, track) = [escape(item) for item in song.tags]
                    except ValueError:
                        log.info("skipping scrobble for {} (bad tags)".format(song.path))
                        continue

                    if type == NOW_PLAYING:
                        log.debug(u"scrobbling now playing %s %s %s" %
                                (artist, track, album))
                        self.login()
                        scrobbler.now_playing(
                                artist,
                                track)
                        # now_playing auto flushes, apparently.  don't call
                        # flush here or it will throw an exception, which is not
                        # what we want.
                    elif type == PLAYED:
                        # See: http://exhuma.wicked.lu/projects/python/scrobbler/api/public/scrobbler-module.html#login
                        # if mimetype is wrong, length == 0
                        if song.length < 30: log.warn(u"song length %s" % song.length)

                        # wait 60 seconds before re-trying
                        # submission
                        if error:
                            if (time.time() - etime) < 60:
                                break
                        log.debug(u"scrobbling played %s %s %s %s" %
                                (artist, track, album, song.length))
                        self.login()
                        scrobbler.submit(
                            artist,
                            track,
                            int(time.mktime(datetime.datetime.now().timetuple())),
                            source=escape(u'P'),
                            length=song.length,
                            album=album)
                        scrobbler.flush()
                except Exception as e:
                    log.exception(u"scrobble error: %s" % e)
                    # put it back
                    scrobble_item.error = True
                    scrobble_item.etime = time.time()
                    self.queue.put(scrobble_item)
            except Queue.Empty:
                pass

            # AS API enforced limit -- do not change.
            time.sleep(10)
示例#3
0
class CmuScrobbler(object):

    CLIENTID = ('cmu', '1.0')

    def __init__(self):
        self.data = {}
        self.status = None
        self.status_content = None
        if self.status is None:
            self.status = '/tmp/cmuscrobbler-%s.status' % os.environ['USER']

    def get_status(self):
        logger.debug('Main Process initialiated')
        self.read_arguments()
        self.read_file()

        now = int(time.mktime(datetime.now().timetuple()))
        """
            The track must be submitted once it has finished playing. Whether
            it has finished playing naturally or has been manually stopped by
            the user is irrelevant.
        """
        if self.status_content is not None:
            if self.status_content['file'] == self.data['file']:
                if self.data['status'] != u'playing' and os.path.exists(
                        self.status):
                    logger.info('Not playing. Removing statusfile')
                    os.remove(self.status)
            self.submit()

        now_playing = None
        if self.data['status'] == u'playing':
            self.write_file(now)
            now_playing = {
                'artist': self.data['artist'],
                'title': self.data['title'],
                'album': self.data['album'],
                'length': self.data['duration'],
                'trackno': self.data['tracknumber'],
                'file': self.data['file'],
            }
        else:
            if os.path.exists(self.status):
                os.remove(self.status)

        self.commit(now_playing)
        logger.debug('Main Process finished.')

    def read_arguments(self):
        for k, v in zip(sys.argv[1::2], sys.argv[2::2]):
            try:
                self.data[k] = v.decode('utf-8')
            except UnicodeDecodeError:
                # if utf-8 fails try with latin1.
                # FIXME: consider making this configurable
                self.data[k] = v.decode('latin1')
        # self.data will be a hash like this:
        """
        {'album': u'Basics',
         'artist': u'Funny van Dannen',
         'duration': u'147',
         'file': u'/home/david/m/m/+DB/Funny_van_Dannen/Basics/01-Guten_Abend.mp3',
         'status': u'stopped',
         'title': u'Guten Abend',
         'tracknumber': u'1'}
        """
        for field in [
                'artist', 'title', 'album', 'tracknumber', 'status', 'file'
        ]:
            if not self.data.has_key(field):
                self.data[field] = u''
        logger.debug('Got Arguments: %s', self.data)

    def read_file(self):
        if not os.path.exists(self.status):
            return
        fo = open(self.status, "r")
        content = fo.read()
        fo.close()
        (file, artist, title, album, trackno, start,
         duration) = content.split("\t")
        duration = duration.strip()
        self.status_content = {
            'file': unquote(file).decode('utf-8'),
            'artist': unquote(artist).decode('utf-8'),
            'title': unquote(title).decode('utf-8'),
            'album': unquote(album).decode('utf-8'),
            'trackno': trackno.decode('utf-8'),
            'start': int(start),
            'duration': int(duration)
        }
        logger.debug('Got statusinfo: %s', self.status_content)

    def write_file(self, start):
        to_write = '\t'.join((quote(self.data['file'].encode('utf-8')),
                              quote(self.data['artist'].encode('utf-8')),
                              quote(self.data['title'].encode('utf-8')),
                              quote(self.data['album'].encode('utf-8')),
                              self.data['tracknumber'].encode('utf-8'),
                              str(start).encode('utf-8'),
                              self.data['duration'].encode('utf-8')))
        fo = open(self.status, "w")
        fo.write(to_write)
        fo.write('\n')
        fo.close()
        logger.info('Wrote statusfile.')
        logger.debug('Content: %s', to_write)

    def submit(self):
        #submits track if it got played long enough
        if self.status_content['artist'] == u'' or self.status_content[
                'title'] == u'':
            logger.info('Not submitting because artist or title is empty')
            return

        now = int(time.mktime(datetime.now().timetuple()))
        """ The track must have been played for a duration of at least 240
            seconds *or* half the track's total length, whichever comes first.
            Skipping or pausing the track is irrelevant as long as the
            appropriate amount has been played.

            The total playback time for the track must be more than 30 seconds.
            Do not submit tracks shorter than this.
        """
        if (self.status_content['duration'] <= 30
                or now - self.status_content['start'] < min(
                    int(round(self.status_content['duration'] / 2.0)), 240)):
            logger.info('Not submitting because didn\'t listen to long enough')
            return

        to_write = '\t'.join(
            (quote(self.status_content['file'].encode('utf-8')),
             quote(self.status_content['artist'].encode('utf-8')),
             quote(self.status_content['title'].encode('utf-8')),
             str(now).encode('utf-8'), 'P',
             str(self.status_content['duration']).encode('utf-8'),
             quote(self.status_content['album'].encode('utf-8')),
             self.status_content['trackno']))
        for config in scrobbler_config:
            cachefile = config.get('cachefile')
            if cachefile is None:
                raise Exception('Broken config! Cachefile missing.')
            fp = file(cachefile, 'a')
            fp.write(to_write)
            fp.write('\n')
            fp.close()
            logger.info('Attached submit to cachefile %s' % cachefile)
        logger.debug('Content: %s', to_write)

    def commit(self, now_playing=None):
        for config in scrobbler_config:
            pidfile = config.get('pidfile')
            password = config.get('password')
            scrobbler_url = config.get('scrobbler_url')
            username = config.get('username')
            cachefile = config.get('cachefile')
            if ((pidfile is None) or (password is None)
                    or (scrobbler_url is None) or (username is None)
                    or (cachefile is None)):
                raise Exception('Broken config! Something is missing.')

            if os.path.exists(pidfile):
                "commit already running maybe waiting for network timeout or something, doing nothing"
                logger.info('Commit already running. Not commiting. (%s)' %
                            pidfile)
                continue

            logger.debug('Forking')
            if not os.fork():
                os.setsid()
                pid = os.fork()
                if pid:
                    fo = file(pidfile, 'w')
                    fo.write(str(pid))
                    fo.close()
                    logger.debug('Wrote pidfile')
                    sys.exit(0)
                else:
                    try:
                        self._real_commit(now_playing, cachefile, username,
                                          password, scrobbler_url)
                    finally:
                        if os.path.exists(pidfile):
                            os.remove(pidfile)
                            logger.debug('Deleted pidfile')

    def _real_commit(self, now_playing, cachefile, username, password,
                     scrobbler_url):
        """this is quite ugly spaghetti code. maybe we could make this a little bit more tidy?"""
        logger.info('Begin scrobbling to %s', scrobbler_url)
        if (not do_now_playing):
            logger.debug('Now playing disabled')
            now_playing = None
        success = False
        tosubmit = set()
        tosubmitted = set()
        cache_count = 0
        retry_sleep = None
        retry_count = 0
        while not success:
            if retry_sleep is None:
                retry_sleep = 60
            else:
                retry_count = retry_count + 1
                if retry_count > 7:
                    logger.info('Giving up scrobbling to %s', scrobbler_url)
                    break
                logger.info('Sleeping %d minute(s)', retry_sleep / 60)
                time.sleep(retry_sleep)
                retry_sleep = min(retry_sleep * 2, 120 * 60)
            #handshake phase
            logger.debug('Handshake')
            try:
                scrobbler.login(username,
                                password,
                                hashpw=False,
                                client=CmuScrobbler.CLIENTID,
                                url=scrobbler_url)
            except Exception, e:
                logger.error('Handshake with %s failed: %s', scrobbler_url, e)
                log_traceback(e)
                continue

            #submit phase
            if os.path.exists(cachefile):
                logger.info('Scrobbling songs to %s', scrobbler_url)
                (_, _, _, _, _, _, _, _, mtime, _) = os.stat(cachefile)
                fo = file(cachefile, 'r')
                line = fo.readline()
                while len(line) > 0:
                    (path, artist, track, playtime, source, length, album,
                     trackno) = line.split('\t')
                    trackno = trackno.strip()
                    mbid = get_mbid(unquote(path).decode('utf-8'))
                    tosubmit.add((playtime, artist, track, source, length,
                                  album, trackno, mbid))
                    line = fo.readline()
                fo.close()
                logger.info('Read %d songs from cachefile %s', len(tosubmit),
                            cachefile)

                logger.debug('Sorting songlist')
                submitlist = list(tosubmit)
                submitlist.sort(key=lambda x: int(x[0]))
                retry = False
                for (playtime, artist, track, source, length, album, trackno,
                     mbid) in submitlist:
                    if (playtime, artist, track, source, length, album,
                            trackno, mbid) in tosubmitted:
                        logger.debug(
                            'Track already submitted or in cache: %s - %s',
                            unquote(artist), unquote(track))
                        continue
                    if cache_count >= 3:
                        logger.info('Flushing. cache_count=%d', cache_count)
                        if self._flush():
                            logger.info('Flush successful.')
                            retry_sleep = None
                            cache_count = 0
                        else:
                            retry = True
                            break
                    sb_success = False
                    for tries in xrange(1, 4):
                        logger.debug(
                            'Try to submit: %s, %s, playtime=%d, source=%s, length=%s, album=%s, trackno=%s, mbid=%s',
                            unquote(artist), unquote(track), int(playtime),
                            source, length, unquote(album), trackno, mbid)
                        try:
                            sb_success = scrobbler.submit(
                                unquote(artist).decode('utf-8'),
                                unquote(track).decode('utf-8'),
                                int(playtime),
                                source=source.decode('utf-8'),
                                length=length.decode('utf-8'),
                                album=unquote(album).decode('utf-8'),
                                trackno=trackno.decode('utf-8'),
                                mbid=mbid,
                            )
                        except Exception, e:
                            logger.error('Submit error: %s', e)
                            log_traceback(e)
                            sb_success = False
                        if sb_success:
                            tosubmitted.add((playtime, artist, track, source,
                                             length, album, trackno, mbid))
                            cache_count += 1
                            logger.info('Submitted. cache_count=%d: %s - %s',
                                        cache_count, unquote(artist),
                                        unquote(track))
                            break
                        logger.error('Submit failed. Try %d', tries)
                    if not sb_success:
                        retry = True
                        break
                    if cache_count >= 3:
                        logger.info('Flushing. cache_count=%d', cache_count)
                        if self._flush():
                            logger.info('Flush successful.')
                            retry_sleep = None
                            cache_count = 0
                        else:
                            retry = True
                            break
                if retry:
                    logger.error('Restaring')
                    continue

                if cache_count > 0:
                    logger.info('Cache not empty: flushing')
                    if self._flush():
                        logger.info('Flush successful.')
                        retry_sleep = None
                        cache_count = 0
                    else:
                        logger.error('Restarting')
                        continue

                (_, _, _, _, _, _, _, _, newmtime, _) = os.stat(cachefile)
                if newmtime != mtime:
                    logger.info(
                        'Cachefile changed since we started. Restarting')
                    continue
                logger.info('Scrobbled all Songs, removing cachefile')
                os.remove(cachefile)

            #now playing phase
            if now_playing is not None and not now_playing[
                    'artist'] == u'' and not now_playing['title'] == u'':
                logger.info('Sending \'Now playing\' to %s', scrobbler_url)
                mbid = get_mbid(now_playing['file'])
                np_success = False
                for tries in xrange(1, 4):
                    try:
                        if len(now_playing['trackno']) == 0:
                            now_playing['trackno'] = '0'
                        np_success = scrobbler.now_playing(
                            now_playing['artist'],
                            now_playing['title'],
                            album=now_playing['album'],
                            length=int(now_playing['length']),
                            trackno=int(now_playing['trackno']),
                            mbid=mbid,
                        )
                    except Exception, e:
                        logger.error('now_playing threw an exception: %s' % e)
                        log_traceback(e)
                        break
                    if np_success:
                        logger.info('\'Now playing\' submitted successfully')
                        retry_sleep = None
                        now_playing = None
                        break
                    logger.error('Sending \'Now playing\' failed. Try %d',
                                 tries)
                if not np_success:
                    logger.error(
                        'Submitting \'Now playing\' failed. Giving up.')
示例#4
0
                logger.info('Scrobbled all Songs, removing cachefile')
                os.remove(cachefile)

            #now playing phase
            if now_playing is not None and not now_playing['artist'] == u'' and not now_playing['title'] == u'':
                logger.info('Sending \'Now playing\' to %s', scrobbler_url)
                mbid = get_mbid(now_playing['file'])
                np_success = False
                for tries in xrange(1, 4):
                    try:
                        if len(now_playing['trackno']) == 0:
                            now_playing['trackno'] = '0'
                        np_success = scrobbler.now_playing(
                            now_playing['artist'],
                            now_playing['title'],
                            album=now_playing['album'],
                            length=int(now_playing['length']),
                            trackno=int(now_playing['trackno']),
                            mbid=mbid,
                        )
                    except Exception, e:
                        logger.error('now_playing threw an exception: %s' % e)
                        log_traceback(e)
                        break
                    if np_success:
                        logger.info('\'Now playing\' submitted successfully')
                        retry_sleep = None
                        now_playing = None
                        break
                    logger.error('Sending \'Now playing\' failed. Try %d', tries)
                if not np_success:
                    logger.error('Submitting \'Now playing\' failed. Giving up.')
                os.remove(cachefile)

            #now playing phase
            if now_playing is not None and not now_playing[
                    'artist'] == u'' and not now_playing['title'] == u'':
                logger.info('Sending \'Now playing\' to %s', scrobbler_url)
                mbid = get_mbid(now_playing['file'])
                np_success = False
                for tries in xrange(1, 4):
                    try:
                        if len(now_playing['trackno']) == 0:
                            now_playing['trackno'] = '0'
                        np_success = scrobbler.now_playing(
                            now_playing['artist'],
                            now_playing['title'],
                            album=now_playing['album'],
                            length=int(now_playing['length']),
                            trackno=int(now_playing['trackno']),
                            mbid=mbid,
                        )
                    except Exception, e:
                        logger.error('now_playing threw an exception: %s' % e)
                        log_traceback(e)
                        break
                    if np_success:
                        logger.info('\'Now playing\' submitted successfully')
                        retry_sleep = None
                        now_playing = None
                        break
                    logger.error('Sending \'Now playing\' failed. Try %d',
                                 tries)
                if not np_success:
示例#6
0
    def run(self):
        # well this is just fugly.  call it "experimental"
        while Config.running:
            try:
                scrobble_item = self.queue.get(0)
                try:
                    song = scrobble_item.song
                    type = scrobble_item.type
                    error = scrobble_item.error
                    etime = scrobble_item.etime

                    try:
                        (tracknumber, artist, album,
                         track) = [escape(item) for item in song.tags]
                    except ValueError:
                        log.info("skipping scrobble for {} (bad tags)".format(
                            song.path))
                        continue

                    if type == NOW_PLAYING:
                        log.debug(u"scrobbling now playing %s %s %s" %
                                  (artist, track, album))
                        self.login()
                        scrobbler.now_playing(artist, track)
                        # now_playing auto flushes, apparently.  don't call
                        # flush here or it will throw an exception, which is not
                        # what we want.
                    elif type == PLAYED:
                        # See: http://exhuma.wicked.lu/projects/python/scrobbler/api/public/scrobbler-module.html#login
                        # if mimetype is wrong, length == 0
                        if song.length < 30:
                            log.warn(u"song length %s" % song.length)

                        # wait 60 seconds before re-trying
                        # submission
                        if error:
                            if (time.time() - etime) < 60:
                                break
                        log.debug(u"scrobbling played %s %s %s %s" %
                                  (artist, track, album, song.length))
                        self.login()
                        scrobbler.submit(
                            artist,
                            track,
                            int(
                                time.mktime(
                                    datetime.datetime.now().timetuple())),
                            source=escape(u'P'),
                            length=song.length,
                            album=album)
                        scrobbler.flush()
                except Exception as e:
                    log.exception(u"scrobble error: %s" % e)
                    # put it back
                    scrobble_item.error = True
                    scrobble_item.etime = time.time()
                    self.queue.put(scrobble_item)
            except Queue.Empty:
                pass

            # AS API enforced limit -- do not change.
            time.sleep(10)
def lastfm_now_playing():
    try:
        if not scrobbler.SESSION_ID: lastfm_login()
        scrobbler.now_playing(playing['artist'], playing['track'], length = playing['length'])
    except: pass