Skip to content

Instantly share code, notes, and snippets.

@CS1000
Last active July 17, 2017 12:50
Show Gist options
  • Save CS1000/455357c3872af7428e3f to your computer and use it in GitHub Desktop.
Save CS1000/455357c3872af7428e3f to your computer and use it in GitHub Desktop.
JamExchange Room @so Live Radio like Player with "User Requests" (YouTube only, UserScript)
// ==UserScript==
// @name JamExchange Player
// @namespace jamexchange
// @description JamExchange Room @SO Live Radio like Player with "User Requests" (YoutTube only)
// @include http://chat.stackoverflow.com/rooms/39426/*
// @version 1.1.0
// @grant none
// ==/UserScript==
/*
## Change Log:
1.1.0
==
[+feat]dynamic player dimensions
[bugfix?]hopefully "next" button is visible now
[feat]modify youtube onebox css, make room for buttons?
1
==
.published
using formulae: init->get_all_vids,POP->play; onAddedVid->PUSH; onVidEnd/onVidErr->POP->play
### TODO:
- run from console, import by script, minimize, bookmarklet
- click on youtube onebox, play instantly
- change vid list from arry to obj(+name, etc)
- more play ctrls ?
- pause vid on very brief window activation/deactivation (when not current tab/active window for at lepast 15s)
- better/efficient vid extraction (REGEX FTW++)
*/
window.vids = [];
var vidId = '';
window.vidPlayer = ''; //because to be available from console
var initState = 0;
var roomTitle = document.getElementById('roomname').textContent;
var targetMutate = document.querySelector('#chat');
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
[].forEach.call(mutation.addedNodes, function(node) {
if (node.getElementsByTagName('a').length < 1) return;
[].forEach.call(node.getElementsByTagName('a'), function(onebox) {
if (!/https?:\/\/(www\.)?youtu(be.com\/|\.be\/)/.test(onebox.href)) return; //meh
vids.push(onebox.href);
if (initState === 2) { //player is initialized
if (vids.length === 1) playNext(); //if ever will be empty
}
});
});
if (initState === 1) initPlayer();
});
});
observer.observe(targetMutate, {childList: true});
$( document ).ready(function(){
//fake up player
document.getElementById('roomdesc').innerHTML = '';
document.getElementById('roomdesc').setAttribute('id', 'player');
//setup youtube api script
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/player_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
//morph CSS (code c from SO, long live SO helping to write for SO on SO)
var css = '',
head = document.head || document.getElementsByTagName('head')[0],
style = document.createElement('style');
style.type = 'text/css';
css += '.ob-youtube { height: 116px; line-height: 116px; } ';
css += '.ob-youtube-title { left: 250px; transition: none !important; } ';
css += '.ob-youtube-preview { position:relative; top: -32px; } ';
css += '.ob-youtube-overlay { height: 116px !important; line-height: 116px !important; } ';
if (style.styleSheet){
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
head.appendChild(style);
document.getElementById("room-tags").remove(); //make some more room
});
window.onYouTubeIframeAPIReady = function() { //needs global
console.log('YouTube player API ready.');
initState = 1;
}
function onPlayerReady(event) {
//event.target.setVolume(100);
event.target.playVideo();
console.log('YT player ready.');
}
function onPlayerError(event) {
console.log('YT player error ' + event.data + ' occured. Consult API docs: https://developers.google.com/youtube/iframe_api_reference');
playNext();
}
function onPlayerStateChange(event) {
var states = ['ended', 'playing', 'paused', 'buffering', 'video cued'];
var state = states[event.data] || 'unstarted';
console.log('YT player state changed to: ' + state);
if (event.data === 1) {
document.title = '▶';
document.title += ' ' + vidPlayer.getVideoData().title + ' (by: ' + vidPlayer.getVideoData().author + ') @JamExchange 39.426FM';
} else {
document.title = roomTitle;
}
if (event.data === 0) { //video ENDED video
playNext();
}
}
window.playNext = function () { //simple button access
vidPlayer.loadVideoById(extractId(vids.pop()), 0, "medium"); //play next video
}
function extractId(url){
return url.replace(/^.*?v=|^.*?tu.be\//, ''); //needs more testing... maybe playlist crash??
}
function initPlayer(vidId) {
initState = 2;
vidId = extractId(vids.pop());// || 'kkGeOWYOFoA'; //default (Numbers By Nature) <- does not compute
console.log('JamExchange player initializing...');
var w = document.getElementById('roomtitle').clientWidth;
w < 200 && (w = 200);
var h = w < 356 ? 200 : (w/16*9)|0;
console.log(w,h);
window.vidPlayer = new YT.Player('player', {
width: w,
height: h,
videoId: vidId,
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
,'onError': onPlayerError
}
});
document.getElementById('toggle-favorite').outerHTML += '<input type="button" value="&#9658;&#9658;" '
+ 'onclick="playNext()" style="padding:1px 3px 3px 5px;position:relative;top:-3px" title="Next">';
}
@rlemon
Copy link

rlemon commented Apr 8, 2015

niiiice

@Unihedro
Copy link

ugh you misspelled "YouTube" in the userscript description as "YoutTube". plz fix

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment