2014年5月26日月曜日

Analysis : Why SoundCloud JavaScript SDK version 2 doesn't accept options(autoplay, onfinish etc.) ?

I wrote an article about the changes of SoundCloud "JavaScript SDK version 2" previously. And then, this question came from abroad.



It's because SDK2 now uses the AudioManager rather than SoundManager2. Let's analyze this deeper.

SDK2
http://connect.soundcloud.com/sdk-2.0.0.js
stream: function (idOrUrl, optionsOrCallback, callback) {
  var a, options, stream_url, track_url;
  a = SC.Helper.extractOptionsAndCallbackArguments(optionsOrCallback, callback);
  options = a.options;
  callback = a.callback;
  options.id = "T" + idOrUrl + "-" + Math.random();
  track_url = this._prepareTrackUrl(idOrUrl);
  stream_url = this._prepareStreamUrl(idOrUrl);
  return SC.whenStreamingReady(function () {
    return SC.get(track_url, function (track) {
      options.duration = track.duration;
      return SC.get(stream_url, function (streams) {
        var createAndCallback, ontimedcommentsCallback, _this = this;
        options.src = streams.http_mp3_128_url || streams.rtmp_mp3_128_url;
        createAndCallback = function (options) {
          var player;
          player = new Player(audioManager.createAudioPlayer(options));
          if (callback != null) {
            callback(player)
          }
          return player
        };
        if (ontimedcommentsCallback = options.ontimedcomments) {
          delete options.ontimedcomments;
          return SC._getAll(track_url + "/comments", function (comments) {
            var player;
            player = createAndCallback(options);
            return SC._setOnPositionListenersForComments(player, comments, ontimedcommentsCallback)
          })
        } else {
          return createAndCallback(options)
        }
      })
    })
  })
},

When creating player, the code looks like this.

"player = new Player(audioManager.createAudioPlayer(options));"

All options seems to be passed to the AudioManager at this stage. Let's see what's happening in createAudioPlayer().

AudioManager
http://connect.soundcloud.com/audiomanager/audiomanager.js
AudioManager.prototype.createAudioPlayer = function (descriptor) {
    var audioType, protocol, extension;
    if (!descriptor.id) {
        descriptor.id = Math.floor(Math.random() * 1e10).toString() + (new Date).getTime().toString()
    }
    if (!descriptor.src) {
        throw new Error("AudioManager: You need to pass a valid src")
    }
    if (!this._players[descriptor.id]) {
        this._players[descriptor.id] = PlayerFactory.createAudioPlayer(descriptor, this._settings)
    }
    this._players[descriptor.id].setVolume(this._volume);
    this._players[descriptor.id].setMute(this._muted);
    this._players[descriptor.id].on("stateChange", this._onStateChange, this);
    return this._players[descriptor.id]
};

"PlayerFactory.createAudioPlayer(descriptor, this._settings)"

It seems the options are processed in the deeper hierarchy. For the HTML5AudioPlayer, it seems the options are finally processed here.

module.exports = HTML5AudioPlayer = function (descriptor, settings) {
  this._id = descriptor.id;
  this._descriptor = descriptor;
  this._isLoaded = false;
  this._settings = settings;
  this._bufferingTimeout = null;
  this._currentPosition = 0;
  this._loadedPosition = 0;
  this._prevCurrentPosition = 0;
  this._prevCheckTime = 0;
  this._prevComparison = 0;
  this._positionUpdateTimer = 0;
  this._playRequested = false;
  if (descriptor.duration) {
    this._duration = descriptor.duration
  }
  _.bindAll(this, "_onPositionChange", "_onStateChange", "_onLoaded", "_onBuffering");
  this._init();
  this.toggleEventListeners(true);
  if (this._descriptor.preload) {
    this.preload()
  }
  if (descriptor.autoPlay) {
    this.play()
  } else {
    this._setState(States.IDLE)
  }
};

We can see the only one effective option(descriptor) is "autoPlay". And also we can see that the event handler like "onfinish" is not used at all. Acctually, I tried to use "autoPlay" option. It works.

If you need onfinish() callback, use this.

player.on("stateChange", function(evt) {
  switch(evt) {
    case "ended":
     onPlayerEnded();
     break;
  }
}

We need more docs about SDK2. Don't you think so ?