Twitch API - Unexplained random array and GetStream API help

Hello there,

I have been working on the Twitch TV project for a couple of weeks now and I am nearly done, however I have two problems left that I just cannot figure out!

Most importantly, something is wrong with my ajaxStream function that means it is not working properly and I cannot retrieve data for whether a user is online or offline (hence why all my streams say they are offline, either though some are online…) I’ve tried getting the username information from my first ajax call and putting it through the second ajax call, but something just isn’t right.

Secondly, every time I rerun the pen my arrays are randomly shuffled in a different order! This isn’t a major problem as the data all still fits but I have no idea why this is happening!

Any light shed on either issue would be greatly appreciated. :slight_smile:

Here is a link to my pen:

Cheers,
Stu

I’m not sure if it’s the only(/best) solution, but if I recall correctly I had to make 2 api calls to get the channel info and user status. The first call was to …/channels/… , then after it returned I called …/users/… and updated the UI with the user status once that call returned.

Glancing at your app, I don’t think there’s anything wrong with your function other than the API doesn’t provide user status from that call.

The reason they’re returning in different orders is due to the asynchronous nature of the calls. Your app makes all ~8 calls at the same time and updates the page as they’re returned. HTTP is very slow compared to code execution so the order of returns is highly variable.

If you wanted them in the same order you would need to sort them after they’re returned before they’re inserted into the page (or update the page with sorted results).

2 Likes

You are playing with asynchronous code.

Any ajax:success callback will be executed after main code, which means that i variable will always be 8 there.

Also, it is not guaranteed that requests will return in the same sequence they were sent, therefore items in userName and userOnline may not match.

I gave up on that project after a week of 12+hr days with the ONLY source code snippet I could get to run with any type of decent CONSISTENT results, taking into consideration all the changes to the API since that FCC challenge was put in place is this little gem -

OK thanks for the replies. I now know why the order is different due to the asynchronous nature of the ajax calls.

I was trying to use the first API to send data to the second API so that the usernames would match up; something about that didn’t work though.

I have now separated the two and used different for loops for both calls and it works! However as stated before they do not match up so it shows the right number of people online… just not the people who are actually online. I’ve been messing around trying to sort the arrays so that they match but can’t seem to work out a way to do it. Does anyone have any ideas? Otherwise I feel like I’m going to be completely stuck and need to change how I’ve done pretty much everything… which would make me sad! :frowning:

Thanks again,
Stu

Or if anyone can suggest how to stop the random order occurring, that also would be very useful. If I could fix that I could fix the whole thing!

the only way to stop it is to hard code the user(s) returned data via the html id tag -

id = “comster404”

and code your JS to return the data back into that markup …

let me try to find the codepen snippet where this guy hardcoded the markup to stop the random return

That would be very useful, thank you.

The simplest fix: pass indexes to functions and instead of pushing, place data at proper indexes.

$(document).ready(function() {
  
  var twitchName = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];
  var userName = [];
  var userLogo = [];
  var userStatus = [];
  var userURL = [];
  var userOnline = [];
  var userStream = [];
  
  for (var i=0; i<twitchName.length; i++) {
    ajaxChannel(i);
  }
 
  function ajaxChannel(i) {
    $.ajax({
      type: "get",
      url: "https://api.twitch.tv/kraken/channels/" + twitchName[i],
      headers: {"Client-ID": "9szmid9uhp4i9xpda3ienaatl9cz4s"},
      success: function(data) {
      userName[i] = (data.display_name);
      userLogo[i] = ("<img class = logo src=" + data.logo + ">");
      userStatus[i] = (data.status);
      userURL[i] = (data.url);
      createHTML(data);
     },
      error: function() {
        console.log("Unable to obtain channel data for" + twitchName[i] + ".")
      }
  });
 };
  
  for (var k=0; k<twitchName.length; k++) {
    ajaxStream(k);
  }
  
  function ajaxStream(k) {
    $.ajax({
      type: "get",
      url: "https://api.twitch.tv/kraken/streams?channel=" + twitchName[k],
      headers: {"Client-ID": "9szmid9uhp4i9xpda3ienaatl9cz4s"},
      success: function(strdata) {
        console.log(strdata);
        if (strdata.streams.length > 0) {
          userOnline[k] = ("Online");
        }
        else {
          userOnline[k] = ("Offline");
        }
        createHTML(strdata);
      },
      error: function() {
        console.log("Unable to obtain stream data for" + twitchName[k] + ".")
      }
    });
  };
  
  function createHTML(data, strdata) {
    var myHTML = "";
    var onlineOrOffline = "";
      
    for (var j=0; j<userName.length; j++) {
      
    if (userOnline[j] == "Offline") {
       onlineOrOffline = "offline-box";
      }
    if (userOnline[j] == "Online") {
       onlineOrOffline = "online-box";
      }
   
      myHTML += "<div class =" + onlineOrOffline + ">";
      myHTML += "<div class = row>";
      myHTML += "<div class = col-xs-3>" + userLogo[j] + "</div>";
      myHTML += "<div class = col-xs-3>" + "<h3 class = user-name>" + userName[j] + "</h3>"
      myHTML += "<h3 class = user-online>" + userOnline[j] + "</h3>" + "</div>";
      myHTML += "<div class = col-xs-5>" + "<h3 class = user-status>" + userStatus[j] + "</h3>" + "</div>";
      myHTML += "</div>";
      myHTML += "</div>";

    $("#myHTML").html(myHTML);
  };
} //end of HTML creation
  
});//end of document ready
1 Like

this code snippet works on an alphabetical “sort” of the returned data -

*I’m still hunting the other one


  // sample Twitch TV Users

  var twitchUsers = [
    "ESL_SC2",
    "ESL_CSGO",
    "OgamingSC2",
    "cretetion",
    "freecodecamp",
    "storbeck",
    "habathcx",
    "RobotCaleb",
    "noobs2ninjas",
    "pink_sparkles",
    "comster404",
    "brunofin",
    "medrybw",
    "monstercat",
    "aces_tv",
    "loserfruit",
    "behkuhtv",
    "fakename",
    "food"
  ];

  var twitchUsersData = [];
  var streamStatus = '';
  var MAX_INFO = 45;
  var refreshRate = 300000;
  var active = 'all';
  var twitchLogo = 'http://2am.ninja/twitch/img/GlitchIcon_WhiteonPurple.png';

  function loading() {
    $("#results").html('<div class="loading"><i class="fa fa-spinner fa-pulse fa-3x fa-fw"></i><span class="sr-only">Loading...</span></div>');
  }

  function getStatus() {
    $("#results").empty();
    loading();
    twitchUsersData = [];

    twitchUsers.forEach(function(user) {

      var URL = URL_Streams + user + callbak;
      $.getJSON(URL, user)
        .done(function(data, textStatus, jqXHR) {

          var tempUsersData = {};

          tempUsersData.name = user;

          tempUsersData.status = data.status;
          

          tempUsersData.streaming = (data.stream !== null);
          if (tempUsersData.streaming) {
            tempUsersData.viewers = data.stream.viewers;
            tempUsersData.preview = data.stream.preview.large;
          } else {
            tempUsersData.viewers = null;
            tempUsersData.preview = null;
          }

          var URL = URL_Channel + user + callbak;

          $.getJSON(URL)
            .done(function(data, textStatus, jqXHR) {
              

              if (data.status === 422) {
                tempUsersData.streaming = null;
                tempUsersData.info = "account closed";
                tempUsersData.viewers = null;
                tempUsersData.preview = null;

              } else if (data.status === 404) {
                tempUsersData.streaming = null;
                tempUsersData.info = "non-existant account";
                tempUsersData.viewers = null;
                tempUsersData.preview = null;
              }

              tempUsersData.logo = data.logo;
              tempUsersData.url = null;
              if (data.status !== 422 && data.status !== 404) {
                tempUsersData.info = data.status;
                tempUsersData.displayName = data.display_name;
                tempUsersData.game = data.game;

                if (tempUsersData.preview === null && data.profile_banner !== null) {
                  tempUsersData.preview = data.profile_banner;
                }
                if (tempUsersData.preview === null && data.video_banner !== null) {
                  tempUsersData.preview = data.video_banner;
                }
                if (tempUsersData.preview === null && data.video_banner === null) {
                  tempUsersData.preview = "twitch";
                }
                //tempUsersData.url = data.url;
                tempUsersData.url = 'https://www.twitch.tv/' + data.name;
              }
              twitchUsersData.push(tempUsersData);
              // showUserData(tempUsersData);
              if (twitchUsersData.length == twitchUsers.length) {
                $("#results").empty();
                twitchUsersData.sort(sortList);
                twitchUsersData.forEach(function(who) {
                  $(".btn-group > .btn").removeClass("active");
                  $("#all").addClass("active");
                  showUserData(who);
                });
              }
            })

          .fail(function(jqXHR, textStatus, errorThrown) {
            console.log(errorThrown.toString());
          });
        })

      .fail(function(jqXHR, textStatus, errorThrown) {
        console.log(errorThrown.toString());
      });
    });
  }

  function showUserData(who) {

    var html = '';
    html += '<div class="col-xs-12 col-md-6 col-lg-4">';

    if (who.url !== null) {
      html += '<a href="' + who.url + '" target="_blank">';
    }

    html += '<div class="infocard" id="infocard_' + who.name + '">';

    if ((who.logo === null) || (who.logo === undefined)) {
      userLogo = 'http://2am.ninja/twitch/img/unknown.png';
    } else {
      userLogo = who.logo;
    }

    if (who.streaming) {
      streamStatus = 'stream-on';
    } else {
      streamStatus = 'stream-off';
    }

    html += '<img class="logo ' + streamStatus + '" src="' + userLogo + '" alt="">';

    if (who.url !== null) {
      html += '<div class="play"><i class="fa fa-play-circle-o fa-5x" aria-hidden="true"></i></div>';
    }

    html += '<div class="info"><ul>';

    var displayName = who.displayName;
    if (who.displayName === undefined) {
      displayName = who.name;
    }
    html += '<li"><span class="name">' + displayName + '</span></li>';

    if (who.info !== null) {
      html += '<li class="smaller">' + truncate(who.info, MAX_INFO) + '</li>';
    }

    var game = "";
    if (who.game !== null) {
      game = who.game;
    }
    if (who.streaming) {
      html += '<li class="smaller2">' + game + '&nbsp;&nbsp;<span class="glyphicon glyphicon-eye-open" aria-hidden="true"></span> ' + who.viewers + '</li>';
    }
    html += '</ul></div>';
    html += '</div>';
    html += '</a>';
    html += '</div>';

    $("#results").append(html);

    if (who.preview !== null && who.preview !== 'twitch') {
      $('#infocard_' + who.name).css({
        "background-image": 'url(' + who.preview + ')',
        "background-color": "black"
      });
    }

    if (who.preview === "twitch") {
      $('#infocard_' + who.name).css(
        "background-color", "#6441A5"
      );
    }

    //$('#infocard_' + who.name).toggle();
    $('#infocard_' + who.name).addClass('animated fadeIn');

  }

  function truncate(str, num) {
    if (typeof(str) !== 'undefined') {
      if (str.length > num) {
        return str.slice(0, num - 3) + '&#8230;';
      }
    }
    return str;
  }

  function sortList(a, b) {
    var nameA = a.name.toLowerCase(),
      nameB = b.name.toLowerCase();
    if (nameA < nameB)
      return -1;
    if (nameA > nameB)
      return 1;
    return 0;
  }

  $(".btn-group > .btn").click(function() {
    $(".btn-group > .btn").removeClass("active");
    $(this).addClass("active");
  });

  $("#all").click(function() {
    $("#results").empty();
    twitchUsersData.sort(sortList);
    twitchUsersData.forEach(function(who) {
      showUserData(who);
    });
  });

  $("#online").click(function() {
    $("#results").empty();
    twitchUsersData.sort(sortList);
    twitchUsersData.filter(function(channel) {
      return channel.streaming;
    }).forEach(function(who) {
      showUserData(who);
    });
  });

  $("#offline").click(function() {
    $("#results").empty();
    twitchUsersData.sort(sortList);
    twitchUsersData.filter(function(channel) {
      return (!channel.streaming);
    }).forEach(function(who) {
      showUserData(who);
    });
  });

  $('input[type="search"]').keyup(function() {
    var search = $(this).val().toLowerCase();
    var results = twitchUsersData.filter(function(who) {
      return (who.name.toLowerCase().indexOf(search) != -1);
    });

    $("#results").empty();
    twitchUsersData.sort(sortList);
    results.forEach(function(who) {
      showUserData(who);
    });
  });

  getStatus();

  // update info every 5 mins
  intervalID = setInterval(getStatus, refreshRate);

});
1 Like

Thank you so so much, I finally did it! :slight_smile:

This has been bugging me for days and I’m just so happy it finally works. Just need to tidy it up, make it look a little better and add a few bits and I’ll finally be done.

You are legends, thank you! :smile:

1 Like