VLCTube

By FurYy Last update Nov 24, 2011 — Installed 12,592 times.

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);
}