Facebook Videos Downloader - Fork

Add download links for facebook videos (support video on newfeeds and single video page). Fork from EThaiZone version.

// ==UserScript==
// @name            Facebook Videos Downloader - Fork
// @description     Add download links for facebook videos (support video on newfeeds and single video page). Fork from EThaiZone version.
// @author          nhtera
// @match     //*.facebook.com/
// @version 0.0.3.1
// @namespace https://greasyfork.org/users/16893
// ==/UserScript==

(function () {
    
    // Get the side bar so we can append to it later
    var sidebar = document.getElementById('fbPhotoPageActions');
    
    function closest(el, selector) {
        var matchesFn;

        // find vendor prefix
        ['matches','webkitMatchesSelector','mozMatchesSelector','msMatchesSelector','oMatchesSelector'].some(function(fn) {
            if (typeof document.body[fn] == 'function') {
                matchesFn = fn;
                return true;
            }
            return false;
        })

        // traverse parents
        while (el!==null) {
            var parent = el.parentElement;
            if (parent!==null && parent[matchesFn](selector)) {
                return parent;
            }
            el = parent;
        }

        return null;
    }
    
    function isElementInViewport (el) {

        //special bonus for those using jQuery
        if (typeof jQuery === "function" && el instanceof jQuery) {
            el = el[0];
        }

        var rect = el.getBoundingClientRect();

        return (
            rect.top >= 0 &&
            rect.left >= 0 &&
            rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
            rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
        );
    }

    function renderFBDownloader(counter) {

        // Get all the <embed> elements
        var embedElements = document.querySelectorAll('embed[flashvars]');

        // Flag if we found the video url or not
        var found = false;

        for (var i = 0; i < embedElements.length; i++) {
            
                    debugger
            // Get the flashvars attribute and decode it
            var flashvars = decodeURIComponent(embedElements[i].getAttribute('flashvars'));
            var videoDiv = closest(embedElements[i], "div"); //"video");
            if (videoDiv)  videoDiv = videoDiv.parentNode
            else videoDiv = embedElements[i].parentNode ;

            // Check if this string contains the code we're looking for
            var hd_src_index = flashvars.indexOf('hd_src');
            var p_width_index = flashvars.indexOf('&width=');
            if (hd_src_index > -1 && p_width_index > -1) {
                // This string contains the payload we are looking for so parse it
                var obj = JSON.parse(flashvars.slice(7, p_width_index));
                //var video_data = obj.video_data[0];
                var video_data = obj.video_data.progressive[0];

                //var title = video_data.video_id;
                var title = video_data.video_id;
                if(document.querySelectorAll('h2.uiHeaderTitle')[0] !== undefined){
                    if(document.querySelectorAll('h2.uiHeaderTitle')[0].innerText.length > 0){
                        title = document.querySelectorAll('h2.uiHeaderTitle')[0].innerText;
                    }
                }

                // High Def
                if (video_data.hd_src)
                {
                    var hd_link = document.createElement('a');
                    hd_link.href = video_data.hd_src;
                    hd_link.innerHTML = 'Download HD Video';
                    hd_link.className = 'fbPhotosPhotoActionsItem';
                    hd_link.download = title + '_hd.mp4';
                    hd_link.target = "_blank";
                    hd_link.style.position  = "relative";
                    hd_link.style.zIndex = 9999;
                    
                    
                    if(sidebar){
                        if(!sidebar.getAttribute("data-linkdownhdloadadded")){
                            sidebar.appendChild(hd_link);
                            sidebar.setAttribute('data-linkdownhdloadadded', true);
                        }
                    }
                    
                    if(videoDiv){
                        if(!videoDiv.getAttribute("data-linkdownhdloadadded")){
                            hd_link.style.margin = "5px 0 0 5px";
                            hd_link.style.color = "red";
                            hd_link.style.cssFloat = "left";
                            videoDiv.appendChild(hd_link);
                            videoDiv.setAttribute('data-linkdownhdloadadded', true);
                        }
                    }
                }

                // Low Def
                if (video_data.sd_src)
                {
                    var sd_link = document.createElement('a');
                    sd_link.href = video_data.sd_src;
                    sd_link.innerHTML = 'Download SD Video';
                    sd_link.className = 'fbPhotosPhotoActionsItem';
                    sd_link.download = title + '_sd.mp4';
                    sd_link.target = "_blank";
                    sd_link.style.position  = "relative";
                    sd_link.style.zIndex = 9999;
                    
                    if(sidebar){
                        if(!sidebar.getAttribute("data-linkdownsdloadadded")){
                            sidebar.appendChild(sd_link);
                            sidebar.setAttribute('data-linkdownsdloadadded', true);
                        }
                    }
                    
                    if(videoDiv){
                        if(!videoDiv.getAttribute("data-linkdownsdloadadded")){
                            sd_link.style.color = "red";
                            sd_link.style.cssFloat = "right";
                            sd_link.style.margin = "5px 5px 0 0";
                            videoDiv.appendChild(sd_link);
                            videoDiv.setAttribute('data-linkdownsdloadadded', true);
                        }
                    }
                }

                found = true;
            } // end if

        } // end loop

        if (!found && counter > 20) {
            //var not_found = document.createElement('span');
            //not_found.innerHTML = 'No download link :(';
            //sidebar.appendChild(not_found);
        }
        
        return found;
    }

    var counter = 0;
    function doExec() {
        counter++;
        try {
            log("Find flashvars. " + counter);
            if (renderFBDownloader(counter) == true) {
                log("Video links rendered.");
            } else {
                setTimeout(doExec, 1000);
                log("Try again.");
            }
        } catch(e) {
            log("Error" + e);
            setTimeout(doExec, 1000);
        }
    }

    function log(msg) {
        //alert(msg);
        console.log("[FB Video Downloader] " + msg);
    }
    log("First Start.");
    doExec();
    
    function checkVideoInViews() {
       var videoElements = document.querySelectorAll('video');
        if(videoElements.length > 0){
            var checkVideoOnView = isElementInViewport(videoElements[0]);
            if(checkVideoOnView){
                doExec();
            }
        }
    }

    var patternfbh = /facebook\.com\/(.*?)/;
    if (!sidebar && document.URL.match(patternfbh)) {
        if (window.removeEventListener) {
            window.removeEventListener("scroll", checkVideoInViews);
        } else if (window.detachEvent) {
            window.detachEvent("scroll", checkVideoInViews);
        }
        
        if (window.addEventListener) {
            window.addEventListener("scroll", checkVideoInViews, false);
        } else if (window.attachEvent)  {
            window.attachEvent("onscroll", checkVideoInViews, false);
        }
    }
    
})();