Search code examples
javascriptangularjsapisoundcloudoembed

SoundCloud API: Using oEmbed to specify start and stop time for playing a track


I have a web application integrating with the SoundCloud API for searching and playing tracks. I can successfully stream tracks through the API using SoundCloud oEmbed like so:

scope.playTrack = function(track) {
  SC.oEmbed(track.permalink_url, { auto_play: true })
    .then(function(oEmbed) {
      scope.soundCloudWidget = $sce.trustAsHtml(oEmbed.html);
      scope.$apply();
    });
};

With the widget bound to the view:

<div ng-bind-html="soundCloudWidget"></div >

This is working as expected. My issue is I would like to stream a track in this way but with a specified start and stop time. I did some research and found a seemingly related StackOverflow question/answer that mentions one can:

append #t=12s to the sound's URL to start it at 12th second

This works when constructing a URL like:

https://soundcloud.com/griz/summer-97-ft-muzzy-bearr#t=54s

However, for start = '54s' I would like to do something like this

scope.playTrackSection = function(track, start) {
  SC.oEmbed(track.permalink_url, { auto_play: true, t: start })
    .then(function(oEmbed) {
      scope.soundCloudWidget = $sce.trustAsHtml(oEmbed.html);
      scope.$apply();
    });
};

or

scope.playTrackSection = function(track, start) {
  var url = track.permalink_url + "#t=" + start;
  SC.oEmbed(track.permalink_url, { auto_play: true })
    .then(function(oEmbed) {
      scope.soundCloudWidget = $sce.trustAsHtml(oEmbed.html);
      scope.$apply();
    });
};

But to no avail. Is there any possible way to specify a start time in this type of context?

Bonus points: Additionally is there any possible way to specify a duration or time when a streaming song should stop playing?


Solution

  • Figured it out. SoundCloud Widget API to the rescue!

    In order to access the JavaScript object which provides the SoundCloud Widget API, add this script to your html page.

    This script exposes the SC.Widget(/*iframeElement|iframeElementID*/) function to the global scope. It allows you to control the widget from the parent page (the page the widget is inserted into). SC.Widget accepts the reference to the iframe element or its id.

    So after adding this script I was able to do something like this:

    scope.playTrackSection = function(track, startTime, endTime) {
      SC.oEmbed(track.permalink_url, { auto_play: true })
        .then(function(oEmbed) {
          scope.soundCloudWidget = $sce.trustAsHtml(oEmbed.html);
          scope.$apply();
    
          scope.iframe = document.getElementById('soundcloud_widget').querySelector('iframe');
          scope.widget = SC.Widget(scope.iframe);
    
          scope.widget.seekTo(startTime);
          scope.widget.bind('playProgress', function(needle) {
            if (needle.currentPosition >= endTime) {
              scope.widget.pause();
            }
          });
        });
    };
    

    With html:

    <div id="soundcloud_widget" ng-bind-html="soundCloudWidget"></div>
    

    It is also useful to know that scope.widget.bind('xxxx', ....) can be used to bind into several Event types of the Widget API. So if you want certain things to happen when a user pauses, plays, finishes, seeks, etc. you can hook into these events.