Skip to content

Instantly share code, notes, and snippets.

@erik-sn
Created July 3, 2016 23:18
Show Gist options
  • Save erik-sn/a5aa73977df6cab46ae0cf40df634f0d to your computer and use it in GitHub Desktop.
Save erik-sn/a5aa73977df6cab46ae0cf40df634f0d to your computer and use it in GitHub Desktop.
Twitch.TV Stream Tracker
<div id="loading-container"></div>
<div id="error-container"></div>
<div id="page-container">
<div id="search-container">
<fieldset class="form-group">
<div id="logo-container"><img height="200" src="https://maxcdn.icons8.com/Share/icon/Logos//twitch1600.png" /></div>
<input type="search" class="form-control" id="channel-search" placeholder="Search a game to find related channels" autocomplete="off">
<small id="search-error" class="text-muted"></small>
</fieldset>
<div id="search-results-container">
<ul id="search-results"></ul>
</div>
</div>
<div id="list-container">
<h1 id="active-streams-label">Active Streams</h1>
<ul id="stream-list-active"></ul>
<h1>Inactive Streams</h1>
<ul id="stream-list-inactive"></ul>
</div>
</div>
</div>
let channels = ["ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas", "towlie", "brunofin", "comster404"];
const baseUrl = 'https://api.twitch.tv/kraken/streams?channel=';
const gameSearch = 'https://api.twitch.tv/kraken/search/games?type=suggest&q='
const channelSearch = 'https://api.twitch.tv/kraken/search/channels?q='
const channelInfo = 'https://api.twitch.tv/kraken/channels/';
$(document).ready(function() {
$('#channel-search').on('input', function(event) {
const term = event.target.value;
if (term.length >= 3) {
searchTwitch(term);
}
});
$('html, body, #page-container').on('click', function() {
$('#search-results').hide();
});
getStreamData();
});
$(document).on('click', '#channel-search', function() {
$('#search-results').show();
});
$(document).on('click', '.preview-image', function () {
const channel = $(this).context.name;
window.open('https://www.twitch.tv/' + channel, '_blank');
});
$(document).on('click', '#channel-search', function() {
$('.channel-search-result').show();
});
$(document).on('click', '.channel-search-result', function() {
const channel = $(this).attr('name');
if (channels.indexOf(channel) === -1) {
channels.push(channel);
getStreamData();
$('.channel-search-result').remove();
$('#channel-search').val('');
}
});
function getStreamData() {
const url = channels.reduce(function(prev, current) {
return prev + ','+ current;
}, baseUrl);
getData(url, 'jsonp', function(twitchData) {
if(twitchData) {
const activeNames = twitchData.streams.map(stream => {
return stream.channel.display_name;
});
const active = twitchData.streams.reduce((prev, current) => {
return generateActiveStream(prev, current);
}, '');
$('#stream-list-active').html(active);
const inactiveNames = channels.filter(channel => {
if (activeNames.indexOf(channel) === -1) {
return true;
}
});
$('.inactive-stream').remove();
for (let i = 0; i < inactiveNames.length; i++) {
const title = inactiveNames[i];
const element = generateInactiveStream(title);
getData(channelInfo + title, 'jsonp', streamInfo => {
let image = ''
let label;
let status;
if (streamInfo.error) {
label = '<span class="offline-label">' + title + ' </span>';
status = '<span class="offline-status">Account Closed</span>';
} else {
label = '<span class="offline-label">' + streamInfo.display_name + ' </span>';
status = '<span class="offline-status">Offline</span>';
if (streamInfo.logo) {
image = '<img class="offline-image" height="40" src="' + streamInfo.logo + '" />'
}
}
const element = '<li class="list-group-item inactive-stream">' + image + label + status + '</li>';
$('#stream-list-inactive').append(element);
});
}
}
});
}
function generateActiveStream(prev, current) {
var preview = '<div class="col-sm-4"><img class="preview-image" src="' + current.preview.medium + '" name="' + current.channel.name + '"/></div>';
var description = '<div class="active-description col-sm-5"><div class="active-title">' + current.channel.name + '<span class="active-language"> - ' + current.channel.language + '</span></div><div class="active-game">' + current.channel.game + '</div>';
var status = '<div class="active-status" >' + current.channel.status + '</div>';
var viewers = '<div class="active-viewers" >Viewers: ' + current.viewers + '</div></div>';
return prev + '<li class="list-group-item active-stream container-fluid"><div class="row">' + preview + description + status + viewers + '</div></div></li>';
}
function generateInactiveStream(title) {
}
function searchTwitch(term) {
getData(gameSearch + term, 'jsonp', gameResults => {
const game = gameResults.games[0];
getData(channelSearch + game.name, 'jsonp', channelResults => {
const channels = channelResults.channels.reduce((prev, current) => {
return prev + generateChannelSearch(current);
}, '');
$("#search-results").html(channels);
});
});
}
function generateChannelSearch(current) {
const status = current.status.substring(0, 25) + '...';
return '<li name="' + current.name + '" class="list-group-item channel-search-result"><img height="40" src="' + current.logo + '" /><div class="search-info"><div>' + current.display_name + ' Followers: ' + current.followers + '</div><div>' + status + '</div></div></li>';
}
function getData(url, dataType, returnData) {
$.ajax({
url: url,
type: 'GET',
dataType: dataType,
success: function(data) {
returnData(data)
},
error: function(jqXHR, textStatus, errorThrown) {
returnData(undefined);
}
});
}
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
$primaryColor: #5F0376;
$secondaryColor: #2E4172;
$tertiaryColor: white;
@mixin shadow($blur, $opacity) {
-webkit-box-shadow: 0px 0px $blur 0px rgba(0,0,0,$opacity);
-moz-box-shadow: 0px 0px $blur 0px rgba(0,0,0,$opacity);
box-shadow: 0px 0px 10px $blur rgba(0,0,0,$opacity);
}
body {
background-color: #8E24AA;
}
#logo-container {
text-align: center;
}
#page-container {
@include shadow(5px, 0.75);
opacity: 0.9;
margin: 0% 15% 0% 15%;
padding: 0px 25px 25px 25px;
background-color: $primaryColor;
min-height: 100vh;
min-width: 400px;
color: $tertiaryColor;
}
#list-container {
padding-top: 20px;
}
#stream-container {
color: $tertiaryColor;
font-size: 28px;
text-align: center;
width: 100%;
height: 50%;
}
h1 {
text-align: center;
}
#search-results {
@include shadow(3px, 0.25);
}
#search-results {
z-index: 2;
width: 50%;
position: absolute;
margin-top: -15px;
padding-left: 0px;
min-width: 300px !important;
}
.channel-search-result {
padding: 0px !important;
color: black;
}
#channel-search {
font-size: 24px;
height: 50px;
}
.channel-search-result img {
margin-top: -20px;
}
.search-info {
margin-left: 10px;
display: inline-block;
}
#active-streams-label {
margin-top: 0px;
}
#stream-container-label {
padding-top: 40px;
}
#stream-list-active {
padding-left: 0px;
color: black;
}
#stream-list-inactive {
padding-left: 0px;
color: black;
}
.offline-image {
margin-right: 10px;
}
.offline-label {
font-weight: bold;
font-size: 18px;
margin-right: 25px;
}
.offline-status {
font-style: italic;
}
.active-stream {
}
.preview-image {
cursor: pointer;
display: inline-block;
margin-right: 10px;
}
.col-sm-4 {
width: 350px !important;
}
.active-description {
margin-left: 25px;
}
.active-title {
font-size: 30px;
font-weight: bold;
}
.active-language {
font-weight: 100;
}
.active-game {
font-size: 20px;
width: 100%;
}
.active-viewers {
font-size: 16px;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment