Skip to content

Instantly share code, notes, and snippets.

@Dan-Q
Created November 4, 2016 12:24
Show Gist options
  • Save Dan-Q/1ea1e663d28d84be4a159ad65675b38a to your computer and use it in GitHub Desktop.
Save Dan-Q/1ea1e663d28d84be4a159ad65675b38a to your computer and use it in GitHub Desktop.
Simple one-file tool for the Bodleian Libraries' Heritage Window (5x3 HD video wall). Shows thumbnails for a YouTube playlist, then picks one video and plays it, then repeats.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>YouTube Video Wall</title>
<style type="text/css">
body {
margin: 0;
background: black;
color: white;
}
main {
position: absolute;
top: 0;
left: 0;
width: 3200px;
height: 1080px;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
img {
opacity: 0;
transition: opacity 0.5s;
}
img.visible {
opacity: 1;
}
</style>
</head>
<body>
<main>
</main>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script>
'use strict';
// Constants
const API_KEY ='PUT-YOUR-API-KEY-HERE';
const PLAYLIST_ID = 'PLjw-j0hh81Ic1Cj43I3HpMLvFZzg6E8CH';
const VIDEO_COUNT = 18; // number of videos to request from the playlist
const IMAGE_APPEAR_TIME = 250; // time (ms) between each thumbnail appearing
const VIDEO_APPEAR_TIME = 3000; // time (ms) after all thumbnails appear before video chosen
const THUMB_REMAIN_TIME = 1000; // time (ms) after all-but-chosen thumbnails disappear before video begins
const PANIC_TIME = 1000 * 60 * 15; // time (15 minutes) after which player will assume something went wrong (e.g. connection dropped) and will restart from scratch
const main = $('main');
// In case of any kind of failure, auto-restart after a while
setTimeout(window.location.reload, PANIC_TIME);
// Fisher-Yates shuffle
function shuffle(array) {
let m = array.length, t, i;
while (m) {
let i = Math.floor(Math.random() * m--);
let t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}
// Draw thumbnail wall, pick video, show video
function run(){
main.html('');
$.get(`https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=${VIDEO_COUNT}&playlistId=${PLAYLIST_ID}&key=${API_KEY}`, function(data) {
for(let item of shuffle(data.items)){
let snippet = item.snippet;
let thumb = snippet.thumbnails.high;
main.append(`<img src="${thumb.url}" height="${thumb.height}" width="${thumb.width}" alt="${snippet.title}" data-video-id="${snippet.resourceId.videoId}" />`);
}
let imageAppearify = setInterval(function(){
let hiddenImages = $('img:not(.visible)');
if(hiddenImages.length == 0) {
clearInterval(imageAppearify);
setTimeout(function(){
let imgs = $('img');
let randomImg = $(imgs[Math.floor(Math.random() * imgs.length)]);
imgs.not(randomImg).removeClass('visible');
setTimeout(function(){
randomImg.removeClass('visible');
setTimeout(function(){
let videoId = randomImg.data('video-id');
$('main').html(`<iframe width="1920" height="1080" src="https://www.youtube-nocookie.com/embed/${videoId}?autoplay=1&rel=0&controls=0&showinfo=0" frameborder="0" allowfullscreen></iframe>`);
$.get(`https://www.googleapis.com/youtube/v3/videos?id=${videoId}&part=contentDetails&key=${API_KEY}`, function(videoData){
let duration = videoData.items[0].contentDetails.duration.match(/((\d+)M)?(\d+)S/);
let mins = parseInt(duration[2]) || 0;
let secs = parseInt(duration[3]) || 0;
let time = (mins * 60) + secs;
setTimeout(function(){ run(); }, time * 1000); // start over when video finishes
});
}, 300); // just longer than fade-out time for images, defined by CSS
}, THUMB_REMAIN_TIME);
}, VIDEO_APPEAR_TIME);
return;
}
$(hiddenImages[Math.floor(Math.random() * hiddenImages.length)]).addClass('visible');
}, IMAGE_APPEAR_TIME);
});
}
// When page finished loading, start!
$(run);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment