There are 25 previous versions of this script.
// ==UserScript==
// @name VLCTube
// @namespace 0d92f6be108e4fbee9a6a0ee4366b72e
// @include http://*youtube.com/watch*
// @version 16
// ==/UserScript==
//Tested on Fx7.0.1
//VLC has some issues with dynamic resizing (it doesn't do it)
var width = 640;
var widthWide = 854;
var height = 480;
var isWide = false;
var vlc_id = 'mymovie';
var VLC_status = new Array("standby", "opening", "buffering", "playing",
"paused", "stopped", "ended", "error");
// Dispatch <select/> onChange event
var chngevt = document.createEvent("HTMLEvents");
chngevt.initEvent("change", false, true);
// Helpers
function $(id){return document.getElementById(id);}
function getArg(args, idx, def){
return idx in args ? args[idx] : (def ? def : '');
}
function ft(i){ if (i>=10) return i; return '0'+i;}
function fmttime(time)
{
var ms = time % 1000 / 10;
var s = time % 60000 / 1000;
//var m = time % 3600000 / 60000;
var m = time / 60000;
return ft(Math.floor(m)) +':'+ ft(Math.floor(s)) + '.' + ft(Math.floor(ms));
}
function restoreSettings(ev){
//volume
myvlc.vlc.audio.volume = GM_getValue('vlc_vol', 100);
if(scroll2) scroll2.setValue(myvlc.vlc.audio.volume);
//quality
var q = GM_getValue('ytquality', undefined);
var sel = $(vlc_id+'_select');
var opt = sel.options.namedItem(q);
if (!opt) opt = sel.options.namedItem('large/x-flv');
if (opt)
opt.selected = true;
//else first item should be selected
//Or:
//for(var i=0; i< sel.options.length; i++)
// if(sel.options[i].innerHTML == q)
// sel.options[i].selected = true;
return true;
}
function saveSettings(ev){
GM_setValue('vlc_vol', myvlc.vlc.audio.volume);
//GM_setValue('vlc_http-caching', unsafeWindow['vlc_controls'].options.get("http-caching"));
var sel = $(vlc_id+'_select');
GM_setValue('ytquality', sel.options[sel.selectedIndex].innerHTML);
}
function setValue(id, val){
var el = document.getElementById(id);
if(el){
el.setAttribute('value',val);
}
}
function setHTML(id, val){
var el = document.getElementById(id);
if(el){
el.innerHTML = val;
}
}
function insertYTmessage(message){
var baseDiv,container,msg;
msg = $('iytmsg');
if(!msg){
baseDiv = $('baseDiv');
container = document.createElement('div');
msg = document.createElement('pre');
msg.id = "iytmsg";
container.setAttribute("style","position:relative;background: #FFA0A0; color: #800000; border: 1px solid; border-color: #F00;");
msg.setAttribute("style","text-align:center; margin-top:1em; margin-bottom:1em;");
container.appendChild(msg);
baseDiv.insertBefore(container,/*baseDiv.childNodes[2]*/
document.getElementById( /*'watch-vid-title'*/ 'content'));
}else{
message = "\r\n" + message;
}
msg.appendChild(document.createTextNode(message));
}
function replaceYTmessage(message){
$('iytmsg').innerHTML=message;
}
function addScriptSrc(src) {
var head, script;
head = document.getElementsByTagName('head')[0];
if (!head) { return; }
script = document.createElement('script');
script.type = 'text/javascript';
script.setAttribute('src', src);
head.appendChild(script);
}
function putCSS(){
var css = "#scrollbar, #scrollbar2{\
position: relative;\
width: 250px;\
height: 15px;\
border: 1px solid #000;\
float: left;\
text-align: center;\
margin-right: 5px;\
border-radius: 3px;\
}\
#scrollbar2 { width: 110px;}\
#knob, #knob2 {\
position: absolute;\
width: 10px;\
height: 15px;\
background: #FAA;\
}\
#knob2 {background: #AAF;}\
.vlccontrols {clear: both; margin:5px;}\
.vlccontrols div {margin-right:5px; display: inline;}";
if (typeof GM_addStyle != "undefined") {
GM_addStyle(css);
} else if (typeof addStyle != "undefined") {
addStyle(css);
} else {
var heads = document.getElementsByTagName("head");
if (heads.length > 0) {
var node = document.createElement("style");
node.type = "text/css";
node.appendChild(document.createTextNode(css));
heads[0].appendChild(node);
}
}
}
// Controls
function ScrollBar () {}
ScrollBar.prototype = {
offX: 0,
offY: 0,
value: 0,
maxValue: 100.0,
type: 0,/*0 - hor, 1 - vert*/
bar: null,
knob: null,
events: new Array(),
$: function(id){ return document.getElementById(id); },
init: function(barId, knobId, type, maxval){
this.bar = this.$(barId);
this.bar.wrappedJSObject.ScrollBar = this;
this.knob = this.$(knobId);
this.knob.wrappedJSObject.ScrollBar = this;
this.bar.unselectable = "on";
this.knob.unselectable = "on";//IE
this.bar.style.MozUserSelect='none';
this.knob.style.MozUserSelect='none';
//this.knob.style.KhtmlUserSelect='none';
this.type = type;
if(maxval)
this.maxValue = maxval;
//this.knob.onmousedown = ScrollBar.eventHandlers.mouseDown;
this.knob.addEventListener('mousedown',ScrollBar.eventHandlers.mouseDown,true);
},
register: function(ev){this.events.push(ev);},
emitValue:function(){
for(var i in this.events){
try{
this.events[i].emitValue(this, this.events[i], this.value);
}catch(e){GM_log(e);}
}
},
setValue: function(val){
if(val<0) val=0;
if(ScrollBar._ScrollBarDragData) return;
this.value = val;
if(this.type == 0)
this.knob.style.left = Math.round((this.value/this.maxValue) * (this.bar.clientWidth - this.knob.clientWidth)) + "px";
if(this.type == 1)
this.knob.style.top = Math.round((this.value/this.maxValue) * (this.bar.clientHeight - this.knob.clientHeight)) + "px";
}
};
ScrollBar.eventHandlers = {
mouseDown: function(ev){
document.addEventListener('mouseup',ScrollBar.eventHandlers.mouseUp,true);
document.addEventListener('mousemove',ScrollBar.eventHandlers.mouseMove,true);
var s = ev.target.wrappedJSObject.ScrollBar;
ScrollBar._currentScrollBar = s;
s.offX = ev.clientX - s.knob.offsetLeft;
s.offY = ev.clientY - s.knob.offsetTop;
ScrollBar._ScrollBarDragData = {
screenX: ev.screenX,
screenY: ev.screenY,
dx: ev.screenX - s.knob.offsetLeft,
dy: ev.screenY - s.knob.offsetTop,
startValue: s.value,
ScrollBar: s
};
},
mouseUp: function(ev){
var s = ScrollBar._currentScrollBar;
ScrollBar._ScrollBarDragData = null;
document.removeEventListener('mouseup',ScrollBar.eventHandlers.mouseUp,true);
document.removeEventListener('mousemove',ScrollBar.eventHandlers.mouseMove,true);
s.emitValue();
},
mouseMove: function(ev){
var s = ScrollBar._currentScrollBar;
switch(s.type){
case 0:
var x = ev.screenX - ScrollBar._ScrollBarDragData.dx;
var w = s.bar.clientWidth - s.knob.clientWidth;
if( x < 0 ) x = 0;
if( x > w ) x = w;
s.knob.style.left = x + "px";
s.value = Math.round(x/w * s.maxValue);
//s.knob.title = Math.floor(x/w * 100) + '%';
break;
case 1:
var y = ev.screenY - ScrollBar._ScrollBarDragData.dy;
var h = s.bar.clientHeight - s.knob.clientHeight;
if( y < 0 ) y = 0;
if( y > h ) y = h;
s.knob.style.top = y + "px";
s.value = Math.round(y/h * s.maxValue);
break;
}
},
}
function VLCObj (){}
VLCObj.prototype = {
vlc:null,
controls:null,
scrollbarPos: null,
scrollbarVol: null,
uri: null,
init: function (scrollbarPos, scrollbarVol){
this.scrollbarPos = scrollbarPos;
this.scrollbarPos.register(this);
this.scrollbarVol = scrollbarVol;
this.scrollbarVol.register(this);
this.vlc = document.getElementById(vlc_id).wrappedJSObject;
this.vlc.VLCObj = this;
this.controls = document.getElementById(vlc_id + "_controls");
var btn;
btn = document.getElementById(vlc_id + "_play");
btn.wrappedJSObject.VLCObj = this;
btn.addEventListener('click', VLCObj.prototype.play, true);
btn = document.getElementById(vlc_id + "_pause");
btn.wrappedJSObject.VLCObj = this;
btn.addEventListener('click', VLCObj.prototype.pause, true);
btn = document.getElementById(vlc_id + "_stop");
btn.wrappedJSObject.VLCObj = this;
btn.addEventListener('click', VLCObj.prototype.stop, true);
btn = document.getElementById(vlc_id + "_fs");
btn.wrappedJSObject.VLCObj = this;
btn.addEventListener('click', VLCObj.prototype.fs, true);
//some calling NPObject method exception crap
//$(vlc_id).addEventListener('MediaPlayerOpening', VLCObj.prototype.handleEvents,false);
//$(vlc_id).addEventListener('MediaPlayerBuffering', VLCObj.prototype.handleEvents,false);
//$(vlc_id).addEventListener('MediaPlayerPlaying', VLCObj.prototype.handleEvents,false);
//$(vlc_id).addEventListener('MediaPlayerPaused', VLCObj.prototype.handleEvents,false);
$(vlc_id+'_select').VLCObj = this;
$(vlc_id+'_select').addEventListener('change', function(ev){ this.VLCObj.add(ev.target.value); },false);
this.stateUpdate(this);
},
handleEvents: function(ev){
console.log("Event:"+ ev.type);
console.log("This:"+ ev.target);
},
doAdd: function(src, waitCount){
if(typeof(waitCount) == 'undefined') waitCount = 0;
if(this.vlc.playlist)this.vlc.playlist.items.clear();
var instance = this;
if(this.vlc.playlist.items.count>0 && waitCount < 5){//Old crap in playlist, do not want
setTimeout(function(){instance.doAdd(src, ++waitCount);}, 250);
return;
}
var id = this.vlc.playlist.add(src,'muuvi',':http-caching=5000');
this.vlc.playlist.playItem(id);
},
add: function(src){
this.doAdd(src,0);
if($('vlclink')) $('vlclink').href = src;
},
emitValue:function(sb, obj, pos){
try{
if(obj.scrollbarPos == sb)
obj.vlc.input.position = pos/obj.scrollbarPos.maxValue;
else if(obj.scrollbarVol == sb)
obj.vlc.audio.volume = pos;
}catch(e){
if(console) console.log("emitValue:"+e);
}
},
seekTo: function(pos){ //Make yuutuub comments 'time links' work
this.vlc.input.time = pos * 1000;
},
play: function(){
this.wrappedJSObject.VLCObj.vlc.playlist.play();
},
pause: function(){
this.wrappedJSObject.VLCObj.vlc.playlist.togglePause();
},
stop: function(){
this.wrappedJSObject.VLCObj.vlc.playlist.stop();
},
fs: function(){
//Uh???
this.wrappedJSObject.VLCObj.vlc.video.toggleFullscreen();
},
stateUpdate: function(obj){
try{
obj.scrollbarPos.setValue(obj.vlc.input.position*obj.scrollbarPos.maxValue);
if(obj.vlc.input){
obj.controls.children.namedItem('vlcstate').innerHTML = VLC_status[obj.vlc.input.state];
obj.controls.children.namedItem('vlctime').innerHTML = fmttime(obj.vlc.input.time) + ' / ' + fmttime(obj.vlc.input.length);
}
if(obj.vlc.log && console){
var iter = obj.vlc.log.messages.iterator();
while(iter.hasNext)
console.log(iter.next().message);
}
}catch(e){
if(console) console.log('stateUpdate: '+e);
}
setTimeout(function(){VLCObj.prototype.stateUpdate(obj);}, 250);
},
};
// Main logics
/* Player */
var player = $('watch-player-div');
if(!player)
player = $('watch-player');
if(player) {
player.innerHTML="";
player.className = player.id = "NotFlashPlayer"; //so youtube CSS doesn't interfere
putCSS();
var z = null;
try{
var yt = unsafeWindow['yt'];
var swf_args = yt.getConfig('SWF_ARGS',null);
if(!swf_args)
swf_args = yt.getConfig('PLAYER_CONFIG',null) ['args'];
z = [0, swf_args['video_id'], swf_args['t']];
if($('watch-video').className.indexOf('wide')>-1) {//Quick hack
var ratio = width/height;
width = widthWide;
height = width / ratio;
isWide = true;
}
if(yt.getConfig('IS_WIDESCREEN',false)) height = width * 9/16;
}catch(e){
var scripts = document.getElementsByTagName('script');
for(var i = 0; i < scripts.length && z == null; ++i) {
z = scripts[i].text.match(/video_id": "([^"]+).+, "t": "([^"]+)/); //"
}
}
if(z == null) {
insertYTmessage ('VlcTube: Unable to find video source');
return;
}
var stream_map = new Array();
var urls = swf_args['url_encoded_fmt_stream_map'];
//Hmmmmmm
urls.split(',').forEach(function(map){
var params = map.split('&');
var kv = {};
for(var i=0; i<params.length; i++)
{
var t = params[i].split('=');
kv[t[0]] = unescape(t[1]);
}
stream_map.push( [ kv['quality']+'/'+kv['type'].split(';')[0].split('/')[1] , kv['url']] );
//console.log(kv['quality'] + ':' + kv['type']);
});
var vlc = document.createElement('div');
vlc.id = "vlccontent";
//vlc.setAttribute('style', 'line-height: 100%; font-size:8pt;color: #000;text-align:left; ');
var options = "";
for(var i=0; i<stream_map.length; i++)
{
options += '<option name="'+ stream_map[i][0] +'" value="' + stream_map[i][1] + '">' + stream_map[i][0] + '</option>';
}
vlc.innerHTML =
'<embed type="application/x-vlc-plugin" pluginspage="http://www.videolan.org" version="VideoLAN.VLCPlugin.2" autoplay="yes" width="'+
width +'" height="'+ height+ '" id="'+ vlc_id +'" name="'+ vlc_id +'"/>' +
'<div id="'+vlc_id+'_controls" class="vlccontrols">\
<div id="vlctime">00:00/00:00</div>\
<div id="vlcstate"></div>\
<div id="scrollbar"><div id="knob"></div>Position</div>\
<div id="scrollbar2"><div id="knob2"></div>Volume</div>\
</div><div>\
<input id="'+vlc_id+'_play" class="yt-uix-button" type="button" value="Play"/>\
<input id="'+vlc_id+'_pause" class="yt-uix-button" type="button" value="Pause"/>\
<input id="'+vlc_id+'_stop" class="yt-uix-button" type="button" value="Stop"/>\
<input id="'+vlc_id+'_fs" class="yt-uix-button" type="button" value="FS"/>\
<select id="'+vlc_id+'_select" class="yt-uix-button">' + options + //Kinda fugly
'</select>\
<a href="#" id="vlclink">Download</a></div>';
var myvlc = new VLCObj(), scroll1, scroll2;
window.addEventListener('load', function(e){
// setTimeout(function(){
player.appendChild(vlc);
scroll1 = new ScrollBar();
scroll1.init('scrollbar', 'knob', 0);
scroll2 = new ScrollBar();
scroll2.init('scrollbar2', 'knob2', 0);
myvlc.init(scroll1, scroll2);
myvlc.add("");//Or else 'no mediaplayer' error
restoreSettings();
try{
unsafeWindow['yt'].www.watch.player = myvlc;
}catch(e){
}
if(isWide)
$('watch-sidebar').style.marginTop = "0px";
else
$('watch-sidebar').style.marginTop = (-height - 55) + "px";
if(stream_map.length > 0)
{
$(vlc_id+'_select').dispatchEvent(chngevt);
/*stream_map.forEach(function(i){
if(i[0]==GM_getValue('ytquality', undefined))
myvlc.add(i[1]);
});*/
}
else if(console)
console.log("no stream maps");
// }, 1000);
}, false);
window.addEventListener('unload', saveSettings,true);
}