SeenChute

BitChute.com. Adds a "watched" bar to top of video cards.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

You will need to install an extension such as Tampermonkey to install this script.

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name            SeenChute
// @version         19.12.7
// @description     BitChute.com. Adds a "watched" bar to top of video cards.
// @license         MIT
// @author          S-Marty
// @compatible      firefox
// @compatible      chrome
// @compatible      opera
// @namespace       https://github.com/s-marty/SeenChute
// @homepageURL     https://github.com/s-marty/SeenChute
// @icon            https://raw.githubusercontent.com/s-marty/SeenChute/master/images/seenChute.png
// @contributionURL https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QHFFSLZ7ENUQN&source=url
// @include         /^https?://www\.bitchute\.com/.*$/
// @run-at          document-end
// @grant           GM.getValue
// @grant           GM.setValue
// @noframes
// ==/UserScript==

/* greasyfork.org jshint syntax checking hacks */
/* jshint asi: true */
/* jshint boss: true */
/* jshint esversion: 6 */
/* jshint multistr: true */

/** **********************   Features   **********************
*** Adds a red metered bar over your watched videos by percent.
*** Videos which are watched not more than 1 % will be ignored.
*** Database size is auto-manageable.  Defaults to 2000 videos.
***     Set "var limit_database_To = " to any whole integer.
***     The oldest database records will be truncated first.
*** Bar colors may be edited as well.
*** See "Editable options" in source code below.
*** No extra @require files (jquery et.al.)

***  ***  Does not & will not work well with IE and IEdge  ***/


/* Editable options */
var limit_database_To = 2000;         /* 0 for unlimited. i.e. 5000 to save only the latest 5000 videos */
var bar_top_color     = "#CC3333";    /* A hexadecimal color specified as: #RRGGBB, where the RR (red), GG (green) and BB (blue)*/
var bar_middle_color  = "#F05555";    /* Hex integers specify the components of the color. Values must be between 00 and FF. */
var bar_bottom_color  = "#000000";    /* Edits made here will be lost during userscript updates. Database data survives updates */
/* End Editable options */


(function() {
    "use strict";

    var BC = {};
    var d = document;
    var videoId = '';
    var updater = null;
    var unloader = null;
    var videoViewedMax = 0;
    var listingsAllHeight = 0;
    var listingsPopHeight = 0;
    var channelVideoHeight = 0;

    function iHaveSeen(e) {

        BC.url          = window.location.href;
        BC.host         = window.location.hostname;
        BC.path         = window.location.pathname;
        BC.searchpage   = BC.url.indexOf('/search') !=-1;
        BC.watchpage    = BC.path.indexOf('/video') !=-1;
        BC.profilepage  = BC.path.indexOf('/profile/') !=-1;
        BC.channelpage  = BC.path.indexOf('/channel/') !=-1;
        BC.hashtagpage  = BC.path.indexOf('/hashtag/') !=-1;
        BC.categorypage = BC.path.indexOf('/category/') !=-1;
        BC.playlistpage = BC.path.indexOf('/playlist/') !=-1;
        BC.homepage     = BC.url == location.protocol +"//"+ BC.host +"/";

        if (!BC.loaded) {
            if (!BC.loader) {
                if (BC.loader = qs("#loader-container")) {
                    addListener(BC.loader, function(e) {
                        if (e.target.style.display == 'none') iHaveSeen(e);
                    },{ attributes: true, attributeFilter: ['style'] });
                }
            }

            let style = d.createElement("style");
            style.type = "text/css";
            style.innerText = '\
                    div.video-seen {height: 3px; margin: 0px; padding: 0px; background-color: '+ bar_middle_color +'; border: 0px; \
                    border-top: 1px solid '+ bar_top_color +'; border-bottom: 1px solid '+ bar_bottom_color +'; overflow: hidden;}';
            d.documentElement.appendChild(style);
            BC.loaded = 1;
        }
        else {
            if (BC.page == 'watchpage') {
                watchedlistAdd();
            }
        }

        if (BC.watchpage) {
            BC.page = 'watchpage';
            videoViewedMax = 0;
            videoId = BC.path.match( /video\/([a-z0-9_-]+)\//i )[1];

            if (! BC.api || ! updater) {
                apiUpdater()
            }
            if (! unloader) {
                window.addEventListener('beforeunload', function(e){ watchedlistAdd(e); }, false);
                unloader = true;
            }
            applySeenBars(3000);
            window.setTimeout(function() { showMoreListen(); }, 5000);
        }
        else if (BC.profilepage || BC.hashtagpage || BC.playlistpage) {
            BC.page = 'profilepage';
            applySeenBars();
        }
        else if (BC.channelpage) {
            BC.page = 'channelpage';
            let channelTabs = qs('#channel-tabs.seeing');
            let listingsChannel = qs('.channel-videos-list');
            if (!channelTabs) {
                addListener(listingsChannel, function(e) {
                    let newlistings = qs('.channel-videos-list');
                    let newlistingsHeight = Math.round(newlistings.getBoundingClientRect().height);
                    if (channelVideoHeight != newlistingsHeight) {
                        channelVideoHeight = newlistingsHeight;
                        applySeenBars();
                    }
                },{ childList: true });

                qs('#channel-tabs').classList.add('seeing');
            }

            applySeenBars();
        }
        else if (BC.homepage || BC.categorypage) {
            BC.page = 'homepage';
            let listingTabs = qs('#listing-tabs.seeing');
            let listingsAll = qs('#listing-all > div.row');
            let listingsPopular = qs('#listing-popular > div.row');

            if (!listingTabs) {
                qs("ul.nav-tabs-list li a[href='#listing-all']")
                  .addEventListener('click', function(e){ applySeenBars() }, false);
                qs("ul.nav-tabs-list li a[href='#listing-popular']")
                  .addEventListener('click', function(e){ applySeenBars() }, false);
                qs("ul.nav-tabs-list li a[href='#listing-subscribed']")
                  .addEventListener('click', function(e){ applySeenBars() }, false);
                qs("ul.nav-tabs-list li a[href='#listing-trending']")
                  .addEventListener('click', function(e){ applySeenBars(); trendingTabs() }, false);

                addListener(listingsAll, function(e) {
                    let newlistings = qs('#listing-all > div.row');
                    let newlistingsHeight = Math.round(newlistings.getBoundingClientRect().height);
                    if (listingsAllHeight != newlistingsHeight) {
                        listingsAllHeight = newlistingsHeight;
                        applySeenBars();
                    }
                },{ childList: true });

                addListener(listingsPopular, function(e) {
                    let newlistings = qs('#listing-popular > div.row');
                    let newlistingsHeight = Math.round(newlistings.getBoundingClientRect().height);
                    if (listingsPopHeight != newlistingsHeight) {
                        listingsPopHeight = newlistingsHeight;
                        applySeenBars();
                    }
                },{ childList: true });

                qs('#listing-tabs').classList.add('seeing');
            }
            listingsAllHeight = Math.round(listingsAll.getBoundingClientRect().height);
            listingsPopHeight = Math.round(listingsPopular.getBoundingClientRect().height);
            applySeenBars();
        }
        else return;
    }

    function trendingTabs(e) {
        qs("ul.nav.nav-tabs li a[href='#trending-day']")
          .addEventListener('click', function(e){ applySeenBars() }, false);
        qs("ul.nav.nav-tabs li a[href='#trending-week']")
          .addEventListener('click', function(e){ applySeenBars() }, false);
        qs("ul.nav.nav-tabs li a[href='#trending-month']")
          .addEventListener('click', function(e){ applySeenBars() }, false);
    }

    function apiUpdater() {
        if (BC.api = qs('video#player')) {
            if (! updater) {
                BC.api.addEventListener('timeupdate', function(e){ onPlayProgress(e); }, false);
                updater = true;
            }
        }
        else window.setTimeout(apiUpdater, 1000)
    }

    function onPlayProgress(e) {
        if (! BC.api) return;
        let active, liveBar, current, i;
        let duration = parseFloat(BC.api.duration);
        let valuenow = parseFloat(BC.api.currentTime);
        let completed = Math.ceil(valuenow / duration * 100);
        if (completed > videoViewedMax && completed <= 100) {
            videoViewedMax = completed;
            active = qsa('.video-card.active');
            if (active.length) {
                for (i = 0; i < active.length; i++) {
                    if (liveBar = active[i].querySelector('.video-seen')) {
                        current = parseInt(liveBar.style.width);
                        if (videoViewedMax > current) {
                            liveBar.title = videoViewedMax +'% Watched';
                            liveBar.style.width = videoViewedMax +'%';
                        }
                    }
                    else {
                        let card = active[i].querySelector('.video-card-image');
                        let bar = d.createElement("div");
                        bar.innerText = "&nbsp;";
                        bar.className = "video-seen";
                        bar.title = videoViewedMax +'% Watched';
                        bar.style.width = videoViewedMax +'%';
                        card.insertBefore(bar, card.firstChild);
                    }
                }
            }
        }
        if (completed == 100) {
            watchedlistAdd();
        }
    }

    function applySeenBars(ms = 2000) {
        window.setTimeout(_applySeenBars, ms);
    }

    function _applySeenBars(e) {
        let i, n,
            cards = [],
            selector = '',
            selectors = [
                '.video-card', 
                '.video-trending-image-container', 
                '.channel-videos-container', 
                '.image-container'
            ];

        selectors.some(function(item) {
            if (qs(item) !== null) {
                selector = item.split(',').join(':not([seen]), ') + ':not([seen])';
                if (cards.length) {
                    cards = cards.concat(Array.prototype.slice.call(qsa(selector)));
                }
                else {
                    cards = Array.prototype.slice.call(qsa(selector));
                }
            }
        });

        if (cards.length) {
            try {
                for (i = 0; i < cards.length; i++) {
                    let link = cards[i].querySelector('a');
                    let card = cards[i].querySelector('.video-card-image, .video-trending-image, .channel-videos-image, .image');
                    if (card) {
                        let href = link.getAttribute("href");
                        let video = href.match( /\/video\/([a-z0-9_-]+)\//i );
                        if (video) {
                            if (BC.watched.has(video[1])) {
                                let bar = d.createElement("div");
                                bar.innerText = "&nbsp;";
                                bar.className = "video-seen";
                                bar.title = BC.watched.get(video[1]) +'% Watched';
                                bar.style.width = BC.watched.get(video[1]) +'%';
                                card.insertBefore(bar, card.firstChild);
                            }
                        }
                    }
                    cards[i].setAttribute('seen', 'true')
                }
            } catch (e) {console.error('SeenChute: applyWatchedlist: '+ e);}
        }
    }

    function showMoreListen() {
        let showMore = qs('.show-more');
        if (showMore) {
            showMore.addEventListener('click', function(e) {
                setTimeout(function() {
                    applySeenBars();
                    showMoreListen();
            }, 2000)}, false)
        }
    }

    function watchedlistAdd(e) {
        if (BC.page != 'watchpage') return false;
        let n, update = false;
        if (videoId && videoViewedMax > 1) {
            if (BC.watched.has(videoId)) {
                if (BC.watched.get(videoId) < videoViewedMax) {
                    BC.watched.set(videoId, videoViewedMax)
                }
                update = true;
            }
            if (! update) {
                BC.watched.set(videoId, videoViewedMax);
                let limit = limit_database_To ? parseInt(limit_database_To) : 0;
                if (limit && BC.watched.size > limit) {
                    do {
                        BC.watched.delete(BC.watched.keys().next().value)
                    } while (BC.watched.size > limit)
                }
            }
            GM.setValue('watched', JSON.stringify(Array.from(BC.watched)))
        }

        videoId = '';
        BC.api = null;
        updater = null;
        videoViewedMax = 0;

        return false;
    }

    function qs(selector) { return document.querySelector(selector) }

    function qsa(selector) { return document.querySelectorAll(selector) }

    function addListener(target, fn, config) {
        // jshint ignore:start
        var cfg = {...{attributes:!1, childList:!1, characterData:!1, subtree:!1}, ...config};
        // jshint ignore:end
        var observer = new MutationObserver(function(mutations) {
          mutations.forEach(function(mutation) { fn(mutation) })});
        observer.observe(target, cfg);
        return observer
    }

    function init(e) {
        GM.getValue('watched', "[]").then(function (value) {
            BC.watched = [];
            BC.page = '';
            BC.api = null;
            BC.url = null;
            BC.host = null;
            BC.path = null;
            BC.loaded = !1;
            BC.loader = null;

            if (value && value != '[]') {
                BC.watched = new Map(JSON.parse(value));
            }
            else {
                    /* Install Database */
                GM.setValue('watched', '[ ]');
                window.location.replace(window.location.href);
            }
        }).catch (error => {
            console.error('SeenChute: Error in promise loading watched list: '+ error)
        })
        window.setTimeout(iHaveSeen, 5000)
    }

      /* Not in Frames */
    if (window.self == window.top) init()

}) ();