Skip to content

Instantly share code, notes, and snippets.

@RealityRipple
Last active May 3, 2022 01:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RealityRipple/f093fff4431847ebf5f26fdfe3ad4315 to your computer and use it in GitHub Desktop.
Save RealityRipple/f093fff4431847ebf5f26fdfe3ad4315 to your computer and use it in GitHub Desktop.
Track Display for StreamElements
<!doctype html>
<html>
<head>
<script>
var userID = 'your_account'; /* streamelements account ID */
var userName = 'your_channel'; /* broadcaster name (to negate requester matching your name) - use '' to show your own name */
var index = 1; /* queue index, starting from 1 for the next song and counting up */
var fadePad = 32; /* left and right margin */
var scrollRate = 20; /* lower is faster */
var interval = 2; /* update time in seconds */
var fadeTime = 0.5; /* fade speed in seconds */
var normPad = 8; /* label padding. must equal #label's left and right padding value */
var scrollPad = 6; /* padding between label and track name when scrolling */
var label = 'Next Up:'; /* use '' for no label */
var fitScroll = false; /* set to true to scroll text that fits */
var fitScrollRate = 15; /* lower is faster */
var trackDisp = '%DISP_CHANNEL%%DISP_TITLE%%DISP_USER%'; /* display of below variables */
var channelDisp = '%CHANNEL% - '; /* channel name display (with text shown when a channel is provided, empty if no channel or live learn) */
var titleDisp = '%TITLE%'; /* title display */
var userDisp = ' for %USER%'; /* requester username display (with text shown when a song is by request, empty if no requester) */
var emptyOpenDisp = 'The queue is empty! You can add a song with the !sr command.' /* message to display when the queue does not have enough songs to reach this index and song requests are open */
var emptyClosedDisp = 'None! Last Song'; /* message to display when the queue does not have enough songs to reach this index and song requests are closed */
/*
* Example Track Displays:
*
* trackDisp = '%DISP_CHANNEL%%DISP_TITLE%%DISP_USER%'
* channelDisp = '%CHANNEL% - '
* titleDisp = '%TITLE%'
* userDisp = 'for %USER'
* => Pink Floyd - Wish You Were Here for RealityRipple
*
* trackDisp = '%DISP_USER%%DISP_TITLE%%DISP_CHANNEL%'
* channelDisp = ' by %CHANNEL%'
* titleDisp = '%TITLE%'
* userDisp = '%USER%\'s Request: '
* => RealityRipple's Request: Wish You Were Here by Pink Floyd
*
* trackDisp = '%DISP_TITLE%'
* channelDisp = '';
* titleDisp = '%TITLE%';
* userDisp = '';
* => Wish You Were Here
*/
var trackStyle = {
'style1': {title: 'Stairway to Heaven'},
'style2': {channel: 'Topic - Led Zeppelin'},
'style3': {user: 'RealityRipple'}
};
</script>
<style>
.text
{
font-size: 32px;
line-height: 36px; /* adjust as needed for the text-shadow */
color: #FFFFFF;
text-shadow: 2px 2px 3px #000000;
/* font-family: 'Comic Sans MS'; */
}
.style1
{
color: #FFFF00;
}
.style2
{
font-style: italic;
}
.style3
{
color: #008000;
}
</style>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Track Display for StreamElements</title>
<style>
:root
{
--pos: 0px;
}
body
{
text-align: left;
margin: 0;
}
table
{
border: 0;
visibility: hidden;
position: fixed;
opacity: 0;
}
#label
{
margin: 0;
background-color: transparent;
padding: 2px 8px 8px;
white-space: nowrap;
}
#trackBox
{
width: 100%;
overflow: hidden;
position: relative;
}
.track
{
margin: 0;
top: 0;
display: none;
position: absolute;
background-color: transparent;
padding: 2px 0;
white-space: nowrap;
animation-name: fullWidth;
animation-timing-function: ease-in-out;
animation-direction: alternate;
animation-iteration-count: infinite;
visibility: hidden;
opacity: 0;
}
@keyframes fullWidth
{
from
{
transform: translate(0, 0);
}
to
{
transform: translate(var(--pos), 0);
}
}
</style>
<script>
var sTitle = false;
var sChannel = false;
var sUser = false;
var lastW = 0;
var lastD = 0;
var queueOpen = false;
function sleep(ms)
{
return new Promise(resolve => setTimeout(resolve, ms));
}
function loadUser()
{
let p = new Promise((resolve, reject) =>
{
let x = new XMLHttpRequest();
x.open('GET', 'https://api.streamelements.com/kappa/v2/songrequest/' + userID + '/settings/public', true);
x.onreadystatechange = function()
{
if(x.readyState !== 4)
return;
if (x.status !== 200 && x.status !== 0)
return;
if (x.responseText === '')
return;
parseRequests(x.responseText);
resolve(true);
}
x.send(null);
}
);
return p;
}
function parseRequests(sJSON)
{
let j = JSON.parse(sJSON);
queueOpen = (j.hasOwnProperty('enabled') && j.enabled);
}
function updateTrack()
{
sTitle = false;
sChannel = false;
let x = new XMLHttpRequest();
x.open('GET', 'https://api.streamelements.com/kappa/v2/songrequest/' + userID +'/queue/public', true);
// 'https://api.streamelements.com/kappa/v2/songrequest/' + userID + '/queue/public'
x.onreadystatechange = function()
{
if(x.readyState !== 4)
return;
if (x.status !== 200 && x.status !== 0)
return;
if (x.responseText === '')
return;
parseTrack(x.responseText);
displayTrack();
}
x.send(null);
}
function cleanTitle(s)
{
let enclosedEnd = /[([][^()[\]]+ (video|lyrics|remaster)[)\]]$/gi;
let enclosedMatch = /[([](video|lyrics|hd|full|720p|1080p|remastered|high quality)[)\]]$/gi;
let paddedEnd = /[~-]+[^-~]+(lyrics|video)$/gi;
let paddedMatch = /[~-]+ *(lyrics|video|hd|full|720p|1080p|high quality)$/gi;
while (s.match(enclosedEnd) ||
s.match(enclosedMatch) ||
s.match(paddedEnd) ||
s.match(paddedMatch))
{
s = s.replace(enclosedEnd, '').trimEnd();
s = s.replace(enclosedMatch, '').trimEnd();
s = s.replace(paddedEnd, '').trimEnd();
s = s.replace(paddedMatch, '').trimEnd();
}
let rawEnd = / (lyrics|1080p|720p)$/gi;
while (s.match(rawEnd))
s = s.replace(rawEnd, '').trimEnd();
return s;
}
function cleanChannel(s)
{
while (s.slice(-8) === ' - Topic')
s = s.slice(0, -8);
return s;
}
function parseTrack(sJSON)
{
let j = JSON.parse(sJSON);
if (!Array.isArray(j) || j.length < index)
{
sTitle = false;
if (queueOpen)
{
if (emptyOpenDisp !== null && emptyOpenDisp !== '')
sTitle = emptyOpenDisp;
}
else
{
if (emptyClosedDisp !== null && emptyClosedDisp !== '')
sTitle = emptyClosedDisp;
}
sChannel = false;
sUser = false;
return;
}
sTitle = cleanTitle(j[index - 1].title);
if (j.hasOwnProperty('channel'))
sChannel = cleanChannel(j[index - 1].channel);
else
sChannel = false;
if (j[index - 1].hasOwnProperty('user') && j[index - 1].user.hasOwnProperty('username') && j[index - 1].user.username.toLowerCase() !== userName.toLowerCase())
sUser = j[index - 1].user.username;
else
sUser = false;
}
async function showTable()
{
let tbl = document.getElementById('trackTable');
if (tbl.style.visibility === 'visible')
return;
tbl.style.opacity = '0';
tbl.style.visibility = 'visible';
tbl.style.transitionProperty = 'opacity';
tbl.style.transitionDuration = fadeTime + 's';
tbl.style.opacity = '1';
}
async function hideTable()
{
let tbl = document.getElementById('trackTable');
if (tbl.style.visibility !== 'visible')
return;
tbl.style.transitionProperty = 'opacity';
tbl.style.transitionDuration = fadeTime + 's';
tbl.style.opacity = '0';
await sleep(fadeTime * 1000);
tbl.style.transitionProperty = '';
tbl.style.transitionDuration = '';
tbl.style.visibility = '';
tbl.style.opacity = '';
}
function showFade()
{
let tb = document.getElementById('trackBox');
if (!tb.hasAttribute('style'))
tb.setAttribute('style', '-webkit-mask-image: linear-gradient(to right, transparent 0, black ' + fadePad + 'px, black calc(100% - ' + fadePad + 'px), transparent 100%);');
}
function hideFade()
{
let tb = document.getElementById('trackBox');
if (tb.hasAttribute('style'))
tb.removeAttribute('style');
}
async function showTrack()
{
let t = document.getElementById('track');
if (t.style.visibility === 'visible')
return;
t.style.visibility = 'visible';
t.style.opacity = '0';
t.style.transitionProperty = 'opacity';
t.style.transitionDuration = fadeTime + 's';
t.style.opacity = '1';
await sleep(fadeTime * 1000);
t.style.transitionProperty = '';
t.style.transitionDuration = '';
}
function styleTrack(measureOnly)
{
if (trackStyle === false)
return;
for (let s in trackStyle)
{
if (!measureOnly)
{
if (document.getElementById('track').classList.contains(s))
document.getElementById('track').classList.remove(s);
}
if (document.getElementById('measure').classList.contains(s))
document.getElementById('measure').classList.remove(s);
if (trackStyle[s].hasOwnProperty('user'))
{
if (sUser === false)
continue;
if (!sUser.toLowerCase().split(', ').includes(trackStyle[s].user.toLowerCase()))
continue;
}
if (trackStyle[s].hasOwnProperty('title'))
{
if (sTitle === false)
continue;
if (sTitle.toLowerCase() !== trackStyle[s].title.toLowerCase())
continue;
}
if (trackStyle[s].hasOwnProperty('channel'))
{
if (sChannel === false)
continue;
if (sChannel.toLowerCase() !== trackStyle[s].channel.toLowerCase())
continue;
}
if (!measureOnly)
document.getElementById('track').classList.add(s);
document.getElementById('measure').classList.add(s);
}
}
async function hideTrack()
{
let t = document.getElementById('track');
if (t.style.display !== 'inline-block')
{
t.style.opacity = '0';
t.style.display = 'inline-block';
}
else
{
t.style.transitionProperty = 'opacity';
t.style.transitionDuration = fadeTime + 's';
t.style.opacity = '0';
await sleep(fadeTime * 1000);
t.style.transitionProperty = '';
t.style.transitionDuration = '';
}
t.style.opacity = '';
t.style.visibility = '';
t.innerHTML = '';
}
function measureTrack(s)
{
styleTrack(true);
let t = document.getElementById('measure');
t.style.display = 'inline-block';
t.innerHTML = s;
let w = t.getBoundingClientRect()['width'];
t.style.display = '';
t.innerHTML = '';
return w;
}
async function displayTrack()
{
let t = document.getElementById('track');
let l = document.getElementById('label');
if (sTitle === false)
{
lastW = 0;
lastD = 0;
await hideTable()
if (t.hasAttribute('style'))
t.removeAttribute('style');
t.innerHTML = '';
return;
}
let w = document.getElementById('trackBox').getBoundingClientRect()['width'];
let dTitle = titleDisp.replace('%TITLE%', sTitle);
let dChannel = '';
if (channelDisp !== false && sChannel !== false)
dChannel = channelDisp.replace('%CHANNEL%', sChannel);
let dUser = '';
if (userDisp !== false && sUser !== false)
dUser = userDisp.replace('%USER%', sUser);
let track = trackDisp.replace('%DISP_TITLE%', dTitle).replace('%DISP_CHANNEL%', dChannel).replace('%DISP_USER%', dUser);
let d = measureTrack(track);
let h = false;
let range = 0;
if (l.style.paddingRight === scrollPad + 'px')
range = Math.abs((normPad * 2) - (normPad + scrollPad));
if (label !== '')
{
if (d + (normPad * 2) > w - range)
{
if (l.style.paddingRight !== scrollPad + 'px')
{
await hideTrack();
l.style.paddingRight = scrollPad + 'px';
w = document.getElementById('trackBox').getBoundingClientRect()['width'];
h = true;
}
}
else
{
if (fitScroll)
{
if (l.style.paddingRight !== scrollPad + 'px')
{
await hideTrack();
l.style.paddingRight = scrollPad + 'px';
w = document.getElementById('trackBox').getBoundingClientRect()['width'];
h = true;
}
}
else
{
if (l.style.paddingRight !== '')
{
await hideTrack();
l.style.paddingRight = '';
w = document.getElementById('trackBox').getBoundingClientRect()['width'];
h = true;
}
}
}
}
if (!h && w === lastW && d === lastD)
return;
lastW = w;
lastD = d;
if (!h)
await hideTrack();
styleTrack(false);
range = 0;
if (l.style.paddingRight === scrollPad + 'px')
range = Math.abs((normPad * 2) - (normPad + scrollPad));
if (d + (normPad * 2) > w - range)
{
d -= w;
d += fadePad * 2;
let u = Math.ceil(d * scrollRate);
if (u < 2000)
u = 2000;
if (l.style.paddingRight !== scrollPad + 'px' && l.style.paddingRight !== '0px')
l.style.paddingRight = scrollPad + 'px';
showFade();
t.innerHTML = track;
document.documentElement.style.setProperty('--pos', '-' + Math.ceil(d) + 'px');
t.style.marginLeft = fadePad + 'px';
t.style.marginRight = fadePad + 'px';
t.style.animationDuration = u + 'ms';
await showTable();
await showTrack();
}
else
{
if (fitScroll)
{
let m = w - d;
m -= normPad;
let u = Math.ceil(m * fitScrollRate);
if (u < 2000)
u = 2000;
if (l.style.paddingRight !== scrollPad + 'px' && l.style.paddingRight !== '0px')
l.style.paddingRight = scrollPad + 'px';
hideFade();
t.innerHTML = track;
document.documentElement.style.setProperty('--pos', Math.ceil(m) + 'px');
t.style.marginLeft = '';
t.style.marginRight = '';
t.style.animationDuration = u + 'ms';
await showTable();
await showTrack();
}
else
{
if (l.style.paddingRight !== '' && l.style.paddingRight !== '0px')
l.style.paddingRight = '';
hideFade();
t.innerHTML = track;
document.documentElement.style.setProperty('--pos', '0px');
t.style.marginLeft = '';
t.style.marginRight = '';
t.style.animationDuration = '';
await showTable();
await showTrack();
}
}
}
function showLabel()
{
let l = document.getElementById('label');
if (label === '')
{
l.style.paddingLeft = '0px';
l.style.paddingRight = '0px';
l.style.width = '1px';
l.innerHTML = '&ZeroWidthSpace;';
}
else
l.innerHTML = label;
}
</script>
</head>
<body>
<table id="trackTable">
<tr>
<td id="label" class="text"></td>
<td id="trackBox"><div id="track" class="track text"></div></td>
</tr>
</table>
<div id="measure" class="track text"></div>
<script>
async function init()
{
await loadUser();
window.setInterval(loadUser, interval * 1000);
showLabel();
window.setInterval(updateTrack, interval * 1000);
updateTrack();
}
init();
</script>
</body>
</html>
<!doctype html>
<html>
<head>
<script>
var userID = 'your_account'; /* streamelements account ID */
var userName = 'your_channel'; /* broadcaster name (to negate requester matching your name) - use '' to show your own name */
var index = 1; /* queue index, starting from 1 for the next song and counting up */
var fadePad = 32; /* left and right margin */
var scrollRate = 20; /* lower is faster */
var interval = 2; /* update time in seconds */
var fadeTime = 0.5; /* fade speed in seconds */
var normPad = 8; /* label padding. must equal #label's left and right padding value */
var scrollPad = 6; /* padding between label and track name when scrolling */
var label = 'Coming Up:'; /* use '' for no label */
var fitScroll = false; /* set to true to scroll text that fits */
var fitScrollRate = 15; /* lower is faster */
var trackDisp = '%DISP_CHANNEL%%DISP_TITLE%%DISP_USER%'; /* display of below variables */
var channelDisp = '%CHANNEL% - '; /* channel name display (with text shown when a channel is provided, empty if no channel or live learn) */
var titleDisp = '%TITLE%'; /* title display */
var userDisp = ' for %USER%'; /* requester username display (with text shown when a song is by request, empty if no requester) */
var emptyOpenDisp = '' /* message to display when the queue does not have enough songs to reach this index and song requests are open */
var emptyClosedDisp = ''; /* message to display when the queue does not have enough songs to reach this index and song requests are closed */
/*
* Example Track Displays:
*
* trackDisp = '%DISP_CHANNEL%%DISP_TITLE%%DISP_USER%'
* channelDisp = '%CHANNEL% - '
* titleDisp = '%TITLE%'
* userDisp = 'for %USER'
* => Pink Floyd - Wish You Were Here for RealityRipple
*
* trackDisp = '%DISP_USER%%DISP_TITLE%%DISP_CHANNEL%'
* channelDisp = ' by %CHANNEL%'
* titleDisp = '%TITLE%'
* userDisp = '%USER%\'s Request: '
* => RealityRipple's Request: Wish You Were Here by Pink Floyd
*
* trackDisp = '%DISP_TITLE%'
* channelDisp = '';
* titleDisp = '%TITLE%';
* userDisp = '';
* => Wish You Were Here
*/
var trackStyle = {
'style1': {title: 'Stairway to Heaven'},
'style2': {channel: 'Topic - Led Zeppelin'},
'style3': {user: 'RealityRipple'}
};
</script>
<style>
.text
{
font-size: 32px;
line-height: 36px; /* adjust as needed for the text-shadow */
color: #FFFFFF;
text-shadow: 2px 2px 3px #000000;
/* font-family: 'Comic Sans MS'; */
}
.style1
{
color: #FFFF00;
}
.style2
{
font-style: italic;
}
.style3
{
color: #008000;
}
</style>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Track Display for StreamElements</title>
<style>
:root
{
--pos: 0px;
}
body
{
text-align: left;
margin: 0;
}
table
{
border: 0;
visibility: hidden;
position: fixed;
opacity: 0;
}
#label
{
margin: 0;
background-color: transparent;
padding: 2px 8px 8px;
white-space: nowrap;
}
#trackBox
{
width: 100%;
overflow: hidden;
position: relative;
}
.track
{
margin: 0;
top: 0;
display: none;
position: absolute;
background-color: transparent;
padding: 2px 0;
white-space: nowrap;
animation-name: fullWidth;
animation-timing-function: ease-in-out;
animation-direction: alternate;
animation-iteration-count: infinite;
visibility: hidden;
opacity: 0;
}
@keyframes fullWidth
{
from
{
transform: translate(0, 0);
}
to
{
transform: translate(var(--pos), 0);
}
}
</style>
<script>
var sTitle = false;
var sChannel = false;
var sUser = false;
var lastW = 0;
var lastD = 0;
var queueOpen = false;
function sleep(ms)
{
return new Promise(resolve => setTimeout(resolve, ms));
}
function loadUser()
{
let p = new Promise((resolve, reject) =>
{
let x = new XMLHttpRequest();
x.open('GET', 'https://api.streamelements.com/kappa/v2/songrequest/' + userID + '/settings/public', true);
x.onreadystatechange = function()
{
if(x.readyState !== 4)
return;
if (x.status !== 200 && x.status !== 0)
return;
if (x.responseText === '')
return;
parseRequests(x.responseText);
resolve(true);
}
x.send(null);
}
);
return p;
}
function parseRequests(sJSON)
{
let j = JSON.parse(sJSON);
queueOpen = (j.hasOwnProperty('enabled') && j.enabled);
}
function updateTrack()
{
sTitle = false;
sChannel = false;
let x = new XMLHttpRequest();
x.open('GET', 'https://api.streamelements.com/kappa/v2/songrequest/' + userID +'/queue/public', true);
// 'https://api.streamelements.com/kappa/v2/songrequest/' + userID + '/queue/public'
x.onreadystatechange = function()
{
if(x.readyState !== 4)
return;
if (x.status !== 200 && x.status !== 0)
return;
if (x.responseText === '')
return;
parseTrack(x.responseText);
displayTrack();
}
x.send(null);
}
function cleanTitle(s)
{
let enclosedEnd = /[([][^()[\]]+ (video|lyrics|remaster)[)\]]$/gi;
let enclosedMatch = /[([](video|lyrics|hd|full|720p|1080p|remastered|high quality)[)\]]$/gi;
let paddedEnd = /[~-]+[^-~]+(lyrics|video)$/gi;
let paddedMatch = /[~-]+ *(lyrics|video|hd|full|720p|1080p|high quality)$/gi;
while (s.match(enclosedEnd) ||
s.match(enclosedMatch) ||
s.match(paddedEnd) ||
s.match(paddedMatch))
{
s = s.replace(enclosedEnd, '').trimEnd();
s = s.replace(enclosedMatch, '').trimEnd();
s = s.replace(paddedEnd, '').trimEnd();
s = s.replace(paddedMatch, '').trimEnd();
}
let rawEnd = / (lyrics|1080p|720p)$/gi;
while (s.match(rawEnd))
s = s.replace(rawEnd, '').trimEnd();
return s;
}
function cleanChannel(s)
{
while (s.slice(-8) === ' - Topic')
s = s.slice(0, -8);
return s;
}
function parseTrack(sJSON)
{
let j = JSON.parse(sJSON);
if (!Array.isArray(j) || j.length < index)
{
sTitle = false;
if (queueOpen)
{
if (emptyOpenDisp !== null && emptyOpenDisp !== '')
sTitle = emptyOpenDisp;
}
else
{
if (emptyClosedDisp !== null && emptyClosedDisp !== '')
sTitle = emptyClosedDisp;
}
sChannel = false;
sUser = false;
return;
}
sTitle = cleanTitle(j[index - 1].title);
if (j.hasOwnProperty('channel'))
sChannel = cleanChannel(j[index - 1].channel);
else
sChannel = false;
if (j[index - 1].hasOwnProperty('user') && j[index - 1].user.hasOwnProperty('username') && j[index - 1].user.username.toLowerCase() !== userName.toLowerCase())
sUser = j[index - 1].user.username;
else
sUser = false;
}
async function showTable()
{
let tbl = document.getElementById('trackTable');
if (tbl.style.visibility === 'visible')
return;
tbl.style.opacity = '0';
tbl.style.visibility = 'visible';
tbl.style.transitionProperty = 'opacity';
tbl.style.transitionDuration = fadeTime + 's';
tbl.style.opacity = '1';
}
async function hideTable()
{
let tbl = document.getElementById('trackTable');
if (tbl.style.visibility !== 'visible')
return;
tbl.style.transitionProperty = 'opacity';
tbl.style.transitionDuration = fadeTime + 's';
tbl.style.opacity = '0';
await sleep(fadeTime * 1000);
tbl.style.transitionProperty = '';
tbl.style.transitionDuration = '';
tbl.style.visibility = '';
tbl.style.opacity = '';
}
function showFade()
{
let tb = document.getElementById('trackBox');
if (!tb.hasAttribute('style'))
tb.setAttribute('style', '-webkit-mask-image: linear-gradient(to right, transparent 0, black ' + fadePad + 'px, black calc(100% - ' + fadePad + 'px), transparent 100%);');
}
function hideFade()
{
let tb = document.getElementById('trackBox');
if (tb.hasAttribute('style'))
tb.removeAttribute('style');
}
async function showTrack()
{
let t = document.getElementById('track');
if (t.style.visibility === 'visible')
return;
t.style.visibility = 'visible';
t.style.opacity = '0';
t.style.transitionProperty = 'opacity';
t.style.transitionDuration = fadeTime + 's';
t.style.opacity = '1';
await sleep(fadeTime * 1000);
t.style.transitionProperty = '';
t.style.transitionDuration = '';
}
function styleTrack(measureOnly)
{
if (trackStyle === false)
return;
for (let s in trackStyle)
{
if (!measureOnly)
{
if (document.getElementById('track').classList.contains(s))
document.getElementById('track').classList.remove(s);
}
if (document.getElementById('measure').classList.contains(s))
document.getElementById('measure').classList.remove(s);
if (trackStyle[s].hasOwnProperty('user'))
{
if (sUser === false)
continue;
if (!sUser.toLowerCase().split(', ').includes(trackStyle[s].user.toLowerCase()))
continue;
}
if (trackStyle[s].hasOwnProperty('title'))
{
if (sTitle === false)
continue;
if (sTitle.toLowerCase() !== trackStyle[s].title.toLowerCase())
continue;
}
if (trackStyle[s].hasOwnProperty('channel'))
{
if (sChannel === false)
continue;
if (sChannel.toLowerCase() !== trackStyle[s].channel.toLowerCase())
continue;
}
if (!measureOnly)
document.getElementById('track').classList.add(s);
document.getElementById('measure').classList.add(s);
}
}
async function hideTrack()
{
let t = document.getElementById('track');
if (t.style.display !== 'inline-block')
{
t.style.opacity = '0';
t.style.display = 'inline-block';
}
else
{
t.style.transitionProperty = 'opacity';
t.style.transitionDuration = fadeTime + 's';
t.style.opacity = '0';
await sleep(fadeTime * 1000);
t.style.transitionProperty = '';
t.style.transitionDuration = '';
}
t.style.opacity = '';
t.style.visibility = '';
t.innerHTML = '';
}
function measureTrack(s)
{
styleTrack(true);
let t = document.getElementById('measure');
t.style.display = 'inline-block';
t.innerHTML = s;
let w = t.getBoundingClientRect()['width'];
t.style.display = '';
t.innerHTML = '';
return w;
}
async function displayTrack()
{
let t = document.getElementById('track');
let l = document.getElementById('label');
if (sTitle === false)
{
lastW = 0;
lastD = 0;
await hideTable()
if (t.hasAttribute('style'))
t.removeAttribute('style');
t.innerHTML = '';
return;
}
let w = document.getElementById('trackBox').getBoundingClientRect()['width'];
let dTitle = titleDisp.replace('%TITLE%', sTitle);
let dChannel = '';
if (channelDisp !== false && sChannel !== false)
dChannel = channelDisp.replace('%CHANNEL%', sChannel);
let dUser = '';
if (userDisp !== false && sUser !== false)
dUser = userDisp.replace('%USER%', sUser);
let track = trackDisp.replace('%DISP_TITLE%', dTitle).replace('%DISP_CHANNEL%', dChannel).replace('%DISP_USER%', dUser);
let d = measureTrack(track);
let h = false;
let range = 0;
if (l.style.paddingRight === scrollPad + 'px')
range = Math.abs((normPad * 2) - (normPad + scrollPad));
if (label !== '')
{
if (d + (normPad * 2) > w - range)
{
if (l.style.paddingRight !== scrollPad + 'px')
{
await hideTrack();
l.style.paddingRight = scrollPad + 'px';
w = document.getElementById('trackBox').getBoundingClientRect()['width'];
h = true;
}
}
else
{
if (fitScroll)
{
if (l.style.paddingRight !== scrollPad + 'px')
{
await hideTrack();
l.style.paddingRight = scrollPad + 'px';
w = document.getElementById('trackBox').getBoundingClientRect()['width'];
h = true;
}
}
else
{
if (l.style.paddingRight !== '')
{
await hideTrack();
l.style.paddingRight = '';
w = document.getElementById('trackBox').getBoundingClientRect()['width'];
h = true;
}
}
}
}
if (!h && w === lastW && d === lastD)
return;
lastW = w;
lastD = d;
if (!h)
await hideTrack();
styleTrack(false);
range = 0;
if (l.style.paddingRight === scrollPad + 'px')
range = Math.abs((normPad * 2) - (normPad + scrollPad));
if (d + (normPad * 2) > w - range)
{
d -= w;
d += fadePad * 2;
let u = Math.ceil(d * scrollRate);
if (u < 2000)
u = 2000;
if (l.style.paddingRight !== scrollPad + 'px' && l.style.paddingRight !== '0px')
l.style.paddingRight = scrollPad + 'px';
showFade();
t.innerHTML = track;
document.documentElement.style.setProperty('--pos', '-' + Math.ceil(d) + 'px');
t.style.marginLeft = fadePad + 'px';
t.style.marginRight = fadePad + 'px';
t.style.animationDuration = u + 'ms';
await showTable();
await showTrack();
}
else
{
if (fitScroll)
{
let m = w - d;
m -= normPad;
let u = Math.ceil(m * fitScrollRate);
if (u < 2000)
u = 2000;
if (l.style.paddingRight !== scrollPad + 'px' && l.style.paddingRight !== '0px')
l.style.paddingRight = scrollPad + 'px';
hideFade();
t.innerHTML = track;
document.documentElement.style.setProperty('--pos', Math.ceil(m) + 'px');
t.style.marginLeft = '';
t.style.marginRight = '';
t.style.animationDuration = u + 'ms';
await showTable();
await showTrack();
}
else
{
if (l.style.paddingRight !== '' && l.style.paddingRight !== '0px')
l.style.paddingRight = '';
hideFade();
t.innerHTML = track;
document.documentElement.style.setProperty('--pos', '0px');
t.style.marginLeft = '';
t.style.marginRight = '';
t.style.animationDuration = '';
await showTable();
await showTrack();
}
}
}
function showLabel()
{
let l = document.getElementById('label');
if (label === '')
{
l.style.paddingLeft = '0px';
l.style.paddingRight = '0px';
l.style.width = '1px';
l.innerHTML = '&ZeroWidthSpace;';
}
else
l.innerHTML = label;
}
</script>
</head>
<body>
<table id="trackTable">
<tr>
<td id="label" class="text"></td>
<td id="trackBox"><div id="track" class="track text"></div></td>
</tr>
</table>
<div id="measure" class="track text"></div>
<script>
async function init()
{
await loadUser();
window.setInterval(loadUser, interval * 1000);
showLabel();
window.setInterval(updateTrack, interval * 1000);
updateTrack();
}
init();
</script>
</body>
</html>
<!doctype html>
<html>
<head>
<script>
var userID = 'your_account'; /* streamelements account ID */
var userName = 'your_channel'; /* broadcaster name (to negate requester matching your name) - use '' to show your own name */
var fadePad = 32; /* left and right margin */
var scrollRate = 20; /* lower is faster */
var interval = 2; /* update time in seconds */
var fadeTime = 0.5; /* fade speed in seconds */
var normPad = 8; /* label padding. must equal #label's left and right padding value */
var scrollPad = 6; /* padding between label and track name when scrolling */
var label = 'Now Playing:'; /* use '' for no label */
var fitScroll = false; /* set to true to scroll text that fits */
var fitScrollRate = 15; /* lower is faster */
var trackDisp = '%DISP_CHANNEL%%DISP_TITLE%%DISP_USER%'; /* display of below variables */
var channelDisp = '%CHANNEL% - '; /* channel name display (with text shown when a channel is provided, empty if no channel or live learn) */
var titleDisp = '%TITLE%'; /* title display */
var userDisp = ' for %USER%'; /* requester username display (with text shown when a song is by request, empty if no requester) */
/*
* Example Track Displays:
*
* trackDisp = '%DISP_CHANNEL%%DISP_TITLE%%DISP_USER%'
* channelDisp = '%CHANNEL% - '
* titleDisp = '%TITLE%'
* userDisp = 'for %USER'
* => Pink Floyd - Wish You Were Here for RealityRipple
*
* trackDisp = '%DISP_USER%%DISP_TITLE%%DISP_CHANNEL%'
* channelDisp = ' by %CHANNEL%'
* titleDisp = '%TITLE%'
* userDisp = '%USER%\'s Request: '
* => RealityRipple's Request: Wish You Were Here by Pink Floyd
*
* trackDisp = '%DISP_TITLE%'
* channelDisp = '';
* titleDisp = '%TITLE%';
* userDisp = '';
* => Wish You Were Here
*/
var trackStyle = {
'style1': {title: 'Stairway to Heaven'},
'style2': {channel: 'Topic - Led Zeppelin'},
'style3': {user: 'RealityRipple'}
};
</script>
<style>
.text
{
font-size: 32px;
line-height: 36px; /* adjust as needed for the text-shadow */
color: #FFFFFF;
text-shadow: 2px 2px 3px #000000;
/* font-family: 'Comic Sans MS'; */
}
.style1
{
color: #FFFF00;
}
.style2
{
font-style: italic;
}
.style3
{
color: #008000;
}
</style>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Track Display for StreamElements</title>
<style>
:root
{
--pos: 0px;
}
body
{
text-align: left;
margin: 0;
}
table
{
border: 0;
visibility: hidden;
position: fixed;
opacity: 0;
}
#label
{
margin: 0;
background-color: transparent;
padding: 2px 8px 8px;
white-space: nowrap;
}
#trackBox
{
width: 100%;
overflow: hidden;
position: relative;
}
.track
{
margin: 0;
top: 0;
display: none;
position: absolute;
background-color: transparent;
padding: 2px 0;
white-space: nowrap;
animation-name: fullWidth;
animation-timing-function: ease-in-out;
animation-direction: alternate;
animation-iteration-count: infinite;
visibility: hidden;
opacity: 0;
}
@keyframes fullWidth
{
from
{
transform: translate(0, 0);
}
to
{
transform: translate(var(--pos), 0);
}
}
</style>
<script>
var sTitle = false;
var sChannel = false;
var sUser = false;
var lastW = 0;
var lastD = 0;
function sleep(ms)
{
return new Promise(resolve => setTimeout(resolve, ms));
}
function updateTrack()
{
sTitle = false;
sChannel = false;
let x = new XMLHttpRequest();
x.open('GET', 'https://api.streamelements.com/kappa/v2/songrequest/' + userID +'/playing', true);
// 'https://api.streamelements.com/kappa/v2/songrequest/' + userID + '/queue/public'
x.onreadystatechange = function()
{
if(x.readyState !== 4)
return;
if (x.status !== 200 && x.status !== 0)
return;
if (x.responseText === '')
return;
parseTrack(x.responseText);
displayTrack();
}
x.send(null);
}
function cleanTitle(s)
{
let enclosedEnd = /[([][^()[\]]+ (video|lyrics|remaster)[)\]]$/gi;
let enclosedMatch = /[([](video|lyrics|hd|full|720p|1080p|remastered|high quality)[)\]]$/gi;
let paddedEnd = /[~-]+[^-~]+(lyrics|video)$/gi;
let paddedMatch = /[~-]+ *(lyrics|video|hd|full|720p|1080p|high quality)$/gi;
while (s.match(enclosedEnd) ||
s.match(enclosedMatch) ||
s.match(paddedEnd) ||
s.match(paddedMatch))
{
s = s.replace(enclosedEnd, '').trimEnd();
s = s.replace(enclosedMatch, '').trimEnd();
s = s.replace(paddedEnd, '').trimEnd();
s = s.replace(paddedMatch, '').trimEnd();
}
let rawEnd = / (lyrics|1080p|720p)$/gi;
while (s.match(rawEnd))
s = s.replace(rawEnd, '').trimEnd();
return s;
}
function cleanChannel(s)
{
while (s.slice(-8) === ' - Topic')
s = s.slice(0, -8);
return s;
}
function parseTrack(sJSON)
{
let j = JSON.parse(sJSON);
if (!j.hasOwnProperty('title'))
{
sTitle = false;
sChannel = false;
sUser = false;
return;
}
sTitle = cleanTitle(j.title);
if (j.hasOwnProperty('channel'))
sChannel = cleanChannel(j.channel);
else
sChannel = false;
if (j.hasOwnProperty('user') && j.user.hasOwnProperty('username') && j.user.username.toLowerCase() !== userName.toLowerCase())
sUser = j.user.username;
else
sUser = false;
}
async function showTable()
{
let tbl = document.getElementById('trackTable');
if (tbl.style.visibility === 'visible')
return;
tbl.style.opacity = '0';
tbl.style.visibility = 'visible';
tbl.style.transitionProperty = 'opacity';
tbl.style.transitionDuration = fadeTime + 's';
tbl.style.opacity = '1';
}
async function hideTable()
{
let tbl = document.getElementById('trackTable');
if (tbl.style.visibility !== 'visible')
return;
tbl.style.transitionProperty = 'opacity';
tbl.style.transitionDuration = fadeTime + 's';
tbl.style.opacity = '0';
await sleep(fadeTime * 1000);
tbl.style.transitionProperty = '';
tbl.style.transitionDuration = '';
tbl.style.visibility = '';
tbl.style.opacity = '';
}
function showFade()
{
let tb = document.getElementById('trackBox');
if (!tb.hasAttribute('style'))
tb.setAttribute('style', '-webkit-mask-image: linear-gradient(to right, transparent 0, black ' + fadePad + 'px, black calc(100% - ' + fadePad + 'px), transparent 100%);');
}
function hideFade()
{
let tb = document.getElementById('trackBox');
if (tb.hasAttribute('style'))
tb.removeAttribute('style');
}
async function showTrack()
{
let t = document.getElementById('track');
if (t.style.visibility === 'visible')
return;
t.style.visibility = 'visible';
t.style.opacity = '0';
t.style.transitionProperty = 'opacity';
t.style.transitionDuration = fadeTime + 's';
t.style.opacity = '1';
await sleep(fadeTime * 1000);
t.style.transitionProperty = '';
t.style.transitionDuration = '';
}
function styleTrack(measureOnly)
{
if (trackStyle === false)
return;
for (let s in trackStyle)
{
if (!measureOnly)
{
if (document.getElementById('track').classList.contains(s))
document.getElementById('track').classList.remove(s);
}
if (document.getElementById('measure').classList.contains(s))
document.getElementById('measure').classList.remove(s);
if (trackStyle[s].hasOwnProperty('user'))
{
if (sUser === false)
continue;
if (!sUser.toLowerCase().split(', ').includes(trackStyle[s].user.toLowerCase()))
continue;
}
if (trackStyle[s].hasOwnProperty('title'))
{
if (sTitle === false)
continue;
if (sTitle.toLowerCase() !== trackStyle[s].title.toLowerCase())
continue;
}
if (trackStyle[s].hasOwnProperty('channel'))
{
if (sChannel === false)
continue;
if (sChannel.toLowerCase() !== trackStyle[s].channel.toLowerCase())
continue;
}
if (!measureOnly)
document.getElementById('track').classList.add(s);
document.getElementById('measure').classList.add(s);
}
}
async function hideTrack()
{
let t = document.getElementById('track');
if (t.style.display !== 'inline-block')
{
t.style.opacity = '0';
t.style.display = 'inline-block';
}
else
{
t.style.transitionProperty = 'opacity';
t.style.transitionDuration = fadeTime + 's';
t.style.opacity = '0';
await sleep(fadeTime * 1000);
t.style.transitionProperty = '';
t.style.transitionDuration = '';
}
t.style.opacity = '';
t.style.visibility = '';
t.innerHTML = '';
}
function measureTrack(s)
{
styleTrack(true);
let t = document.getElementById('measure');
t.style.display = 'inline-block';
t.innerHTML = s;
let w = t.getBoundingClientRect()['width'];
t.style.display = '';
t.innerHTML = '';
return w;
}
async function displayTrack()
{
let t = document.getElementById('track');
let l = document.getElementById('label');
if (sTitle === false)
{
lastW = 0;
lastD = 0;
await hideTable()
if (t.hasAttribute('style'))
t.removeAttribute('style');
t.innerHTML = '';
return;
}
let w = document.getElementById('trackBox').getBoundingClientRect()['width'];
let dTitle = titleDisp.replace('%TITLE%', sTitle);
let dChannel = '';
if (channelDisp !== false && sChannel !== false)
dChannel = channelDisp.replace('%CHANNEL%', sChannel);
let dUser = '';
if (userDisp !== false && sUser !== false)
dUser = userDisp.replace('%USER%', sUser);
let track = trackDisp.replace('%DISP_TITLE%', dTitle).replace('%DISP_CHANNEL%', dChannel).replace('%DISP_USER%', dUser);
let d = measureTrack(track);
let h = false;
let range = 0;
if (l.style.paddingRight === scrollPad + 'px')
range = Math.abs((normPad * 2) - (normPad + scrollPad));
if (label !== '')
{
if (d + (normPad * 2) > w - range)
{
if (l.style.paddingRight !== scrollPad + 'px')
{
await hideTrack();
l.style.paddingRight = scrollPad + 'px';
w = document.getElementById('trackBox').getBoundingClientRect()['width'];
h = true;
}
}
else
{
if (fitScroll)
{
if (l.style.paddingRight !== scrollPad + 'px')
{
await hideTrack();
l.style.paddingRight = scrollPad + 'px';
w = document.getElementById('trackBox').getBoundingClientRect()['width'];
h = true;
}
}
else
{
if (l.style.paddingRight !== '')
{
await hideTrack();
l.style.paddingRight = '';
w = document.getElementById('trackBox').getBoundingClientRect()['width'];
h = true;
}
}
}
}
if (!h && w === lastW && d === lastD)
return;
lastW = w;
lastD = d;
if (!h)
await hideTrack();
styleTrack(false);
range = 0;
if (l.style.paddingRight === scrollPad + 'px')
range = Math.abs((normPad * 2) - (normPad + scrollPad));
if (d + (normPad * 2) > w - range)
{
d -= w;
d += fadePad * 2;
let u = Math.ceil(d * scrollRate);
if (u < 2000)
u = 2000;
if (l.style.paddingRight !== scrollPad + 'px' && l.style.paddingRight !== '0px')
l.style.paddingRight = scrollPad + 'px';
showFade();
t.innerHTML = track;
document.documentElement.style.setProperty('--pos', '-' + Math.ceil(d) + 'px');
t.style.marginLeft = fadePad + 'px';
t.style.marginRight = fadePad + 'px';
t.style.animationDuration = u + 'ms';
await showTable();
await showTrack();
}
else
{
if (fitScroll)
{
let m = w - d;
m -= normPad;
let u = Math.ceil(m * fitScrollRate);
if (u < 2000)
u = 2000;
if (l.style.paddingRight !== scrollPad + 'px' && l.style.paddingRight !== '0px')
l.style.paddingRight = scrollPad + 'px';
hideFade();
t.innerHTML = track;
document.documentElement.style.setProperty('--pos', Math.ceil(m) + 'px');
t.style.marginLeft = '';
t.style.marginRight = '';
t.style.animationDuration = u + 'ms';
await showTable();
await showTrack();
}
else
{
if (l.style.paddingRight !== '' && l.style.paddingRight !== '0px')
l.style.paddingRight = '';
hideFade();
t.innerHTML = track;
document.documentElement.style.setProperty('--pos', '0px');
t.style.marginLeft = '';
t.style.marginRight = '';
t.style.animationDuration = '';
await showTable();
await showTrack();
}
}
}
function showLabel()
{
let l = document.getElementById('label');
if (label === '')
{
l.style.paddingLeft = '0px';
l.style.paddingRight = '0px';
l.style.width = '1px';
l.innerHTML = '&ZeroWidthSpace;';
}
else
l.innerHTML = label;
}
</script>
</head>
<body>
<table id="trackTable">
<tr>
<td id="label" class="text"></td>
<td id="trackBox"><div id="track" class="track text"></div></td>
</tr>
</table>
<div id="measure" class="track text"></div>
<script>
function init()
{
showLabel();
window.setInterval(updateTrack, interval * 1000);
updateTrack();
}
init();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment