2014年5月26日月曜日

なぜSoundCloud「JavaScript SDK version 2」ではautoplayやonfinishなどのオプションを受け付けなくなったのか?の分析

以前にSoundCloud「JavaScript SDK version 2」の変更点についての記事を書いた。そうしたら海外からこんな質問が来た。



要するに、なぜ新しいSDKではautoplayやonfinishなどのオプションを受け付けなくなったのか?という質問だ。

その理由はSDK2はSoundManager2でなくAudioManagerを使うようになったからだ。これをさらに詳しく分析してみよう。

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)
        }
      })
    })
  })
},

playerを作る時にこのようになっている。

「player = new Player(audioManager.createAudioPlayer(options));」

この段階では一応全てのオプションがAudioManagerに渡されるようだ。では、この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)」

つまり,オプションが処理されるのはまだ深い階層のようだ。

HTML5AudioPlayerの場合、最終的にオプションが処理されるのはここだと思われる。

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)
  }
};

つまり、オプション(descriptor)で有効なのはautoPlayのみで、onfinish()などのイベントハンドラーは全く使われていないことが分かる。実際に試してみたところautoPlayは効いた。

onfinish()のコールバックが必要な場合はこんな感じ。

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

やっぱりね、もっとドキュメントが必要だよね。

なんか海外でもニーズがありそうだから英語版も書いといた。情けは人のためならずと申します。
琴線探査: Analysis : Why SoundCloud JavaScript SDK version 2 doesn't accept options(autoplay, onfinish etc.) ?