Last active
July 17, 2017 12:50
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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="►►" ' | |
+ 'onclick="playNext()" style="padding:1px 3px 3px 5px;position:relative;top:-3px" title="Next">'; | |
} |
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
niiiice