/* * Ask the server to skip the current song. */ private IEnumerator SkipPlay(JSONNode play) { Ajax ajax = new Ajax(Ajax.RequestType.POST, apiServerBase + "/play/" + play["id"] + "/skip"); yield return StartCoroutine(SignedRequest (ajax)); if ((current == null) || (current.play != play)) { // current song has since changed - we don't care about this response any more yield break; } if (ajax.success) { if (pendingPlay != null) { // skip to play already queued up JSONNode pp = pendingPlay; pendingPlay = null; AssignCurrentPlay(pp); } else if (pendingRequest != null) { // we're waiting for a pending request to come in AssignCurrentPlay(null, true); } else { // we're probably out of music here, since a 'start' notification // should have already kicked off a 'requestNextPlay()' call AssignCurrentPlay(null); } yield break; } else { current.canSkip = false; // prevent future tries until we get next song if (onSkipDenied != null) onSkipDenied(this); yield break; } }
/* * Tell the server that we're starting playback of our active song */ private IEnumerator StartPlay(JSONNode play) { while (true) { Ajax ajax = new Ajax(Ajax.RequestType.POST, apiServerBase + "/play/" + play["id"] + "/start"); yield return StartCoroutine(SignedRequest(ajax)); if ((current == null) || (current.play != play)) { // nobody cares about this song any more yield break; } if (ajax.success) { current.canSkip = ajax.response["can_skip"].AsBool; current.started = true; if (onPlayStarted != null) onPlayStarted(this, play); // start looking for the next song yield return StartCoroutine(RequestNextPlay()); yield break; } else if (ajax.error == (int) FeedError.PlaybackStarted) { // we appear to have missed the response to the original start. // assume the song was good current.canSkip = true; current.started = true; if (onPlayStarted != null) onPlayStarted(this, play); // start looking for the next song yield return StartCoroutine(RequestNextPlay()); yield break; } else { current.retryCount++; yield return new WaitForSeconds(2.0f); // try again later } } }
/* * Ask the server to create a new play for us, and queue it up */ private IEnumerator RequestNextPlay() { if (pendingRequest != null) { // we're already waiting for a play to come in yield break; } yield return StartCoroutine (EnsureClientId ()); while (clientId != null) { Ajax ajax = new Ajax(Ajax.RequestType.POST, apiServerBase + "/play"); ajax.addParameter("formats", formats); ajax.addParameter("client_id", clientId); ajax.addParameter("max_bitrate", maxBitrate); if (!String.IsNullOrEmpty(_placementId)) { ajax.addParameter("placement_id", _placementId); } if (!String.IsNullOrEmpty(_stationId)) { ajax.addParameter ("station_id", _stationId); } // let the rest of the code know we're awaiting a response pendingRequest = new PendingRequest { ajax = ajax, retryCount = 0 }; yield return StartCoroutine(SignedRequest(ajax)); if ((pendingRequest == null) || (pendingRequest.ajax != ajax)) { // another request snuck in while waiting for the response to this one, // so we don't care about this one any more - just quit yield break; } if (ajax.success) { inUS = true; pendingRequest = null; if (current != null) { // play this when the current song is complete pendingPlay = ajax.response["play"]; } else { // start playing this right now, since nothing else is active AssignCurrentPlay (ajax.response["play"]); } yield break; } else if (ajax.error == (int) FeedError.NoMoreMusic) { if (current != null) { // ran out of music to play, but we're still playing something, so // just make a note here pendingPlay = null; } else { // ran out of music, and nothing else to play exhausted = true; if (onPlaysExhausted != null) onPlaysExhausted(this); } pendingRequest = null; yield break; } else if (ajax.error == (int) FeedError.NotUS) { // user isn't in the united states, so can't play anything inUS = false; if (onNotInUS != null) onNotInUS(this); yield break; } else { // some unknown error pendingRequest.retryCount++; // wait for an increasingly long time before retrying yield return new WaitForSeconds(0.5f * (float) Math.Pow(2.0, pendingRequest.retryCount)); } } }
/************** internal API ******************/ /* * Send an ajax request to the server along with authentication information */ private IEnumerator SignedRequest(Ajax ajax) { // add in authentication header ajax.addHeader ("Authorization", "Basic " + System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(token + ":" + secret))); yield return StartCoroutine(ajax.Request()); if (!ajax.success && (ajax.error == (int) FeedError.BadCredentials)) { throw new Exception("Invalid credentials provided!"); } //ajax.DebugResponse(); yield break; }
private IEnumerator InvalidatePlay(JSONNode play) { int retryCount = 0; while (true) { Ajax ajax = new Ajax(Ajax.RequestType.POST, apiServerBase + "/play/" + play["id"] + "/invalidate"); yield return StartCoroutine (SignedRequest (ajax)); if ((current == null) || (current.play != play)) { // nboody cares about this song any more yield break; } if (ajax.success) { if (pendingPlay != null) { // skip to play already queued up JSONNode pp = pendingPlay; pendingPlay = null; AssignCurrentPlay(pp); } else { // invalidate current song AssignCurrentPlay(null, true); // If nothing is queued up, that might be because we haven't tried to 'start' // this play yet, triggering the 'requestNextPlay'. So trigger it here. yield return StartCoroutine(RequestNextPlay()); } yield break; } else { retryCount++; yield return new WaitForSeconds(0.2f * (float) Math.Pow(2.0, retryCount)); } } }
/* * Request information about the placement referred to by placementId. If it is * empty, then request a default placement and set placementId. */ private IEnumerator GetPlacementInformation() { if (!String.IsNullOrEmpty(_placementId) && (placement != null) && ((string) placement["id"] == _placementId)) { // we already have this placement loaded up yield break; } while (true) { Ajax ajax; if (String.IsNullOrEmpty(_placementId)) { ajax = new Ajax(Ajax.RequestType.GET, apiServerBase + "/placement"); } else { ajax = new Ajax(Ajax.RequestType.GET, apiServerBase + "/placement/" + _placementId); } yield return StartCoroutine (SignedRequest (ajax)); if (ajax.success) { placement = ajax.response["placement"]; stations = ajax.response["stations"]; if (String.IsNullOrEmpty(_placementId)) { if (onPlacementChanged != null) onPlacementChanged(this, _placementId); } if (_placementId != placement["id"]) { _placementId = placement["id"]; if (onPlacement != null) onPlacement(this, placement); } if (String.IsNullOrEmpty(_stationId) && (stations.Count > 0)) { _stationId = stations[0]["id"]; if (onStationChanged != null) onStationChanged(this, _stationId); } if (onStations != null) onStations(this, stations); yield break; } else if (ajax.error == (int) FeedError.MissingObject) { // can't find placement - no point in continuing if (String.IsNullOrEmpty(_placementId)) { // no default placement throw new Exception("No default placement for these credentials"); } else { // no such placement throw new Exception("No such placement with id " + _placementId); } } yield return new WaitForSeconds(0.5f); } }
/* * Ensure we've got a clientId */ private IEnumerator EnsureClientId() { if (clientId != null) { yield break; } if (PlayerPrefs.HasKey ("feedfm.client_id")) { // have one already, so use it clientId = PlayerPrefs.GetString("feedfm.client_id"); if (onClientRegistered != null) onClientRegistered(this); yield break; } else { // need to get an id while (true) { Ajax ajax = new Ajax(Ajax.RequestType.POST, apiServerBase + "/client"); yield return StartCoroutine(SignedRequest (ajax)); if (ajax.success) { clientId = ajax.response["client_id"]; try { PlayerPrefs.SetString ("feedfm.client_id", clientId); } catch (PlayerPrefsException) { // ignore, *sigh* } if (onClientRegistered != null) onClientRegistered(this); yield break; } else if (ajax.error == (int) FeedError.NotUS) { // user isn't in the united states, so can't play anything inUS = false; if (onNotInUS != null) onNotInUS(this); yield break; } // no success, so wait bit and then try again yield return new WaitForSeconds(1.0f); } } }
/* * Tell the server we've completed the current play, and make any pending * play active. */ private IEnumerator CompletePlay() { Ajax ajax = new Ajax(Ajax.RequestType.POST, apiServerBase + "/play/" + current.play["id"] + "/complete"); yield return StartCoroutine(SignedRequest (ajax)); // we really don't care what the response was, really if (pendingRequest == null) { // start playing whatever we've got queued up JSONNode pp = pendingPlay; pendingPlay = null; AssignCurrentPlay(pp); } else { // waiting for a request to come in, so kill current song and announce that we're waiting AssignCurrentPlay (null, true); } }
/* * Tell the server how much of the song we've listened to */ public virtual void ReportPlayElapsed(int seconds) { if (current == null) { throw new Exception ("Attempt to report elapsed play time, but the pay hasn't started"); } Ajax ajax = new Ajax (Ajax.RequestType.POST, apiServerBase + "/play/" + current.play ["id"] + "/elapse"); ajax.addParameter ("seconds", seconds.ToString ()); StartCoroutine (SignedRequest (ajax)); }