def get_time(self): self._last_hb_ts = time.time() * MICROSECONDS content = utils.unserialize_response(request.get_data()) command_epoch = content['command_epoch'] if "term" in content and int(content["term"]) > self._master_term: self._master_term = int(content["term"]) if "ip" in content and (self._master_ip == None or self._master_ip != content["ip"]): self._master_ip = content["ip"] curr_time = time.time() * MICROSECONDS curr_micros = int(round(curr_time)) if self._in_recovery: failover_resp = utils.format_rpc_response(False, HB, {}, \ msg='Replica in recovery mode', \ command_epoch=command_epoch) return utils.serialize_response(failover_resp) with self._pygame_lock: replica_playing = pygame.mixer.music.get_busy() # Song has finished playing if not replica_playing: self._current_song = None resp = utils.format_rpc_response(True, HB, \ {'time' : curr_micros, \ 'replica_playing': replica_playing}, command_epoch = command_epoch) return utils.serialize_response(resp)
def pause(self): self._last_hb_ts = time.time() * MICROSECONDS content = utils.unserialize_response(request.get_data()) command_epoch = content['command_epoch'] master_stop_micros = content['stop_time'] if self._in_recovery: failover_resp = utils.format_rpc_response(False, PAUSE, {}, \ msg='Replica in recovery mode', \ command_epoch=command_epoch) return utils.serialize_response(failover_resp) # Wait till appointed stop time, allowed to be less precise than play # (if vs while loop) curr_replica_micros = int(round(time.time() * MICROSECONDS)) if (curr_replica_micros < master_stop_micros): time.sleep((master_stop_micros - curr_replica_micros) / float(MICROSECONDS)) with self._pygame_lock: pygame.mixer.music.pause() # return offset from start of song replica_offset = int(round(pygame.mixer.music.get_pos())) replica_micros = int(round(time.time() * MICROSECONDS)) resp = \ utils.format_rpc_response(True, PAUSE, \ {'time': replica_micros, \ 'offset': replica_offset}, \ command_epoch=command_epoch) return utils.serialize_response(resp)
def recover_replica(self): data = utils.unserialize_response(request.get_data()) replica_song_hashes = data['song_hashes'] with open(PLAYLIST_STATE_FILE, 'r') as f: data = utils.unserialize_response(f.read()) print data master_queue = data['playlist'] current_song = data['current_song'] missing_songs = {} print master_queue for song_hash in master_queue: print 'song_hash ' + str(song_hash) music_path = utils.get_music_path(song_hash) if (os.path.exists(music_path)) and ( replica_song_hashes.count(song_hash + EXT) == 0): with open(music_path, 'r') as f: song_bytes = f.read() missing_songs[song_hash + EXT] = song_bytes if current_song != None: music_path = utils.get_music_path(current_song) if (os.path.exists(current_song + EXT)) and ( replica_song_hashes.count(current_song + EXT) == 0): with open(music_path, 'r') as f: song_bytes = f.read() missing_songs[current_song + EXT] = song_bytes resp = utils.format_rpc_response( True, RECOVER, { 'songs': missing_songs, 'master_queue': master_queue, 'current_song': current_song }) return utils.serialize_response(resp)
def recover_replica(self): data = utils.unserialize_response(request.get_data()) replica_song_hashes = data['song_hashes'] with open(PLAYLIST_STATE_FILE, 'r') as f: data = utils.unserialize_response(f.read()) print data master_queue = data['playlist'] current_song = data['current_song'] missing_songs = {} print master_queue for song_hash in master_queue: print 'song_hash ' + str(song_hash) music_path = utils.get_music_path(song_hash) if (os.path.exists(music_path)) and (replica_song_hashes.count(song_hash + EXT) == 0): with open(music_path, 'r') as f: song_bytes = f.read() missing_songs[song_hash + EXT] = song_bytes if current_song != None: music_path = utils.get_music_path(current_song) if (os.path.exists(current_song + EXT)) and (replica_song_hashes.count(current_song + EXT) == 0): with open(music_path, 'r') as f: song_bytes = f.read() missing_songs[current_song + EXT] = song_bytes resp = utils.format_rpc_response(True, RECOVER, {'songs': missing_songs, 'master_queue': master_queue, 'current_song': current_song}) return utils.serialize_response(resp)
def check_song(self, song_hash): self._last_hb_ts = time.time() * MICROSECONDS content = utils.unserialize_response(request.get_data()) command_epoch = content['command_epoch'] if self._in_recovery: failover_resp = utils.format_rpc_response(False, CHECK, {}, \ msg='Replica in recovery mode', \ command_epoch=command_epoch) return utils.serialize_response(failover_resp) if song_hash in self._song_hashes: resp = utils.format_rpc_response(True, CHECK, \ {'has_song': True, 'ip': self._ip}, \ command_epoch = command_epoch) else: resp = utils.format_rpc_response(True, CHECK, {'ip': self._ip}, \ command_epoch = command_epoch) return utils.serialize_response(resp)
def enqueue_song(self, song_hash): self._last_hb_ts = time.time() * MICROSECONDS content = utils.unserialize_response(request.get_data()) command_epoch = content['command_epoch'] master_post_hash = content['hashed_post_playlist'] master_current_song = content['current_song'] failover_resp = utils.format_rpc_response(False, ENQUEUE, {}, \ msg='Replica in recovery mode', \ command_epoch=command_epoch) if self._in_recovery: return utils.serialize_response(failover_resp) print "In Enqueue" print "Enqueue: " + str(self._current_song) replica_pre_hash = utils.hash_string(pickle.dumps( self._playlist_queue)) if replica_pre_hash == master_post_hash and self._current_song == master_current_song: print "Already Performed Operation in Enqueue" repeat_resp = utils.format_rpc_response(False, ENQUEUE, {}, \ msg='Already performed operation', \ command_epoch=command_epoch) return utils.serialize_response(repeat_resp) # Do enqueue, check for failover mode song_not_exist = not os.path.exists(utils.get_music_path(song_hash)) self._playlist_queue.append(song_hash) replica_post_hash = utils.hash_string( pickle.dumps(self._playlist_queue)) inconsistent_queue = master_post_hash != replica_post_hash or \ master_current_song != self._current_song print "queue hashes match: " + str( master_post_hash == replica_post_hash) print "current song matches: " + str( master_current_song) + " == " + str(self._current_song) replica_failover = song_not_exist or inconsistent_queue if replica_failover: self._in_recovery = True return utils.serialize_response(failover_resp) master_time = content['time'] self._master_timestamp = master_time resp = utils.format_rpc_response(True, ENQUEUE, {'enqueued': True}, \ command_epoch=command_epoch) print str(resp) return utils.serialize_response(resp)
def dequeue_song(self): self._last_hb_ts = time.time() * MICROSECONDS content = utils.unserialize_response(request.get_data()) command_epoch = content['command_epoch'] master_post_hash = content['hashed_post_playlist'] master_current_song = content['current_song'] failover_resp = utils.format_rpc_response(False, DEQUEUE, {}, \ msg='Replica in recovery mode', \ command_epoch=command_epoch) if self._in_recovery: return utils.serialize_response(failover_resp) print "In Dequeue" replica_pre_hash = utils.hash_string(pickle.dumps( self._playlist_queue)) if replica_pre_hash == master_post_hash and self._current_song == master_current_song and self._current_song != None: repeat_resp = utils.format_rpc_response(False, DEQUEUE, {}, \ msg='Already performed operation', \ command_epoch=command_epoch) return utils.serialize_response(repeat_resp) # Check for length 0 queue if len(self._playlist_queue) == 0: self._current_song = None else: self._current_song = self._playlist_queue.popleft() replica_post_hash = utils.hash_string( pickle.dumps(self._playlist_queue)) if (replica_post_hash != master_post_hash or self._current_song != master_current_song): self._in_recovery = True return utils.serialize_response(failover_resp) master_time = content['time'] self._master_timestamp = master_time resp = utils.format_rpc_response(True, DEQUEUE, {}, \ msg='Successfully dequeued', \ command_epoch=command_epoch) print "end of dequeue: current song for replica: " + str( self._current_song) return utils.serialize_response(resp)
def load_song(self, song_hash): content = utils.unserialize_response(request.get_data()) command_epoch = content['command_epoch'] song_bytes = content['song_bytes'] if self._in_recovery: failover_resp = utils.format_rpc_response(False, LOAD, {}, \ msg='Replica in recovery mode', \ command_epoch=command_epoch) self._last_hb_ts = time.time() * MICROSECONDS return utils.serialize_response(failover_resp) try: with open(utils.get_music_path(song_hash), 'w') as f: f.write(song_bytes) except Exception: resp = utils.format_rpc_response(False, LOAD, {}, \ msg='Error occured in writing to replica', \ command_epoch=command_epoch) else: self._song_hashes.add(song_hash) resp = utils.format_rpc_response(True, LOAD, \ {'has_song': True, 'ip':self._ip}, \ command_epoch=command_epoch) self._last_hb_ts = time.time() * MICROSECONDS return utils.serialize_response(resp)
def play(self): self._last_hb_ts = time.time() * MICROSECONDS # Parse payload content = utils.unserialize_response(request.get_data()) command_epoch = content['command_epoch'] start_time_microsec = content['start_time'] # Not sure when it would be -1 but double check assert (start_time_microsec >= 0) master_offset = 0 if 'offset' in content: master_offset = int(content['offset']) # May be None, need to call .stop() then song_hash = content['song_hash'] failover_mode_resp = utils.format_rpc_response(\ False, PLAY, {}, \ msg='Replica in recovery mode', \ command_epoch=command_epoch) # Case 1: Failover Mode already # Short circuit crucial to not starve failover_service process on # pygame_mixer object # Case 2: Song hash doesn't exist if self._in_recovery or (song_hash != None and not \ os.path.exists(utils.get_music_path(song_hash))): self._in_recovery = True return utils.serialize_response(failover_mode_resp) # Could be in recovery mode at this point, wait for it to finish # before moving on (True) with self._pygame_lock: replica_offset = int(round(pygame.mixer.music.get_pos())) offset_diff = master_offset - replica_offset # Ideally if pygame.mixer.music.play(offset) works then these would not be # errors but alas such is life and we must go into recovery if master_offset > 0: # Case 3: Unpause command, but songs do not match # Case 4: Unpause command, but master's offset is way in future # This should not happen since master determines # offset as max of replica offsets if self._current_song != song_hash or \ abs(offset_diff) > MAX_OFFSET_DIF: self._in_recovery = True return utils.serialize_response(failover_mode_resp) elif song_hash != None: print "Loaded song" pygame.mixer.music.stop() pygame.mixer.music.load(utils.get_music_path(song_hash)) # Adjust start_time_microsec to account for offset difference if master_offset > 0: start_time_microsec = \ start_time_microsec - (offset_diff*MILLISECONDS) # wait until start time, then play curr_replica_microsec = int(round(time.time() * MICROSECONDS)) print song_hash print master_offset while (curr_replica_microsec + ALLOWED_REPLICA_BUFFER < start_time_microsec): curr_replica_microsec = int(round(time.time() * MICROSECONDS)) print "Song hash: " + str(song_hash) if song_hash == None: pygame.mixer.music.stop() elif master_offset == 0: pygame.mixer.music.play(1) else: pygame.mixer.music.unpause() time.sleep(2) # necessary to allow mp3 thread to start? self._current_song = song_hash curr_replica_microsec = int(round(time.time() * MICROSECONDS)) resp = utils.format_rpc_response(True, PLAY, \ {'time': curr_replica_microsec }, \ command_epoch='command_epoch') print "Play: " + str(self._current_song) return utils.serialize_response(resp)
def postload_song(self): self._loading_song = False resp = utils.format_rpc_response(True, POSTLOAD, {}) return utils.serialize_response(resp)
def preload_song(self): self._loading_song = True resp = utils.format_rpc_response(True, PRELOAD, {}) return utils.serialize_response(resp)