Enhanced Video Playing Experience for Viu.com

Automatically Full HD Video Quality (Paid Member) and Disable Auto Playing when the page is randomly reloaded

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         Enhanced Video Playing Experience for Viu.com
// @version      1.5
// @description  Automatically Full HD Video Quality (Paid Member) and Disable Auto Playing when the page is randomly reloaded 
// @match        https://www.viu.com/ott/*/vod/*
// @icon         https://www.google.com/s2/favicons?domain=viu.com
// @grant        unsafeWindow
// @grant        window.onurlchange
// @namespace https://greasyfork.org/users/371179
// ==/UserScript==
(function $$() {
    'use strict';

    if (!document || !document.documentElement) return window.requestAnimationFrame($$);

const uWin = window.unsafeWindow || window;


    if ([document.hidden, window.requestAnimationFrame, Object.defineProperty, window.performance].some(x => x === undefined)) throw 'Your browser is too outdated.';
    var navStart = performance.timeOrigin || performance.timing.navigationStart || null;
    if (navStart === null) throw 'Your browser is too outdated.';
    navStart = Math.ceil(navStart);

    var lastPlayingStatus = null;
    var settings = {}
    var falseReloaded = false;
    var lastUserClickAt = 0;
    var lastUserClickStatus = 0;
    var forceQuality = "1080";
    const __jver__ = "20210617b";

    const isPassiveOptionEnable = window.queueMicrotask?true:false; //here just a simple trick for checking browser is modern or not


        function isVideoPlaying(video){
            return video.currentTime > 0 && !video.paused && !video.ended && video.readyState > video.HAVE_CURRENT_DATA;
        }

    function pageInit() {


        var _userPaused = null;

        Object.defineProperty(settings, 'userPaused', {
            get() {
                return _userPaused;
            },
            set(nv) {
                _userPaused = nv;
                console.log('[viu] userPaused: ', _userPaused);
            }
        })



        function doSomething(obj) {
            //for viu page auto reload ( it is confirmed that the page could randomly reload)


            if (!obj) return;
            let {
                version,
                datetime,
                pageVisible,
                withVideo,
                userPaused
            } = obj;

            if (version != __jver__) return;

            const isReload = (t) => (t > 0 && t - 2 < navStart && t + 480 > navStart); // max 155ms  => 155*3=465 => 480ms

            if (isReload(datetime) && withVideo) {
                console.log('[viu] reloaded page', obj)
                if (userPaused || !pageVisible) settings.userPaused = true;
            }

            console.log('[viu] reload time diff: ' + (navStart - datetime) + ' => ' + isReload(datetime))


        }

        var _saveObj = localStorage.__zvpp_unload1__;
        if (typeof _saveObj == 'string' && _saveObj.length > 0) {

            var _jObj = null;
            try {
                _jObj = JSON.parse(_saveObj);
            } catch (e) {}
            if (_jObj) {
                doSomething(_jObj);
            }
        }


        delete localStorage.__viu_js_fewunaznqjrx__
        delete localStorage.__viu_js_fewunaznqjr2__
        delete localStorage.__viu_js_fewunaznqjra__
        delete localStorage.__zvpp_unload1__

        if (localStorage.__zvpp_ver__ !== __jver__) {
            for (var k in localStorage) {
                if (k.indexOf('__zvpp_') === 0) localStorage.removeItem(k)
            }
            localStorage.__zvpp_ver__ = __jver__
        }




    }


    function getVideoFromEvent(evt) {
        return (evt && evt.target && evt.target.nodeName == 'VIDEO') ? evt.target : null;
    }

    function hasOffsetParent(v) {
        return v && v.offsetParent !== null && v.offsetParent.nodeType === 1; //is DOM valid on the page
    }

    function noAutoStart(evt) {
        const video = getVideoFromEvent(evt);

        if (video) {
            if (lastUserClickStatus===2) {
                //do nothing for user click
            } else if (settings.userPaused === true) {
                video.autoplay = false;
                video.pause();
            } else {
                if (lastPlayingStatus === true && document.hidden === true) {} else if (document.hidden === true) {
                    video.autoplay = false;
                    video.pause();
                }
            }
        }

    }

    const delayCall = function(p, f, d) {

        if (delayCall[p] > 0) clearTimeout(delayCall[p])
        delayCall[p] = setTimeout(f, d)
    }


    function loader(detection) {
        return function() {
            let oldHref = document.location.href,
                bodyDOM = document.querySelector("body");
            const observer = new MutationObserver(function(mutations) {
                if (oldHref != document.location.href) {
                    oldHref = document.location.href;
                    detection();
                    window.requestAnimationFrame(function() {
                        let tmp = document.querySelector("body");
                        if (tmp != bodyDOM) {
                            bodyDOM = tmp;
                            observer.observe(bodyDOM, config);
                        }
                    })
                }
            });
            const config = {
                childList: true,
                subtree: true
            };
            observer.observe(bodyDOM, config);
        }
    }

    const anyRecentClick=()=>lastUserClickAt + 5000 > +new Date;


    function pageEnableAutoQualityAtStart() {
        //default 1080p



        var cid = 0;
        var mDate = 0;
        const TIMEOUT = 8000; // just in case DOM is not found


        var gn = function(evt) {

            const video = getVideoFromEvent(evt);
            if (!video) return;


            delayCall('$$video_init', function() {

                if (video.hasAttribute('_viu_js_hooked')) return;
                video.setAttribute('_viu_js_hooked', '')
                video.addEventListener('playing', function(evt) {

                    const video = getVideoFromEvent(evt);
                    if (!video) return;
                    delayCall('$$video_playing_switch', function() {
                        if (video.paused === false && hasOffsetParent(video)) {
                            if (document.hidden !== true && settings.userPaused === true) settings.userPaused = false;
                        }
                    }, 300);
                }, true)
                video.addEventListener('pause', function(evt) {
                    const video = getVideoFromEvent(evt);
                    if (!video) return;
                    delayCall('$$video_playing_switch', function() {

                        if (video.paused === true && hasOffsetParent(video)) {
                            if (document.hidden !== true && (settings.userPaused === null || settings.userPaused === false)) settings.userPaused = true;
                        }
                    }, 300);
                }, true)

            }, 800);

            //call when the first video event fires
            if(lastUserClickStatus===1 && lastUserClickAt+5000 > +new Date){
                //url change: 2598  1443 1542 1975
                //without url change : 561
                console.log(`[viu] click and video status change ${+new Date -lastUserClickAt}`)
                //user perform action and video status changed
                lastUserClickStatus=2;
            }
            if(lastUserClickStatus!=2){
                // clear lastUserClickStatus
                delayCall('$$user_click_action_w38',function(){
                    if(lastUserClickStatus!==2) lastUserClickStatus=0;
                },300)
            }else{
                // extend duration of status 2 for 800ms
                delayCall('$$user_click_action_w38',function(){
                    if(lastUserClickStatus===2) lastUserClickStatus=0;
                },800)
            }

            //disable autostart
            noAutoStart(evt);

            mDate = +new Date + TIMEOUT;
            var jn = function(btn1080) {
                //button is found
                const bool = btn1080.matches(':not([aria-disabled=""]):not([aria-disabled="true"]):not([aria-checked="true"]):not([aria-checked=""])');
                console.log('resolution button found');
                if (bool) {

                    Promise.resolve()
                    .then(()=>new Promise(r=>{
                        const tf = function(){

                            let bool = document.querySelectorAll('button[id^="resolution_"]').length>=2 && document.querySelectorAll('button[id^="resolution_"].vjs-selected').length===1
                            if(!bool)return setTimeout(tf,30);

                            let video = document.querySelector('video[id*="-video-viu-player"][src]')
                            if(!video)return setTimeout(tf,30);
                            const bReady = video.currentTime>0 && !video.ended && video.readyState>video.HAVE_CURRENT_DATA;
                            if(!bReady)return setTimeout(tf,30);



                            console.log('video ready');

                            r();

                        }
                        tf();
                    }))
                    .then(()=>{

                        let btn=document.querySelector('button#resolution_list.bmpui-ui-qualitysettingstogglebutton[aria-pressed]');

                        if(btn){
                        btn.dispatchEvent(new Event('mouseenter'))
                        btn.className.replace(/\b(bmpui-off)\b/,'bmpui-on');
                            btn.setAttribute('aria-pressed','true');
                        }

                        btn1080.dispatchEvent(new Event('mouseenter'))

                    })
                    .then(()=>new Promise((resolve)=>setTimeout(resolve,8)))
                    .then(()=>btn1080.click())
                    .then(()=>new Promise((resolve)=>setTimeout(resolve,20)))
                    .then(()=>{

                        btn1080.dispatchEvent(new Event('mouseleave'))


                        let btn=document.querySelector('button#resolution_list.bmpui-ui-qualitysettingstogglebutton[aria-pressed]');
                        if(btn){
                        btn.dispatchEvent(new Event('mouseleave'))
                        btn.className.replace(/\b(bmpui-on)\b/,'bmpui-off');
                            btn.setAttribute('aria-pressed','false');
                        }


                          })
                    .then(()=>new Promise((resolve)=>setTimeout(resolve,8)))
                        .then(()=>{

                        let pElm=btn1080;
                        let menuUI=null;
                        while(pElm && pElm.parentNode){
                            let checkClsName=pElm.className.replace(/\b(bmpui-ui-settings-panel|customize-video-option-panel)\b/gi,'@@');
                            checkClsName=checkClsName.replace(/\b[a-zA-Z0-9_\-]+\b/gi,'').replace(/\s+/g,' ').trim();
                            if(checkClsName=='@@ @@'){
                                menuUI=pElm;
                                break;
                            }
                            pElm=pElm.parentNode;
                        }

                        if(menuUI){
                            if(!/\b(bmpui-hidden)\b/.test(menuUI.className)) menuUI.className=menuUI.className.trim()+' bmpui-hidden';
                        }


                    })
                    .catch((e)=>0)


                }
            }
            var zn = function() {
                //query when the video is loading/loaded/ready...
                if (cid > 0 && mDate < +new Date) {
                    cid = clearInterval(cid);
                    return;
                }
                var btn1080 = document.querySelector(`button[id^="resolution_${forceQuality}"]`)
                //var btn1080 = document.querySelector(`.vjs-menu-item[data-r="${forceQuality}"]`);
                if (!btn1080) return;
                if (cid > 0) cid = clearInterval(cid);
                if (btn1080.matches('[__userscript_viu_loaded]')) return true;
                btn1080.setAttribute('__userscript_viu_loaded', 'true');
                window.requestAnimationFrame(() => jn(btn1080)); // prevent too fast
            }
            if (cid > 0) cid = clearInterval(cid);
            if (!zn()) cid = setInterval(zn, 33);
        }

        document.addEventListener('loadstart', gn, true)
        document.addEventListener('durationchange', gn, true)
        document.addEventListener('loadedmetadata', gn, true)
        document.addEventListener('loadeddata', gn, true)
        //document.addEventListener('progress', gn, true)
        document.addEventListener('canplay', gn, true)
        //document.addEventListener('canplaythrough', gn, true)

        document.addEventListener('playing',function(evt){

            if(!evt||!evt.target||evt.target.nodeName!="VIDEO")return;
            let video = evt.target;

            requestAnimationFrame(()=>{

                if(!isVideoPlaying(video)) return;
                let unmuteBtn = document.querySelector('button.bmpui-unmute-button.unmute-button.bmpui-muted');
                if(video.muted && unmuteBtn) unmuteBtn.click();

            })


        },true)


    }



    const detection1 = function() {

        console.log('[viu] viu.com reloaded url - detection #1')



    }
    const detection2 = function() {

        console.log('[viu] viu.com reloaded url - detection #2')



    }

    const detection3 = function(info) {

        console.log('[viu] viu.com reloaded url - detection #3', info)


    }

    function handleBeforeUnload(event) {
        //core event handler for detection of false reloading


        console.log('[viu] viu.com reloaded url - detection #4')

        const video = document.querySelector('video#viu-player_html5_api') || document.querySelector('video');

        var saveObj = {};
        saveObj.version = __jver__;
        saveObj.datetime = +new Date();
        saveObj.pageVisible = !(document.hidden === true);
        saveObj.withVideo = (video && video.nodeName == "VIDEO")
        saveObj.userPaused = (settings.userPaused === true);

        localStorage.__zvpp_unload1__ = JSON.stringify(saveObj);


        navStart = (+new Date) - 1;

        // Cancel the event
        //event.preventDefault();
        //return (event.returnValue = ""); // Legacy method for cross browser support


    }

    function handleVisibilityChange() {
        //enable if document.hidden exists
        const video = document.querySelector('video#viu-player_html5_api') || document.querySelector('video'); // just in case
        if (!video) lastPlayingStatus = null;
        if (document.hidden === false) {
            console.log('[viu] page show')
        } else if (document.hidden === true) {
            console.log('[viu] page hide')
            if (video) lastPlayingStatus = !video.paused
        } else {
            lastPlayingStatus = null;
        }
    }


    function isMenuBtn(evt) {
        return evt && evt.target && evt.target.nodeType === 1 && (evt.target.className || "").indexOf('vjs-menu-item') === 0;
    }

    function setQualityAfterClick() {
        delayCall('$$change_video_quality', function() {

            let selectedQuality=-1

            let elm = document.querySelector('button[id^="resolution_"].vjs-selected');
            if(!elm)return;
            let regexp=/\d+/.exec(elm.id);
            if(regexp){
                selectedQuality = regexp[0];
            }
            //let attr = document.querySelector('.vjs-menu-item.vjs-selected[data-r]').getAttribute('data-r');
            if (+selectedQuality > 0) {
                forceQuality = (+selectedQuality).toString();
                console.log(`[viu] video quality set at ${selectedQuality}`)
            }
        }, 300)
    }


    pageInit();
    pageEnableAutoQualityAtStart();

    if (window.onurlchange === null) window.addEventListener('urlchange', detection3, true); // feature is supported

    window.addEventListener("load", loader(detection1), true);

    uWin.addEventListener("load", loader(detection2), true);

    uWin.addEventListener("beforeunload", handleBeforeUnload, true);

    if (typeof document.hidden !== "undefined") document.addEventListener("visibilitychange", handleVisibilityChange, true);


    function handleMouseDown(evt) {
        lastUserClickAt = +new Date;
        lastUserClickStatus =1;
        delayCall('$$user_click_action_w38',function(){
            if(lastUserClickStatus===1) lastUserClickStatus=0;
        },5000)
        if (isMenuBtn(evt)) setQualityAfterClick();
    }
    document.addEventListener("mousedown", handleMouseDown, isPassiveOptionEnable?{
        passive: true,
        capture: true
    }:true)




    function onReady(){

        setTimeout(function(){


            for(const s of document.querySelectorAll('link[rel="stylesheet"][href*=".css"]')){

                if(!s.hasAttribute('type')) s.setAttribute('type','text/css');
                if(!s.hasAttribute('charset')) s.setAttribute('charset','utf-8');

            }


        },40);

    }


    if (document.readyState != 'loading') {
        onReady();
    } else {
        window.addEventListener("DOMContentLoaded", onReady, false);
    }



    // Your code here...
})(unsafeWindow || window);