0

I prefer using a program like smplayer to watch youtube videos. In order to use smplayer with a playlist, however, I'd need to right-click and copy each and every individual link and paste each one into smplayer...

I wanted a simple application that took a youtube playlist link and obtained a list of the urls for the respective videos within the playlist. Then I could simply copy-paste the list of urls into the program and be done.

This apparently is extremely difficult to pull off...

I came close finding a solution that already existed here. But for playlists that are really big, it seems to fail.

So, after searching around on google, I came across this post. From it, I was able to create a solution... however these requests seem to finish whenever they want, and enter into the output in whatever order they choose.

while(keepGoing&&index<absoluteMax){
        $.getJSON(playListURL, {}).done(function(data) {
            var list_data="";
            if (data.feed.entry.length<resultsPerPage){
                keepGoing=false;
            }
            $.each(data.feed.entry, function(i, item) {
                var feedURL = item.link[1].href;
                var fragments = feedURL.split("/");
                var videoID = fragments[fragments.length - 2];
                var url = videoURL + videoID;
                list_data += url + '\n';
            });
            var results = $('#results').val();
            $('#results').val(results+=list_data);
        });
        index+=resultsPerPage;
        playListURL = 'http://gdata.youtube.com/feeds/api/playlists/PLpclVninnGnu9a9qQphjPsK95kkZsNS_z?alt=json&v=2&max-results='+resultsPerPage+'&start-index='+index+'&callback=?';
    }

Link to jsfiddle

You'll note I had to loop in order to pull in video links, because the API does not allow you to pull in more than 50 videos at a time. The problem here is that each set of 50 comes back in a different order than when it was called. Click the start button in the fiddle, and you'll notice the urls come back in different orders each time...

What I'd like to know is why are these results coming back in random orders? What have I missed? I've tried adding a wait timer, but that doesn't work. I've tried simply appending each result to an array and displaying the array at the end, but that doesn't work either. And for some reason in firebug debug, I cannot seem to step inside the "getJSON" function... in fact it never even goes inside. The only time this function works is when I don't debug at all...

I'm on the cusp of getting this right, and I've spent far too many hours hacking away at it... if anyone who's more familiar with it has a better idea, please let me know :)

Community
  • 1
  • 1
Edge D-Vort
  • 473
  • 3
  • 7
  • 18

1 Answers1

1

The problem is that you're using a while loop. Because your calls to the YT API are asynchronous, the script reaches the end of the while loop and triggers the next iteration before results have come back from the first call. There are lots of ways to avoid this; a simple one might be to wrap it in a function instead of a loop:

$('#startButton').on("click",function(){
  $('#results').val('');
  var youtubeUrl = $('#youtubeUrl').val();
  var fromListOnwards = youtubeUrl.substring(youtubeUrl.indexOf('list=')).split('&');
  var playlistID = fromListOnwards[0].substring(5);
  var resultsPerPage=50;
  var index=1;
  var absoluteMax=999;
  var ytkey="{YOUR API KEY}";
  var videoURL= 'http://www.youtube.com/watch?v=';
  function getYtPage(pageToken) {
   playListURL= 'https://www.googleapis.com/youtube/v3/playlistItemspart=snippet&maxResults='+resultsPerPage+'&playlistId='+playlistID+'&key='+ytkey;
   if (typeof pageToken !== 'undefined') {
         playListURL +="&pageToken="+pageToken;
   }
   $.get(playListURL, {}).done(function(data) {
      var list_data="";
      $.each(data.items, function(i, item) {
         var url = videoURL + item.snippet.resourceId.videoId;
         list_data += url + '\n';
      });
      var results = $('#results').val();
      $('#results').val(results+=list_data);
      if (data.items.length===resultsPerPage&&index<absoluteMax) {
         getYtPage(data.nextPageToken);
      }
   });
  }
  getYtPage();
});

(Note that I used v3 of the API for working up the code example, as it makes getting the videoID and pagination easier, but the same paradigm is easily applied to v2).

This way, your call will be returned and processed before the next function execution.

Also note that, again, because it's ajax, if you click the start button once then do so again while the page is in the process of waiting for results (i.e. before everything has completed), then that call won't be cancelled ... so you might get some duplication of later results pages. But the ordering will always be consistent. And if you let one full cycle finish before clicking the button again, your results will be identical.

jlmcdonald
  • 13,408
  • 2
  • 54
  • 64
  • I tried that, and I seem to get the same problem. http://jsfiddle.net/z9LT2/1/ I'm having difficulty even getting started with v3 of the api... Seems to be an excessive amount of stuff I need to set up and configure all for the purpose of running a simple get on a url... I'm just trying to make an html file that I can use locally. – Edge D-Vort Feb 12 '14 at 15:30
  • 1
    Almost there; your only problem is that you've put the check for re-calling the function outside the callback of the get request. When you do that, you create the same problem as before; the request is asynchronous, so it won't wait for it to come back before going to that trigger to re-call the function. Here's your edited fiddle that demonstrates how moving the final if clause inside the ajax callback will give you consistent results (remembering, as I said earlier, that you can't hit the button a second time until the first set of requests is completely done). http://jsfiddle.net/z9LT2/2/ – jlmcdonald Feb 12 '14 at 19:53
  • That works! Thank you very much! So is jquery by its nature asynchronous, or is this a youtube-specific thing? – Edge D-Vort Feb 14 '14 at 20:12
  • AJAX by its nature is asynchronous, and so any jQuery function that does an ajax call ($.get is just a shortcut for $.ajax with the method set to GET) will inherit that. You can always set $.ajaxSetup({'async':false}) to force synchronicity as well. Of course the best solution is to use javascript promises, since that's what they were created for (deferring the action of another function until a promise object has been resolved), but that would require much more rewriting of your functions. – jlmcdonald Feb 14 '14 at 21:20