Enhance twitch

Show images/video in chat, always source quality, Auto click claim bonus, hide offline channels and more

От 06.10.2020. Виж последната версия.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         Enhance twitch
// @namespace    http://tampermonkey.net/
// @version      2.0.0
// @description  Show images/video in chat, always source quality, Auto click claim bonus, hide offline channels and more
// @author       Bum
// @require      http://code.jquery.com/jquery-3.4.1.min.js
// @match        https://www.twitch.tv/*
// @match https://clips.twitch.tv/*
// @grant        none
// ==/UserScript==

var displayImages = "true";
var displayVideos = "true";
var autoPlayClips = "false";
var hideHypeTrain = "false";
var hideBitGiftLeaderBoard = "false";
var hideUnfollowButton = "false";
var ultraWide = "true";
var hideOfflineChannels = "true";
var hideRecommendedChannels = "true";
var hideTags = "false";
var hideSubscribeText = "false";
var alwaysSourceQuality = "true";

if (localStorage.getItem("hideSubscribeText") != null) {
    hideSubscribeText = localStorage.getItem("hideSubscribeText");
}
if (localStorage.getItem("alwaysSourceQuality") != null) {
    alwaysSourceQuality = localStorage.getItem("alwaysSourceQuality");
}
if (localStorage.getItem("hideRecommendedChannels") != null) {
    hideRecommendedChannels = localStorage.getItem("hideRecommendedChannels");
}
if (localStorage.getItem("hideTags") != null) {
    hideTags = localStorage.getItem("hideTags");
}

if (localStorage.getItem("displayImages") != null) {
    displayImages = localStorage.getItem("displayImages");
}
if (localStorage.getItem("hideOfflineChannels") != null) {
    hideOfflineChannels = localStorage.getItem("hideOfflineChannels");
}
if (localStorage.getItem("displayVideos") != null) {
    displayVideos = localStorage.getItem("displayVideos");
}
if (localStorage.getItem("autoPlayClips") != null) {
    autoPlayClips = localStorage.getItem("autoPlayClips");
}
if (localStorage.getItem("hideHypeTrain") != null) {
    hideHypeTrain = localStorage.getItem("hideHypeTrain");
}
if (localStorage.getItem("hideBitGiftLeaderBoard") != null) {
    hideBitGiftLeaderBoard = localStorage.getItem("hideBitGiftLeaderBoard");
}
if (localStorage.getItem("hideUnfollowButton") != null) {
    hideUnfollowButton = localStorage.getItem("hideUnfollowButton");
}

if (localStorage.getItem("ultraWide") != null) {
    ultraWide = localStorage.getItem("ultraWide");
}

if (ultraWide == "true"){

    GM_addStyle(".common-centered-column{    max-width: 100%;}");
}


function GM_addStyle(css) {
    const style = document.getElementById("GM_addStyle") || (function() {
        const style = document.createElement('style');
        style.type = 'text/css';
        style.id = "GM_addStyle";
        document.head.appendChild(style);
        return style;
    })();
    const sheet = style.sheet;
    sheet.insertRule(css, (sheet.rules || sheet.cssRules || []).length);
}

function hideSubButton(){

    let btnSub = $('button[data-a-target="subscribe-button"]');
    if (btnSub.length ==0)
        btnSub = $('button[data-a-target="subscribed-button"]');
    var isReady = btnSub.length > 0;
    if (!isReady) {
        setTimeout(hideSubButton, 300);
        return;
    }

    btnSub.find(".tw-mg-l-05").attr("style","display: none !important");
    btnSub.find('div[data-a-target="tw-core-button-label-text"]').attr("style","display: none !important");
}
$( window ).on( "load", function() {
    if (hideTags == "true")
        $(".tw-tag").attr("style","display:none !important");
    if(hideSubscribeText == "true"){
        hideSubButton();
    }
    AlwaysSourceQuality();
});

function AlwaysSourceQuality(){
    var hidden, state, visibilityChange;
    if (typeof document.hidden !== "undefined") {
        hidden = "hidden";
        visibilityChange = "visibilitychange";
        state = "visibilityState";
    } else if (typeof document.mozHidden !== "undefined") {
        hidden = "mozHidden";
        visibilityChange = "mozvisibilitychange";
        state = "mozVisibilityState";
    } else if (typeof document.msHidden !== "undefined") {
        hidden = "msHidden";
        visibilityChange = "msvisibilitychange";
        state = "msVisibilityState";
    } else if (typeof document.webkitHidden !== "undefined") {
        hidden = "webkitHidden";
        visibilityChange = "webkitvisibilitychange";
        state = "webkitVisibilityState";
    }
    if (alwaysSourceQuality == "true")
    {
        Object.defineProperty(document, hidden, {value: false, writable: false});
        Object.defineProperty(document, state, {value: 'visible', writable: false});
        document.addEventListener(visibilityChange, function(e) {
            window.localStorage.setItem('s-qs-ts', Math.floor(Date.now()));
            window.localStorage.setItem('video-quality', '{"default":"chunked"}');
            e.stopImmediatePropagation();
        }, false);
    }
    else{
        delete document[hidden];
        delete document[state];
    }
}

function RemoveCssRule(css) {
    const style = document.getElementById("GM_addStyle") || (function() {
        const style = document.createElement('style');
        style.type = 'text/css';
        style.id = "GM_addStyle";
        document.head.appendChild(style);
        return style;
    })();
    const sheet = style.sheet;
    for (var i=0; i<sheet.cssRules.length; i++) {
        if (sheet.cssRules[i].selectorText == css) {
            sheet.deleteRule (i);
        }
    }
}

function LoadTwitchEnhance(){
    $('body').click(function(evt){
        $(".fixMenuAppended").removeClass("fixMenuAppended");
    });
    var config = { attributes: false, childList: true, subtree: true };

    function getMenuItem(id, display, checked){
        if (checked == "true"){
            var res = '<div class="tw-pd-05"><div data-a-target="high-contrast-color-checkbox" class="tw-align-items-center tw-flex"><label class="tw-drop-down-menu-input-item__label tw-flex-grow-1 tw-mg-r-2" for="'+id+'">'+display+'</label><div class="tw-toggle" data-a-target="high-contrast-color-checkbox"><input type="checkbox" id="'+id+'" label="Readable Colors" class="tw-toggle__input" data-a-target="tw-toggle" checked=""><label for="'+id+'" class="tw-toggle__button"><p class="tw-hide-accessible">'+display+'</p></label></div></div></div>';
            return res;
        }
        else{
            var res2 = '   <div class="tw-pd-05"><div class="tw-align-items-center tw-flex"><label class="tw-drop-down-menu-input-item__label tw-flex-grow-1 tw-mg-r-2" for="'+id+'">'+display+'</label><div class="tw-toggle"><input type="checkbox" label="Subscribers-Only Chat" id="'+id+'" class="tw-toggle__input" data-a-target="tw-toggle"><label for="'+id+'" class="tw-toggle__button"><p class="tw-hide-accessible">'+display+'</p></label></div></div></div>';
            return res2;
        }
     }

    function addMenu(){
        var menu = $(".chat-settings__content");
        var isReady = menu.length > 0;
        if (!isReady) {
            setTimeout(addMenu, 50);
            return;
        }
        if ($(".fixMenuAppended ").length > 0 )
            return;

        menu.append('<div class="tw-border-t tw-mg-t-1 tw-mg-x-05 tw-pd-b-1 customEnhanceMenu"></div><div class="tw-mg-y-05 tw-pd-x-05"><p class="tw-c-text-alt-2 tw-font-size-6 tw-strong tw-upcase">Enhance Twitch</p></div>');
//alwaysSourceQuality
        menu.append(getMenuItem('alwaysSourceQuality','Always source quality', alwaysSourceQuality));
        $("#alwaysSourceQuality").change(function() {
            localStorage.setItem("alwaysSourceQuality", this.checked);
            alwaysSourceQuality = localStorage.getItem("alwaysSourceQuality");
            AlwaysSourceQuality();
        });


        menu.append(getMenuItem('displayImages','Display Images', displayImages));
        $("#displayImages").change(function() {
            localStorage.setItem("displayImages", this.checked);
            displayImages = localStorage.getItem("displayImages");
        });

        menu.append(getMenuItem('displayVideos','Display Videos',displayVideos));
        $("#displayVideos").change(function() {
            localStorage.setItem("displayVideos", this.checked);
            displayVideos = localStorage.getItem("displayVideos");
        });

        menu.append(getMenuItem('autoPlayClips','Auto play clips',autoPlayClips));
        $("#autoPlayClips").change(function() {
            localStorage.setItem("autoPlayClips", this.checked);
            autoPlayClips = localStorage.getItem("autoPlayClips");
        });
        //hideOfflineChannels

        menu.append(getMenuItem('fixhideOfflineChannels','Hide offline channels',hideOfflineChannels));
        $("#fixhideOfflineChannels").change(function() {
            localStorage.setItem("hideOfflineChannels", this.checked);
            hideOfflineChannels = localStorage.getItem("hideOfflineChannels");
        });

        //hideRecommendedChannels
        menu.append(getMenuItem('fixhideRecommendedChannels','Show only followed channels',hideRecommendedChannels));
        $("#fixhideRecommendedChannels").change(function() {
            localStorage.setItem("hideRecommendedChannels", this.checked);
            hideRecommendedChannels = localStorage.getItem("hideRecommendedChannels");
            if (hideRecommendedChannels == "true")
                hideRecommendedChannelsPlease();
            else{
                RemoveCssRule(".side-nav-section:nth-of-type(n+2)");
            }
        });

        menu.append(getMenuItem('hideHypeTrain','Hide hype train',hideHypeTrain));
        $("#hideHypeTrain").change(function() {
            localStorage.setItem("hideHypeTrain", this.checked);
            hideHypeTrain = localStorage.getItem("hideHypeTrain");
            if (hideHypeTrain == "false")
                RemoveCssRule(".community-highlight-stack__scroll-area--disable");
            else
                GM_addStyle(".community-highlight-stack__scroll-area--disable{display: none !important;}");
        });

        menu.append(getMenuItem('hideBitGiftLeaderBoard','Hide gift/bit leaderboard',hideBitGiftLeaderBoard));
        $("#hideBitGiftLeaderBoard").change(function() {
            localStorage.setItem("hideBitGiftLeaderBoard", this.checked);
            hideBitGiftLeaderBoard = localStorage.getItem("hideBitGiftLeaderBoard");
            if (hideBitGiftLeaderBoard == "false")
                RemoveCssRule(".channel-leaderboard");
            else
                GM_addStyle(".channel-leaderboard{display: none !important;}");
        });

        menu.append(getMenuItem('hideUnfollowButton','Hide unfollow button',hideUnfollowButton));
        $("#hideUnfollowButton").change(function() {
            localStorage.setItem("hideUnfollowButton", this.checked);
            hideUnfollowButton = localStorage.getItem("hideUnfollowButton");
            if (hideUnfollowButton == "false")
                RemoveCssRule('button[data-a-target="unfollow-button"] div:first-child');
            else
                GM_addStyle('button[data-a-target="unfollow-button"] div:first-child{width: 0 !important;}');
        });

        //hideTags
        menu.append(getMenuItem('hideTags','Hide tags',hideTags));
        $("#hideTags").change(function() {
            localStorage.setItem("hideTags", this.checked);
            hideTags = localStorage.getItem("hideTags");
            if (hideTags == "false")
                $(".tw-tag").attr("style","display:flex !important");
            else
                $(".tw-tag").attr("style","display:none !important");
        });

        //hideSubscribeText

        menu.append(getMenuItem('hideSubscribeText','Hide (re)subscribe text',hideSubscribeText));
        $("#hideSubscribeText").change(function() {
            localStorage.setItem("hideSubscribeText", this.checked);
            hideSubscribeText = localStorage.getItem("hideSubscribeText");
            if (hideSubscribeText == "false"){
                let btnSub = $('button[data-a-target="subscribe-button"]');
                if (btnSub.length ==0)
                    btnSub = $('button[data-a-target="subscribed-button"]');
                btnSub.find(".tw-mg-l-05").attr("style","display: block !important");
                btnSub.find('div[data-a-target="tw-core-button-label-text"]').attr("style","display: block !important");
            }
            else{
                hideSubButton();
            }
        });

            $('button[data-a-target="chat-settings"]').addClass("fixMenuAppended");
    }

    var SupportedImageFormats = [".jpg", ".jpeg", ".png", ".webp", ".gif"];
    var SupportedVideoFormats = [".mp4",".webm"];

    var maxHeight = "240";
    var maxWidth = "300";

    GM_addStyle("iframe[class^='imgur-embed']{max-width: "+maxWidth+"px !important;}");
    GM_addStyle("svg[class*='logotwitchwordmark']{display: none !important;}");
    if (hideHypeTrain == "true")
        GM_addStyle(".community-highlight-stack__scroll-area--disable{display: none !important;}");
    if (hideBitGiftLeaderBoard == "true")
        GM_addStyle(".channel-leaderboard{display: none !important;}");
    GM_addStyle(".btnRefreshEnhance button{height: 15px; width: 100%}");
    GM_addStyle(".chat-settings{    max-height: 500px !important;    overflow: hidden !important;}");

    function isSupportedImage(url) {
        var length = SupportedImageFormats.length;
        while(length--) {
            if (url.indexOf(SupportedImageFormats[length])!=-1) {
                return true;
            }
        }
        return false;
    }
    function isSupportedVideo(url) {
        var length = SupportedVideoFormats.length;
        while(length--) {
            if (url.indexOf(SupportedVideoFormats[length])!=-1) {
                return true;
            }
        }
        return false;
    }

    function alterNode(node) {
        var thisUrl = $(node).text();
        var parentToScroll = $(node).parent().parent().parent().parent();
        if (displayImages == "true" && isSupportedImage(thisUrl)){
            $(node).html("<br><img src='" + thisUrl + "' width='" + maxWidth +"px'/>");
            parentToScroll.animate({scrollTop:parentToScroll.scrollTop() + 500}, 'slow');
        }
        else if (displayVideos == "true" && thisUrl.indexOf("www.youtube") > 0 ){
            if (thisUrl.indexOf("watch") > 0){
                var videoId  = thisUrl.match('v=([^&]*)')[1];
                $(node).html('<br><iframe width="'+ maxWidth +'" height="'+ maxHeight +'" src="https://www.youtube.com/embed/' + videoId+'"></iframe>');
                parentToScroll.animate({scrollTop:parentToScroll.scrollTop() + 500}, 'slow');
            }
        }
        else if (displayVideos == "true" && isSupportedVideo(thisUrl)){
           $(node).html('<br><video width="'+ maxWidth +'" height="'+ maxHeight +'" controls autoplay muted><source src="'+thisUrl +'" type="video/mp4"></video>');
            parentToScroll.animate({scrollTop:parentToScroll.scrollTop() + 500}, 'slow');
        }
        else if (displayImages == "true" && thisUrl.indexOf("https://gyazo.com") > -1 ){
           $(node).html("<br><img src='" + thisUrl.replace("https://gyazo.com", "https://i.gyazo.com") +".png' width='" + maxWidth +"px'/>");
            parentToScroll.animate({scrollTop:parentToScroll.scrollTop() + 500}, 'slow');
        }
        else if (displayImages == "true" && thisUrl.indexOf("imgur") > -1){
            var imgurId = "";
            if (thisUrl.indexOf("gallery") > -1){
                imgurId = thisUrl.match('gallery\/([^#]*)')[1];
                try{
                    $(node).html('<div style="width:200"><blockquote class="imgur-embed-pub" lang="en" data-id="a/'+ imgurId +'"><a href="//imgur.com/'+ imgurId +'" ></a></blockquote><script async src="//s.imgur.com/min/embed.js" charset="utf-8"></script></div>');
                }
                catch(err){
                }
            }
            else{
                imgurId = thisUrl.match('a\/([^#]*)')[1];
                try{
                    $(node).html('<div style="width:200"><blockquote class="imgur-embed-pub" lang="en" data-id="a/'+ imgurId +'"><a href="//imgur.com/'+ imgurId +'" ></a></blockquote><script async src="//s.imgur.com/min/embed.js" charset="utf-8"></script></div>');
                }
                catch(err){
                }
            }
            parentToScroll.animate({scrollTop:parentToScroll.scrollTop() + 500}, 'slow');
        }
        else if (displayVideos == "true" && thisUrl.indexOf("clips.twitch.tv") > -1){
            var clipId = thisUrl.match('.tv\/(.*)')[1];
            $(node).html('<iframe src="https://clips.twitch.tv/embed?clip='+clipId+'&autoplay='+autoPlayClips+'&muted=true&parent=twitch.tv" frameborder="0"   allowfullscreen="true" height="'+maxHeight+'" width="'+maxWidth+'"></iframe>');
            parentToScroll.animate({scrollTop:parentToScroll.scrollTop() + 500}, 'slow');
        }
        else if (displayVideos == "true" && thisUrl.indexOf("twitch") > -1 && thisUrl.indexOf("clip") > -1){
            var clipId1 = thisUrl.match('clip\/([^?]*)')[1];
            $(node).html('<iframe src="https://clips.twitch.tv/embed?clip='+clipId1+'&autoplay='+autoPlayClips+'&muted=true&parent=twitch.tv" frameborder="0"   allowfullscreen="true" height="'+maxHeight+'" width="'+maxWidth+'"></iframe>');
            parentToScroll.animate({scrollTop:parentToScroll.scrollTop() + 500}, 'slow');
        }
    }
    // Callback function to execute when mutations are observed
    var callback = function(mutationsList, observer) {
        for(var mutation of mutationsList)  {
            mutation.addedNodes.forEach(function(node) {
                if (node.classList != undefined && node.classList.contains('link-fragment')) {
                    alterNode(node);
                }
                if (node.querySelectorAll){
                    node.querySelectorAll('.link-fragment').forEach(function(node) {
                        alterNode(node);
                    })
                    node.querySelectorAll('.text-fragment').forEach(function(node) {
                        alterNode(node);
                    })
                }
            });
        }
    };
    var callbackAddMenu = function(mutationsList, observer) {
        $('button[data-a-target="chat-settings"]:not(.fixMenuAppended)').click(function(){
            addMenu();
        });
    };
    var callbackClaim = function(mutationsList, observer) {
        for(var mutation of mutationsList) {
            $(".claimable-bonus__icon").click();
        }
    };


    var observerMenu = new MutationObserver(callbackAddMenu);
    // Create an observer instance linked to the callback function
    var observer = new MutationObserver(callback);
    function _appendObserver() {
        let isReady = $("div.chat-list--default").length > 0;
        console.log("waiting");
        if (!isReady) {
            setTimeout(_appendObserver, 500);
            return;
        }
        if (!$("div.chat-list--default").hasClass("enhanceAppended")){
            $("div.chat-list--default").addClass("enhanceAppended");
            var targetNode = $("div.chat-list--default").get(0);
            console.log(targetNode);
            var targetNode2 = $(".right-column").get(0);
            console.log(targetNode2);
            // Start observing the target node for configured mutations
            observer.observe(targetNode, config);
            observerMenu.observe(targetNode2, config);
        }
    }
    _appendObserver();

    var targetNodeRoot = document.getElementById('root');
    var configRoot = { attributes: false, childList: true, subtree: true };

    var callbackRoot = function(mutationsList, observer) {
        $(".claimable-bonus__icon").click();
        _appendObserver();
        if (hideTags == "true")
            $(".tw-tag").attr("style","display:none !important");
        if (hideOfflineChannels == "true"){
            for(var mutation of mutationsList){
                mutation.addedNodes.forEach(function(node) {
                    //side-nav-card__avatar
                    var channelStatus = $(node).find(".side-nav-card__avatar");
                    var offlineChannels = $(node).find(".side-nav-card__avatar--offline");
                    if (offlineChannels.length > 0){
                        var parentStatus = offlineChannels.parent();
                        parentStatus.attr("style","display:none !important;");
                    }
                    if (channelStatus.length > 0){
                        var elemToObserve = channelStatus.get(0);
                        var observer = new MutationObserver(function(mutations) {
                            mutations.forEach(function(mutation) {
                                var channelIsOffline = mutation.target.classList.contains('side-nav-card__avatar--offline');
                                var parentStatus = $($(mutation)[0].target).parent();
                                if(channelIsOffline){
                                    parentStatus.attr("style", "display:none !important;");
                                }
                                else{
                                    parentStatus.attr("style","display:inherit !important;");
                                    $('button[data-test-selector="ShowMore"]').click();
                                }
                            });
                        });
                        observer.observe(elemToObserve, {attributes: true});
                    }
                });
            }
        }

        if (window.location.href == "https://www.twitch.tv/"){
            var videoMainPage = $(".featured-content-carousel:not(.fixVideoPausedOnce)").find("video");
            if (videoMainPage.length > 0){
                videoMainPage.get(0).pause();
                videoMainPage.parent().click(function(){
                    $(".featured-content-carousel").addClass('fixVideoPausedOnce');
                });
            }
        }

        var linkArg = $(".social-media-space--content").find(".tw-avatar").attr("aria-label");
        if ($(".social-media-space--content:not(.fixLinksAppended)").length > 0 || $(".social-media-space--content").attr("id") != "fix" + linkArg){
            var appendable = $(".social-media-space--content").find('.tw-title').parent();
            if (appendable.length>0) {
                $("#fixVideosLinks").remove();
                $("#fixClipsLinks").remove();
                $("#fixSpanLinks").remove();
                appendable.append('<a target="_blank" id ="fixVideosLinks" href="/'+linkArg+'/videos"><span>Videos</span></a><span id = "fixSpanLinks"> / </span>');
                appendable.append('<a target="_blank" id ="fixClipsLinks" href="/'+linkArg+'/clips?filter=clips&range=7d"><span>Clips</span></a>');
                $(".social-media-space--content").addClass("fixLinksAppended");
                $(".social-media-space--content").attr("id","fix"+linkArg);
                if (hideTags == "true")
                    $(".tw-tag").attr("style","display:none !important");
                if(hideSubscribeText == "true"){
                    hideSubButton();
                }
                if (hideUnfollowButton == "true")
                    GM_addStyle('button[data-a-target="unfollow-button"] div:first-child{width: 0 !important;}');
            }
        }
    };

    // Create an observer instance linked to the callback function
    var observerroot = new MutationObserver(callbackRoot);

    // Start observing the target node for configured mutations
    observerroot.observe(targetNodeRoot, config);

    var callbackQuality = function(mutationsList, observer) {
        localStorage.setItem('video-quality', '{"default":"chunked"}');
    };

    function _appendQualityObserver() {
        var isReady = $('div[data-a-target="player-controls"]').length > 0;
        if (!isReady) {
            setTimeout(_appendQualityObserver, 500);
            return;
        }
        var targetNodeQuality = $('div[data-a-target="player-controls"]').get(0);
        var observerQuality = new MutationObserver(callbackQuality);
        observerQuality.observe(targetNodeQuality, config);
    }

    function hideRecommendedChannelsPlease(){
        GM_addStyle(".side-nav-section:nth-of-type(n+2){    display:none;}");
    }

    if (hideRecommendedChannels == "true"){
        hideRecommendedChannelsPlease();
    }

}

(function() {
    'use strict';
    LoadTwitchEnhance();
})();