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 scrobble(self, media, end_time): if not self.authenticated: return if media.length <= 30: self.l.info("%s is too short to be scrobbled" % media) return time_played = (media.length + time.time() - time.mktime(end_time.timetuple())) if time_played < 240 and time_played < media.length * 0.5: self.l.info("%s has not played long enough" % media) return scrobbler.submit(media.artist, media.title, int(time.mktime(end_time.timetuple()) - media.length), length=int(media.length)) scrobbler.flush()
def _flush(self): sb_success = False for tries in xrange(1, 4): try: sb_success = scrobbler.flush() except Exception, e: logger.error('Flush error: %s', e) log_traceback(e) sb_success = False if sb_success: break logger.error('Flush failed. try %d', tries)
def main(): import sys, os import plistlib from time import strftime # get modus operandi and additional arguments mode = None try: mode = sys.argv[1] except: mode = 'scrobble' if mode not in ('update', 'scrobble'): exit(__doc__) # load internal database dbPath = 'itunesScrobbler.sqlite3' print 'loading internal database...' db = openDatabase(dbPath) # load itunes media library print 'loading iTunes Media Library...' mediaLibPath = os.path.join(os.path.expanduser('~'), 'Music', 'iTunes', 'iTunes Music Library.xml') mediaLib = plistlib.readPlist(mediaLibPath) # synchronize libraries tracks = mediaLib['Tracks'] tracksToScrobble = [] print 'synchronizing databases...' for trackId, track in tracks.iteritems(): try: if mode == 'update': updateDatabaseWithTrack(db, track) # gather scrobble data elif mode == 'scrobble': try: count = playCountDiffWithDatabaseForTrack(db, track) except ValueError, e: print 'Warning!', e.message else: if count: tracksToScrobble.append((count, track)) except KeyError: pass # process gathered information if mode == 'update': print 'done! - internal database updated.' elif mode == 'scrobble': if not tracksToScrobble: print 'done! - nothing changed; nothing to scrobble.' else: # sort tracks tracksToScrobble.sort(key=lambda t:(t[1]['Artist']+t[1]['Name']).lower()) # fix tracks' play time fauxPlayTime = datetime.now() # could be any datetime instance, like datetime(2012,1,1) patchAll = False for count, track in tracksToScrobble: if patchAll or 'Play Date UTC' not in track: track['Play Date UTC'] = fauxPlayTime trackLength = track.get('Total Time', 0) // 1000 fauxPlayTime = dateTimeWithOffsetFromDateTime(-count * trackLength, fauxPlayTime) # print what we want to scrobble print print 'This is what we\'ll send to last.fm:' print for count, track in tracksToScrobble: playDate = strftime('%Y-%m-%d %H:%M', track['Play Date UTC'].timetuple()) print count, ('x %(Artist)s - %(Name)s' % track).encode('unicode-escape'), ('(%s)' % playDate) print okay = raw_input('is this okay with you? (y/N) ') if okay != 'y': print 'alright, let\'s forget about it.' else: # try to load username and password and log in with open('.itunesScrobbler') as fd: username = fd.readline().rstrip('\r\n') password = fd.readline().rstrip('\r\n') print 'trying to log in to last.fm...' scrobbler.login(username, password) # scrobble! print 'scrobble ...', for count, track in tracksToScrobble: trackDescription = ('%(Name)s by %(Artist)s' % track).encode('unicode-escape') if len(trackDescription) > 69: trackDescription = trackDescription[:67] + '..' print '\rscrobble', trackDescription, for i in xrange(count): # need to compensate; we only know when the track was *last* played if i is 0: fixedTrack = track else: trackLength = track.get('Total Time', 0) // 1000 fixedTrack = track.copy() fixedTrack['Play Date UTC'] = dateTimeWithOffsetFromDateTime(-i * trackLength, track['Play Date UTC']) # send the fixed track information off to last.fm if not scrobble(fixedTrack): print raise scrobbler.PostError('could not scrobble!') updateDatabaseWithTrack(db, track) if not scrobbler.flush(): # Damn! Something went wrong right at the end. # We could roll back our internal database now, which could lead to duplicate scrobbles, # or we could just ignore this error, which could/will lead to tracks not being scrobbled at all; # both scenarios suck donkey dick! print 'b0rked hard; so sorry :f - you just lost some scrobbles due to bad caching.' else: print print 'all done! :)' db.commit() db.close()
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)