def apply_state_change(self, data, callback=None): # TODO: This code is intertwined untestable rubbish. Need to pull out # steps into seperate methods to allow testing... '''Will take the provided data and propegate out the necessary changes to the sessions Remote Player. On return, the remote player, if present, has been asked to sync up. The callback is called once the remote player has confirmed it is in sync. Callback is always called after this function has returned.''' logger.debug('Got state change request on session {}, new state {}'.format(self.id, data)) # if we're changing player new_url = data.get('player_url') if isinstance(new_url, basestring) and (self.player == None or new_url != self.model.player_url): # aquire the new one, do first in case of failure if new_url == '': new_player = None else: new_player = self.player_set.acquire_player(self.name, new_url) # push out a request to clear the old one, retry apply with new player :) if self.player: self.clear_player() self.player_set.release_player(self.name, self.model.player_url) # save this stuff self.player = new_player self.model.player_url = new_url self.model.save() # it's a new player, we want to resend everything, not just a delta full_data = extract_attrs(self.model, SessionProxy.DEFAULTS.iterkeys()) full_data.update(data) data = full_data if self.player is None: # persist changes update_attributes(self.model, data, SessionProxy.DEFAULTS.keys(), False) self.model.save() if callback is not None: # done this way to make sure the function is called after we've returned IOLoop.instance().add_callback(partial(callback, 204, None)) return def player_callback(status, body): if 200 <= status < 300: # make sure to save the new state update_attributes(self.model, data, SessionProxy.DEFAULTS.keys(), False) self.model.save() else: logger.error('Non 2xx response, {}, from Remote Player: {}'.format(status, body)) if callback: callback(status, body) # send of the request to the player self.player.send_request('PATCH', '/playback', data, callback=player_callback)
def player_callback(status, body): if 200 <= status < 300: # make sure to save the new state update_attributes(self.model, data, SessionProxy.DEFAULTS.keys(), False) self.model.save() else: logger.error('Non 2xx response, {}, from Remote Player: {}'.format(status, body)) if callback: callback(status, body)
def __init__(self, player_set, data={}, gen_url=None, callback=None): '''This guy maintains the state of a Playback session. Interleaving both the requests from clients via process_request and events that come back from the Remote Player itself via process_event. On return, the session is persisted to SQL. The callback is called once the remote players state is up-to-date. It will always be called after this method has returned.''' super(SessionProxy, self).__init__() # Precondition: There isn't an active session with this name # Note: There might be a saved session with this name # it must have a name name = data.get('name') or '' if name == '' or not isinstance(name, basestring): # TODO: This should be a Session Error or such, not HTTP specific raise HTTPError(400, 'Sessions must have a string name of at least 1 character') # check we can get the remote player if 'player_url' in data: player = player_set.acquire_player(name, data['player_url']) else: player = None # ensure the model exists, and update it's properties try: model = m.Session.objects.get(name=name) except m.Session.DoesNotExist: model = m.Session(name=name) # update it with the defaults update_attributes(model, SessionProxy.DEFAULTS, SessionProxy.DEFAULTS.viewkeys()) # apply the changes in the payload update_attributes(model, data, SessionProxy.DEFAULTS.viewkeys()) # save the boring stuff self.player_set = player_set self.url = gen_url and gen_url(model.id) self.name = name self.model = model self.player = None self.duration = None # will recieve in playback events # now apply our state, by setting player to None, we force a full aquire cycle of the new player self.apply_state_change({}, callback)