HTML5 Video Player Enhance

To enhance the functional features of html5 player (h5player) supporting all websites,shortcuts similar to Potplayer - Adjustment of 亮度(brightness),飽和度(saturate),對比度(contrast),速度(playback speed); frame , hue , blur

Versão de: 27/09/2019. Veja: a última versão.

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name         HTML5 Video Player Enhance
// @version      2.6.1(tw)
// @description  To enhance the functional features of html5 player (h5player) supporting all websites,shortcuts similar to Potplayer - Adjustment of 亮度(brightness),飽和度(saturate),對比度(contrast),速度(playback speed); frame , hue , blur
// @author       CY Fung
// @match        http://*/*
// @match        https://*/*
// @run-at       document-start
// @grant        GM_addStyle
// @namespace https://greatest.deepsurf.us/users/371179
// ==/UserScript==
(function () {
    'use strict';
    // Your code here...
    let gbl = {};
    var UA={};
    var tool={};
    Object.assign(gbl, {
        checkFullScreen: function checkFullScreen(doc) {
            if (typeof doc.fullScreen == 'boolean') return doc.fullScreen;
            if (typeof doc.webkitIsFullScreen == 'boolean') return doc.webkitIsFullScreen;
            if (typeof doc.mozFullScreen == 'boolean') return doc.mozFullScreen;
            return null;
        },
        gv001: {
            isFull: false,
            isIframe: false,
            autoCheckCount: 0
        },
        h5player_specific_id_rules :{
            'www.acfun.cn': ['.player-container .player'],
            'www.bilibili.com': ['#bilibiliPlayer'],
            'www.douyu.com': ['#js-player-video-case'],
            'www.huya.com': ['#videoContainer'],
            'www.twitch.tv': ['.player'],
            'www.youtube.com': ['#movie_player'],
            'www.yy.com': ['#player'],
            'www.viu.com': ['#viu-player']
        },
        h5player_general_id_rules : ['.dplayer', '.video-js', '.jwplayer'],
    });
    var gv = gbl.gv001;

    if (window.top !== window.self) {
        gv.isIframe = true;
    }
    if (navigator.language.toLocaleLowerCase() == 'zh-cn') {
        gv.btnText = {
            tip: 'Iframe内视频,请用鼠标点击视频后重试'
        };
    } else {
        gv.btnText = {
            tip: 'Iframe video. Please click on the video and try again'
        };
    }
    var domTool = {
        getRect: function (element) {
            var rect = element.getBoundingClientRect();
            var scroll = domTool.getScroll();
            return {
                pageX: rect.left + scroll.left,
                pageY: rect.top + scroll.top,
                screenX: rect.left,
                screenY: rect.top
            };
        },
        isHalfFullClient: function (element) {
            var client = domTool.getClient();
            var rect = domTool.getRect(element);
            if ((Math.abs(client.width - element.offsetWidth) < 21 && rect.screenX < 20) || (Math.abs(client.height - element.offsetHeight) < 21 && rect.screenY < 10)) {
                if (Math.abs(element.offsetWidth / 2 + rect.screenX - client.width / 2) < 10 && Math.abs(element.offsetHeight / 2 + rect.screenY - client.height / 2) < 10) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        },
        isAllFullClient: function (element) {
            var client = domTool.getClient();
            var rect = domTool.getRect(element);
            if ((Math.abs(client.width - element.offsetWidth) < 21 && rect.screenX < 20) && (Math.abs(client.height - element.offsetHeight) < 21 && rect.screenY < 10)) {
                return true;
            } else {
                return false;
            }
        },
        getScroll: function () {
            return {
                left: document.documentElement.scrollLeft || document.body.scrollLeft,
                top: document.documentElement.scrollTop || document.body.scrollTop
            };
        },
        getClient: function () {
            return {
                width: document.compatMode == 'CSS1Compat' ? document.documentElement.clientWidth : document.body.clientWidth,
                height: document.compatMode == 'CSS1Compat' ? document.documentElement.clientHeight : document.body.clientHeight
            };
        },
        addStyle: function (css) {
            var style = document.createElement('style');
            style.type = 'text/css';
            var node = document.createTextNode(css);
            style.appendChild(node);
            document.head.appendChild(style);
            return style;
        },
        matchRule: function (str, rule) {
            return new RegExp("^" + rule.split("*").join(".*") + "$").test(str);
        },
        createButton: function (id) {
            var btn = document.createElement('tbdiv');
            btn.id = id;
            btn.onclick = function () {
                Maximize.playerControl();
            };
            document.body.appendChild(btn);
            return btn;
        },
        addTip: async function (str) {
            if (!document.getElementById('catTip')) {
                var tip = document.createElement('tbdiv');
                tip.id = 'catTip';
                tip.innerHTML = str;
                tip.style.cssText = 'transition: all 0.8s ease-out;background: none repeat scroll 0 0 #27a9d8;color: #FFFFFF;font: 1.1em "微软雅黑";margin-left: -250px;overflow: hidden;padding: 10px;position: fixed;text-align: center;bottom: 100px;z-index: 300;';
                document.body.appendChild(tip);
                tip.style.right = -tip.offsetWidth - 5 + 'px';
                await new Promise(resolve => {
                    tip.style.display = 'block';
                    setTimeout(() => {
                        tip.style.right = '25px';
                        resolve('OK');
                    }, 300);
                });
                await new Promise(resolve => {
                    setTimeout(() => {
                        tip.style.right = -tip.offsetWidth - 5 + 'px';
                        resolve('OK');
                    }, 3500);
                });
                await new Promise(resolve => {
                    setTimeout(() => {
                        document.body.removeChild(tip);
                        resolve('OK');
                    }, 1000);
                });
            }
        },

        eachParentNode:function (dom, fn) {
            let parent = dom.parentNode
            while (parent) {
                let isEnd = fn(parent, dom)
                parent = parent.parentNode
                if (isEnd) {
                    break
                }
            }
        },

        hideDom:function hideDom(selector, delay) {
            setTimeout(function () {
                const dom = document.querySelector(selector)
                if (dom) {
                    dom.style.opacity = 0
                }
            }, delay || 1000 * 3)
        }
    };
    gbl.setButton = {
        init: function () {
            if (gv.isIframe && domTool.isHalfFullClient(gv.player)) {
                window.parent.postMessage('iframeVideo', '*');
                return;
            }
        }
    };
    var handle = {
        getPlayer: function (e) {
            if (gv.isFull) {
                return;
            }
            gv.mouseoverEl = e.target;
            var hostname = document.location.hostname;
            var players = [];
            for (var i in gbl.h5player_specific_id_rules) {
                if (domTool.matchRule(hostname, i)) {
                    for (let v of gbl.h5player_specific_id_rules[i]) {
                        players = document.querySelectorAll(v);
                        if (players.length > 0) {
                            break;
                        }
                    }
                    break;
                }
            }
            if (players.length == 0) {
                for (let v of gbl.h5player_general_id_rules) {
                    players = document.querySelectorAll(v);
                    if (players.length > 0) {
                        break;
                    }
                }
            }
            if (players.length == 0 && e.target.nodeName != 'VIDEO' && document.querySelectorAll('video').length > 0) {
                var videos = document.querySelectorAll('video');
                for (let v of videos) {
                    var vRect = v.getBoundingClientRect();
                    if (e.clientX >= vRect.x - 2 && e.clientX <= vRect.x + vRect.width + 2 && e.clientY >= vRect.y - 2 && e.clientY <= vRect.y + vRect.height + 2 && v.offsetWidth > 399 && v.offsetHeight > 220) {
                        players = [];
                        players[0] = handle.autoCheck(v);
                        gv.autoCheckCount = 1;
                        break;
                    }
                }
            }
            if (players.length > 0) {
                var path = e.path || e.composedPath();
                for (let v of players) {
                    if (path.indexOf(v) > -1) {
                        gv.player = v;
                        gbl.setButton.init();
                        return;
                    }
                }
            }
            switch (e.target.nodeName) {
                case 'VIDEO':
                case 'OBJECT':
                case 'EMBED':
                    if (e.target.offsetWidth > 399 && e.target.offsetHeight > 220) {
                        gv.player = e.target;
                        gbl.setButton.init();
                    }
                    break;
                default:
                    0;
            }
        },
        autoCheck: function (v) {
            var tempPlayer, el = v;
            gv.playerChilds = [];
            gv.playerChilds.push(v);
            while (el = el.parentNode) {
                if (Math.abs(v.offsetWidth - el.offsetWidth) < 15 && Math.abs(v.offsetHeight - el.offsetHeight) < 15) {
                    tempPlayer = el;
                    gv.playerChilds.push(el);
                } else {
                    break;
                }
            }
            return tempPlayer;
        },
        receiveMessage: async function (e) {
            switch (e.data) {
                case 'iframePicInPic':
                    console.log('messege:iframePicInPic');
                    if (!document.pictureInPictureElement) {
                        await document.querySelector('video').requestPictureInPicture().catch(error => {
                            domTool.addTip(gv.btnText.tip);
                        });
                    } else {
                        await document.exitPictureInPicture();
                    }
                    break;
                case 'iframeVideo':
                    console.log('messege:iframeVideo');
                    if (!gv.isFull) {
                        gv.player = gv.mouseoverEl;
                        gbl.setButton.init();
                    }
                    break;
                case 'parentFull':
                    console.log('messege:parentFull');
                    gv.player = gv.mouseoverEl;
                    if (gv.isIframe) {
                        window.parent.postMessage('parentFull', '*');
                    }
                    Maximize.checkParent();
                    Maximize.fullWin();
                    if (getComputedStyle(gv.player).left != '0px') {
                        domTool.addStyle('#htmlToothbrush #bodyToothbrush .playerToothbrush {left:0px !important;width:100vw !important;}');
                    }
                    gv.isFull = true;
                    break;
                case 'parentSmall':
                    console.log('messege:parentSmall');
                    if (gv.isIframe) {
                        window.parent.postMessage('parentSmall', '*');
                    }
                    Maximize.smallWin();
                    break;
                case 'innerFull':
                    console.log('messege:innerFull');
                    if (gv.player.nodeName == 'IFRAME') {
                        gv.player.contentWindow.postMessage('innerFull', '*');
                    }
                    Maximize.checkParent();
                    Maximize.fullWin();
                    break;
                case 'innerSmall':
                    console.log('messege:innerSmall');
                    if (gv.player.nodeName == 'IFRAME') {
                        gv.player.contentWindow.postMessage('innerSmall', '*');
                    }
                    Maximize.smallWin();
                    break;
            }
        }
    };
    var Maximize = {
        playerControl: function () {
            if (!gv.player) {
                return;
            }
            this.checkParent();
            if (!gv.isFull) {
                if (gv.isIframe) {
                    window.parent.postMessage('parentFull', '*');
                }
                if (gv.player.nodeName == 'IFRAME') {
                    gv.player.contentWindow.postMessage('innerFull', '*');
                }
                this.fullWin();
                if (gv.autoCheckCount > 0 && !domTool.isHalfFullClient(gv.playerChilds[0])) {
                    if (gv.autoCheckCount > 10) {
                        for (var v of gv.playerChilds) {
                            v.classList.add('videoToothbrush');
                        }
                        return;
                    }
                    var tempPlayer = handle.autoCheck(gv.playerChilds[0]);
                    gv.autoCheckCount++;
                    Maximize.playerControl();
                    gv.player = tempPlayer;
                    Maximize.playerControl();
                } else {
                    gv.autoCheckCount = 0;
                }
            } else {
                if (gv.isIframe) {
                    window.parent.postMessage('parentSmall', '*');
                }
                if (gv.player.nodeName == 'IFRAME') {
                    gv.player.contentWindow.postMessage('innerSmall', '*');
                }
                this.smallWin();
            }
        },
        checkParent: function () {
            if (gv.isFull) {
                return;
            }
            gv.playerParents = [];
            var full = gv.player;
            while (full = full.parentNode) {
                if (full.nodeName == 'BODY') {
                    break;
                }
                if (full.getAttribute) {
                    gv.playerParents.push(full);
                }
            }
        },
        fullWin: function () {
            if (!gv.isFull) {
                document.removeEventListener('mouseover', handle.getPlayer, false);
                gv.backHtmlId = document.body.parentNode.id;
                gv.backBodyId = document.body.id;
                if (document.location.hostname == 'www.youtube.com' && document.querySelector('#movie_player .ytp-size-button .ytp-svg-shadow').getBoundingClientRect().width == 20) {
                    document.querySelector('.ytp-size-button').click();
                    gv.ytbStageChange = true;
                }
                this.addClass();
            }
            gv.isFull = true;
        },
        addClass: function () {
            document.body.parentNode.id = 'htmlToothbrush';
            document.body.id = 'bodyToothbrush';
            for (var v of gv.playerParents) {
                v.classList.add('parentToothbrush');
                //父元素position:fixed会造成层级错乱
                if (getComputedStyle(v).position == 'fixed') {
                    v.classList.add('absoluteToothbrush');
                }
            }
            gv.player.classList.add('playerToothbrush');
            if (gv.player.nodeName == 'VIDEO') {
                gv.backControls = gv.player.controls;
                gv.player.controls = true;
            }
            window.dispatchEvent(new Event('resize'));
        },
        smallWin: function () {
            document.body.parentNode.id = gv.backHtmlId;
            document.body.id = gv.backBodyId;
            for (var v of gv.playerParents) {
                v.classList.remove('parentToothbrush');
                v.classList.remove('absoluteToothbrush');
            }
            gv.player.classList.remove('playerToothbrush');
            if (document.location.hostname == 'www.youtube.com' && gv.ytbStageChange) {
                document.querySelector('.ytp-size-button').click();
                gv.ytbStageChange = false;
            }
            if (gv.player.nodeName == 'VIDEO') {
                gv.player.controls = gv.backControls;
            }
            document.addEventListener('mouseover', handle.getPlayer, false);
            window.dispatchEvent(new Event('resize'));
            gv.isFull = false;
        }
    };
    gbl.Maximize = Maximize;
    gbl.pictureInPicture = function () {
        if (document.pictureInPictureElement) document.exitPictureInPicture();
        else {
            if (gv.player && gv.player.nodeName == 'IFRAME') gv.player.contentWindow.postMessage('iframePicInPic', '*');
            else {
                var videoElm = (gv.player && gv.player.nodeName != 'IFRAME') ? gv.player.parentNode.querySelector('video') : document.querySelector('video');
                if ('requestPictureInPicture' in videoElm) videoElm.requestPictureInPicture();
                else gbl.h5Player.tips('PIP is not supported.');
            }
        }
    }

    const TCC = {
        /**
	 * 任務配置中心 Task Control Center
	 * 用於配置所有無法進行通用處理的任務,如不同網站的FULLSCREEN方式不一樣,必須調用網站本身的FULLSCREEN邏輯,才能確保字幕、彈幕等正常工作
	 * */



        'demo.demo': {
            /**
			 * 配置示例
			 * 父級鍵名對應的是一級域名,
			 * 子級鍵名對應的相關功能名稱,鍵值對應的該功能要觸發的點擊選擇器或者要調用的相關函數
			 * 所有子級的鍵值都支持使用選擇器觸發或函數調用
			 * 配置了子級的則使用子級配置邏輯進行操作,否則使用默認邏輯
			 * 注意:include,exclude這兩個子級鍵名除外,這兩個是用來進行url範圍匹配的
			 * */
            'fullScreen': '.fullscreen-btn',
            'exitFullScreen': '.exit-fullscreen-btn',
            'webFullScreen': function () {},
            'exitWebFullScreen': '.exit-fullscreen-btn',
            'autoPlay': '.player-start-btn',
            'pause': '.player-pause',
            'play': '.player-play',
            'switchPlayStatus': '.player-play',
            'playbackRate': function () {},
            'currentTime': function () {},
            'addCurrentTime': '.add-currenttime',
            'subtractCurrentTime': '.subtract-currenttime',
            // 自定義快捷鍵的執行方式,如果是組合鍵,必須是 ctrl-->shift-->alt 這樣的順序,沒有可以忽略,鍵名必須全小寫
            'shortcuts': {
                /* 註冊要執行自定義回調操作的快捷鍵 */
                register: ['ctrl+shift+alt+c', 'ctrl+shift+c', 'ctrl+alt+c', 'ctrl+c', 'c'],
                /* 自定義快捷鍵的回調操作 */
                callback: function (h5Player, taskConf, data) {
                    let {
                        event, player
                    } = data
                    console.log(event, player)
                }
            },
            /* 當前域名下需包含的路徑信息,默認整個域名下所有路徑可用 必須是正則 */
            include: /^.*/,
            /* 當前域名下需排除的路徑信息,默認不排除任何路徑 必須是正則 */
            exclude: /\t/
        },
        'youtube.com': {
            // 'webFullScreen': 'button.ytp-size-button',
            'fullScreen': 'button.ytp-fullscreen-button'
        },
        'netflix.com': {
            'fullScreen': 'button.button-nfplayerFullscreen',
            'addCurrentTime': 'button.button-nfplayerFastForward',
            'subtractCurrentTime': 'button.button-nfplayerBackTen'
        },
        'bilibili.com': {
            'fullScreen': '[data-text="進入FULLSCREEN"]',
            'webFullScreen': '[data-text="網頁FULLSCREEN"]',
            'autoPlay': '.bilibili-player-video-btn-start',
            'switchPlayStatus': '.bilibili-player-video-btn-start'
        },
        'live.bilibili.com': {
            'fullScreen': '.bilibili-live-player-video-controller-fullscreen-btn button',
            'webFullScreen': '.bilibili-live-player-video-controller-web-fullscreen-btn button',
            'switchPlayStatus': '.bilibili-live-player-video-controller-start-btn button'
        },
        'iqiyi.com': {
            'fullScreen': '.iqp-btn-fullscreen',
            'webFullScreen': '.iqp-btn-webscreen',
            'init': function (h5Player, taskConf) {
                // 隱藏水印
                domTool.hideDom('.iqp-logo-box')
                // 移除暫停廣告
                GM_addStyle('div[templatetype="common_pause"]{ display:none }')
            }
        },
        'youku.com': {
            'fullScreen': '.control-fullscreen-icon',
            'init': function (h5Player, taskConf) {
                // 隱藏水印
                domTool.hideDom('.youku-layer-logo')
            }
        },
        'ted.com': {
            'fullScreen': 'button.Fullscreen'
        },
        'v.qq.com': {
            'pause': '.container_inner .txp-shadow-mod]',
            'play': '.container_inner .txp-shadow-mod',
            'shortcuts': {
                register: ['c', 'x', 'z'],
                callback: function (h5Player, taskConf, data) {
                    let {
                        event
                    } = data
                    let key = event.key.toLowerCase()
                    let speedItems = document.querySelectorAll('.container_inner txpdiv[data-role="txp-button-speed-list"] .txp_menuitem')
                    /* 利用sessionStorage下的playbackRate進行設置 */
                    if (window.sessionStorage.playbackRate && /(c|x|z)/.test(key)) {
                        let curSpeed = Number(window.sessionStorage.playbackRate)
                        let perSpeed = curSpeed - 0.1 >= 0 ? curSpeed - 0.1 : 0.1
                        let nextSpeed = curSpeed + 0.1 <= 4 ? curSpeed + 0.1 : 4
                        let targetSpeed = curSpeed
                        switch (key) {
                            case 'z':
                                targetSpeed = 1
                                break
                            case 'c':
                                targetSpeed = nextSpeed
                                break
                            case 'x':
                                targetSpeed = perSpeed
                                break
                        }
                        window.sessionStorage.playbackRate = targetSpeed
                        h5Player.setCurrentTime(0.1, true)
                        h5Player.setPlaybackRate(targetSpeed, true)
                        return true
                    }
                    /* 模擬點擊觸發 */
                    if (speedItems.length >= 3 && /(c|x|z)/.test(key)) {
                        let curIndex = 1
                        speedItems.forEach((item, index) => {
                            if (item.classList.contains('txp_current')) {
                                curIndex = index
                            }
                        })
                        let perIndex = curIndex - 1 >= 0 ? curIndex - 1 : 0
                        let nextIndex = curIndex + 1 < speedItems.length ? curIndex + 1 : speedItems.length - 1
                        let target = speedItems[1]
                        switch (key) {
                            case 'z':
                                target = speedItems[1]
                                break
                            case 'c':
                                target = speedItems[nextIndex]
                                break
                            case 'x':
                                target = speedItems[perIndex]
                                break
                        }
                        target.click()
                        let speedNum = Number(target.innerHTML.replace('x'))
                        h5Player.setPlaybackRate(speedNum)
                    }
                }
            },
            'init': function (h5Player, taskConf) {
                // 隱藏水印
                domTool.hideDom('.txp-watermark')
            }
        },
        'pan.baidu.com': {
            'fullScreen': function (h5Player, taskConf) {
                h5Player.playerInstance.parentNode.querySelector('.vjs-fullscreen-control').click()
            }
        },
        /**
			 * 獲取域名 , 目前實現方式不好,需改造,對地區性域名(如com.cn)、三級及以上域名支持不好
			 * */
        getDomain: function () {
            let host = window.location.host
            let domain = host
            let tmpArr = host.split('.')
            if (tmpArr.length > 2) {
                tmpArr.shift()
                domain = tmpArr.join('.')
            }
            return domain
        },
        /**
			 * 格式化配置任務
			 * @param isAll { boolean } -可選 默認只格式當前域名或host下的配置任務,傳入true則將所有域名下的任務配置都進行格式化
			 */
        formatTCC: function (isAll) {
            let t = this
            let keys = Object.keys(t)
            let domain = t.getDomain()
            let host = window.location.host

            function formatter(item) {
                let defObj = {
                    include: /^.*/,
                    exclude: /\t/
                }
                item.include = item.include || defObj.include
                item.exclude = item.exclude || defObj.exclude
                return item
            }
            let result = {}
            keys.forEach(function (key) {
                let item = t[key]
                if (tool.isObj(item)) {
                    if (isAll) {
                        item = formatter(item)
                        result[key] = item
                    } else {
                        if (key === host || key === domain) {
                            item = formatter(item)
                            result[key] = item
                        }
                    }
                }
            })
            return result
        },
        /* 判斷所提供的配置任務是否適用於當前URL */
        isMatch: function (taskConf) {
            let url = window.location.href
            let isMatch = false
            if (taskConf.include.test(url)) {
                isMatch = true
            }
            if (taskConf.exclude.test(url)) {
                isMatch = false
            }
            return isMatch
        },
        /**
			 * 獲取任務配置,只能獲取到當前域名下的任務配置信息
			 * @param taskName {string} -可選 指定具體任務,默認返回所有類型的任務配置
			 */
        getTaskConfig: function () {
            let t = this
            if (!t._hasFormatTCC_) {
                t.formatTCC()
                t._hasFormatTCC_ = true
            }
            let domain = t.getDomain()
            let taskConf = t[window.location.host] || t[domain]
            if (taskConf && t.isMatch(taskConf)) {
                return taskConf
            }
            return {}
        },
        /**
			 * 執行當前頁面下的相應任務
			 * @param taskName {object|string} -必選,可直接傳入任務配置對象,也可用是任務名稱的字符串信息,自己去查找是否有任務需要執行
			 * @param data {object} -可選,傳給回調函數的數據
			 */
        doTask: function (taskName, data) {
            let t = this
            let isDo = false
            if (!taskName) return isDo
            let taskConf = tool.isObj(taskName) ? taskName : t.getTaskConfig()
            if (!tool.isObj(taskConf) || !taskConf[taskName]) return isDo
            let task = taskConf[taskName]
            console.log('h5player-dotask',taskName)
            let clickDOM = null;
            clickDOM = null;
            if (taskName === 'shortcuts') {
                if (tool.isObj(task) && tool.getType(task.callback) === 'function') {
                    task.callback(h5Player, taskConf, data)
                    isDo = true
                }
            } else if (tool.getType(task) === 'function') {
                task(h5Player, taskConf, data)
                isDo = true
            } else {
                let wrapDom = h5Player.getPlayerWrapDom()
                /* 觸發選擇器上的點擊事件 */
                if (wrapDom && wrapDom.querySelector(task)) {
                    // 在video的父元素裡查找,是為了盡可能相容多實例下的邏輯
                    clickDOM=wrapDom.querySelector(task)
                } else if (document.querySelector(task)) {
                    clickDOM=document.querySelector(task)
                }
                if(clickDOM){
                    setTimeout(function(){
                        //prevent keydown and click same time
                        clickDOM.click()
                    },3);
                    isDo = true
                }
            }
            return isDo
        }
    }


    function ready(selector, fn, shadowRoot) {



        if(!wVideo._readyCount && !shadowRoot){
            document.addEventListener('mouseover', handle.getPlayer, false);
            window.addEventListener('message', handle.receiveMessage, false);
        }

        /**
		 * 元素監聽器
		 * @param selector -必選
		 * @param fn -必選,元素存在時的回調
		 * @param shadowRoot -可選 指定監聽某個shadowRoot下面的DOM元素
		 * 參考:https://javascript.ruanyifeng.com/dom/mutationobserver.html
		 */
        let listeners = []
        let win = window
        let doc = shadowRoot || win.document
        let MutationObserver = win.MutationObserver || win.WebKitMutationObserver
        let observer

        function check() {
            for (let i = 0; i < listeners.length; i++) {
                var listener = listeners[i]
                var elements = doc.querySelectorAll(listener.selector)
                for (let j = 0; j < elements.length; j++) {
                    var element = elements[j]
                    if (!element._isMutationReady_) {
                        element._isMutationReady_ = true
                        listener.fn.call(element, element)
                    }
                }
            }
        }

        // 儲存選擇器和回調函數
        listeners.push({
            selector: selector,
            fn: fn
        })
        if (!observer) {
            // 監聽document變化
            observer = new MutationObserver(check)
            observer.observe(shadowRoot || doc.documentElement, {
                childList: true,
                subtree: true
            })
        }
        // 檢查該節點是否已經在DOM中
        check()
    }

    /**
	 * 某些網頁用了attachShadow closed mode,需要open才能獲取video標籤,例如百度雲盤
	 * 解決參考:
	 * https://developers.google.com/web/fundamentals/web-components/shadowdom?hl=zh-cn#closed
	 * https://stackoverflow.com/questions/54954383/override-element-prototype-attachshadow-using-chrome-extension
	 */


    function hackAttachShadow() {
        if (window._hasHackAttachShadow_) return
        window._hasHackAttachShadow_ = true
        var err = null;

        function errFunc(_err) {
            console.error('hackAttachShadow error by h5player plug-in', {
                errCode: err = _err
            })
            return
        }
        window._shadowDomList_ = []
        if (!window.Element) return errFunc(0x1E01);
        if (!window.Element.prototype) return (0x1E02);
        window.Element.prototype._attachShadow = window.Element.prototype.attachShadow

        window.Element.prototype.attachShadow = function () {

            let arg = arguments
            try {

                if (arg[0] && arg[0]['mode']) {
                    // 強制使用 open mode
                    arg[0]['mode'] = 'open'
                }
            } catch (e) {
                return errFunc(0x3001);
            }
            let shadowRoot = this._attachShadow.apply(this, arg)

            // 存一份shadowDomListf
            if(window._shadowDomList_ && window._shadowDomList_.push) window._shadowDomList_.push(shadowRoot)


            try {
                // 在document下面添加 addShadowRoot 自定義事件
                let shadowEvent = new window.CustomEvent('addShadowRoot', {
                    shadowRoot,
                    detail: {
                        shadowRoot,
                        message: 'addShadowRoot',
                        time: new Date()
                    },
                    bubbles: true,
                    cancelable: true
                })
                document.dispatchEvent(shadowEvent)

            } catch (e) {
                return errFunc(0x3000);
            }
            return shadowRoot
        }

    }


    /* 事件偵聽hack */
    function hackEventListener() {
        if(!window.EventTarget)return
        const EVENT = window.EventTarget.prototype
        if (EVENT._addEventListener) return

        function Listeners(dom,type) { this._dom=dom;this._type=type;this.listenersCount=0;this.hashList={}; };
        Listeners.prototype = new Object;
        Listeners.prototype.__defineGetter__("baseFunc",function(){
            if(this._dom && this._type){
                return this._dom['on'+this._type];
            }
        });
        Listeners.prototype.__defineGetter__("funcCount",function(){
            if(this._dom && this._type){
                return (typeof this.baseFunc=='function')*1+(this.listenersCount||0)
            }
        });

        EVENT._baseLogTime=(+new Date().getTime())*100-1200000000000;

        EVENT._addEventListener = EVENT.addEventListener
        EVENT._removeEventListener = EVENT.removeEventListener
        // hack addEventListener
        EVENT._evtCount=0;
        EVENT.addEventListener = function () {
            let arg = arguments
            let type = arg[0]
            let listener = arg[1]
            this._addEventListener.apply(this, arg)
            this._listeners = this._listeners || {}
            this._listeners[type] = this._listeners[type] || new Listeners(this,type)
            var addedTime=+new Date().getTime();
            var uid = 100000+(++EVENT._evtCount);
            let listenerObj = {
                //target: this,
                //type,
                listener,
                options: arg[2],
                uid: uid,
                //addedTime:addedTime
            }
            this._listeners[type].hashList[uid+'']=listenerObj;
            this._listeners[type].listenersCount++;
        }
        // hack removeEventListener
        EVENT.removeEventListener = function () {
            let arg = arguments
            let type = arg[0]
            let listener = arg[1]
            this._removeEventListener.apply(this, arg)
            if(this._listeners&&this._listeners[type]&&this._listeners[type].hashList){
                var hashList=this._listeners[type].hashList
                for(var k in hashList){
                    if(hashList[k].listener===listener){
                        delete hashList[k];
                        this._listeners[type].listenersCount--;
                        break;
                    }
                }
            }
        }
    }



    Object.assign(tool,{
        quickSort  : function (arr) {

            /**
		 * 向上查找操作
		 * @param dom {Element} -必選 初始dom元素
		 * @param fn {function} -必選 每一級ParentNode的回調操作
		 * 如果函數返回true則表示停止向上查找動作
		 */
            function _quickSort(arr){

                if (arr.length <= 1) {
                    return arr
                }
                var pivotIndex = Math.floor(arr.length / 2)
                var pivot = arr.splice(pivotIndex, 1)[0]
                var left = []
                var right = []
                for (var i = 0; i < arr.length; i++) {
                    if (arr[i] < pivot) {
                        left.push(arr[i])
                    } else {
                        right.push(arr[i])
                    }
                }
                return _quickSort(left).concat([pivot], _quickSort(right))
            }

            return _quickSort(arr)
        },


        getType:function (obj) {

            /**
	 * 準確地獲取對象的具體類型
	 * @param obj { all } -必選 要判斷的對象
	 * @returns {*} 返回判斷的具體類型
	 */
            if (obj == null) {
                return String(obj)
            }
            return typeof obj === 'object' || typeof obj === 'function' ? (obj.constructor && obj.constructor.name && obj.constructor.name.toLowerCase()) || /function\s(.+?)\(/.exec(obj.constructor)[1].toLowerCase() : typeof obj
        },

        isObj:function isObj(obj) {
            return tool.getType(obj) === 'object'
        }
    });


    Object.assign(UA,{
        fakeUA:function fakeUA(ua) {
            Object.defineProperty(navigator, 'userAgent', {
                value: ua,
                writable: false,
                configurable: false,
                enumerable: true
            })
        },
        userAgentMap: {
            android: {
                chrome: 'Mozilla/5.0 (Linux; Android 9; SM-G960F Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.157 Mobile Safari/537.36',
                firefox: 'Mozilla/5.0 (Android 7.0; Mobile; rv:57.0) Gecko/57.0 Firefox/57.0'
            },
            iPhone: {
                safari: 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Mobile/15E148 Safari/604.1',
                chrome: 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/74.0.3729.121 Mobile/15E148 Safari/605.1'
            },
            iPad: {
                safari: 'Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Mobile/15E148 Safari/604.1',
                chrome: 'Mozilla/5.0 (iPad; CPU OS 12_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/74.0.3729.155 Mobile/15E148 Safari/605.1'
            }
        }

    });

    Object.assign(UA,{
        fakeConfig : {
            // 'tv.cctv.com': userAgentMap.iPhone.chrome,
            // 'v.qq.com': userAgentMap.iPad.chrome,
            'open.163.com': UA.userAgentMap.iPhone.chrome,
            'm.open.163.com': UA.userAgentMap.iPhone.chrome
        }
    });

    function debugMsg() {
        let arg = Array.from(arguments)
        arg.unshift('h5player debug message :')
        console.info.apply(console, arg)
    }
    let h5Player = {
        /* 提示文本的字號 */
        fontSize: 16,
        enable: true,
        globalMode: true,
        playerInstance: null,
        scale: 1,
        translate: {
            x: 0,
            y: 0
        },
        playbackRate: 1,
        /* 快進快退步長 */
        skipStep: 5,
        /* 獲取當前播放器的實例 */
        player: function () {
            let t = this
            return t.playerInstance || t.getPlayerList()[0]
        },
        /* 每個網頁可能存在的多個video播放器 */
        getPlayerList: function () {
            let list = []

            function findPlayer(context) {
                context.querySelectorAll('video').forEach(function (player) {
                    list.push(player)
                })
            }
            findPlayer(document)
            // 被封裝在 shadow dom 裡面的video
            if (window._shadowDomList_) {
                window._shadowDomList_.forEach(function (shadowRoot) {
                    findPlayer(shadowRoot)
                })
            }
            return list
        },
        getPlayerWrapDom: function () {
            let t = this
            let player = t.player()
            if (!player) return
            let wrapDom = null
            let playerBox = player.getBoundingClientRect()
            domTool.eachParentNode(player, function (parent) {
                if (parent === document || !parent.getBoundingClientRect) return
                let parentBox = parent.getBoundingClientRect()
                if (parentBox.width && parentBox.height) {
                    if (parentBox.width === playerBox.width && parentBox.height === playerBox.height) {
                        wrapDom = parent
                    }
                }
            })
            return wrapDom
        },
        fireGlobalInit:function(){

            let t = this
            /* 綁定鍵盤事件 */
            t.bindEvent()
            let host = window.location.host
            if (UA.fakeConfig[host]) {
                t.setFakeUA(UA.fakeConfig[host])
            }

            let lostFocus=null;

            //fireGlobalInit
            var doc=window.top.document
            doc.addEventListener('focusout', function (e) {
                if (!this.hasFocus()&&t.player()) {
                    lostFocus=true;
                    setTimeout(function () {
                        h5Player.tips('focus is lost', -1);
                    }, 1);
                }
            }, true)
            doc.addEventListener('focusin', function (e) {
                if (this.hasFocus()&&t.player()) {
                    lostFocus=false;
                    setTimeout(function () {
                        h5Player.tips(false);
                    }, 1);
                }
            }, true)


        },
        initPlayerInstance: function (isSingle) {

            /**
		 * 初始化播放器實例
		 * @param isSingle 是否為單實例video標籤
		 */
            let t = this
            if (!t.playerInstance) return
            let player = t.playerInstance

            if(!t.init_count) {
                t.init_count=(t.init_count||0)+1;
                if(t.init_count===1) t.fireGlobalInit();
            }

            if (!player.hasAttribute('tabindex')) player.setAttribute('tabindex', '-1');
            if (!player.hasAttribute('playsinline')) player.setAttribute('playsinline', 'playsinline');
            if (!player.hasAttribute('x-webkit-airplay')) player.setAttribute('x-webkit-airplay', 'deny');
            if (!player.hasAttribute('preload')) player.setAttribute('preload', 'auto');
            player.style['image-rendering'] = '-webkit-optimize-contrast';
            player.style['image-rendering'] = 'crisp-edges';
            t.filter.reset()
            t.initTips()
            t.initPlaybackRate()
            t.isFoucs()
            /* 增加通用FULLSCREEN,網頁FULLSCREEN-api */
            if (!player._hasCanplayEvent_) {
                player.addEventListener('canplay', function (event) {
                    t.initAutoPlay(player)
                })
                player._hasCanplayEvent_ = true
            }
            /* 播放的時候進行相關同步操作 */
            if (!player._hasPlayingInitEvent_) {
                let setPlaybackRateOnPlayingCount = 0
                player.addEventListener('playing', function (event) {
                    if (setPlaybackRateOnPlayingCount === 0) {
                        /* 同步之前設定的播放速度 */
                        t.setPlaybackRate()
                        if (isSingle === true) {
                            /* 恢復播放進度和進行進度記錄 */
                            t.setPlayProgress(player)
                            setTimeout(function () {
                                t.playProgressRecorder(player)
                            }, 1000 * 3)
                        }
                    } else {
                        t.setPlaybackRate(null, true)
                    }
                    setPlaybackRateOnPlayingCount += 1
                })
                player._hasPlayingInitEvent_ = true
            }
            /* 進行自定義初始化操作 */
            let taskConf = TCC.getTaskConfig()
            if (taskConf.init) {
                TCC.doTask('init', player)
            }
        },
        initPlaybackRate: function () {
            let t = this
            t.playbackRate = t.getPlaybackRate()
        },
        getPlaybackRate: function () {
            let t = this
            let playbackRate = window.localStorage.getItem('_h5_player_playback_rate_') || t.playbackRate
            return Number(Number(playbackRate).toFixed(1))
        },
        change_playerBox: function (tips) {
            let t = this;
            let player = t.player()
            var playerBox = player.parentNode
            while (playerBox && playerBox.offsetHeight == 0) playerBox = playerBox.parentNode;
            while (playerBox && playerBox.offsetHeight < player.offsetHeight) playerBox = playerBox.parentNode;
            playerBox = playerBox || player.parentNode
            if (playerBox) {
                if (!tips.parentNode || tips.parentNode !== playerBox) {
                    playerBox.insertBefore(tips, playerBox.firstChild);
                }
            }
        },
        callFullScreenBtn7: function () {
            let t = this;
            let player = t.player()
            if (gv.player && gv.player.contains(player)) {} else {
                gv.player = player;
                gbl.setButton.init();
            }
            gbl.Maximize.playerControl();
        },
        callFullScreenBtn8: function () {
            let t = this;
            let player = t.player()
            var doubleClickEvent = document.createEvent('MouseEvents');
            doubleClickEvent.initEvent('dblclick', true, true);
            player.dispatchEvent(doubleClickEvent); // inside method
        },
        callFullScreenBtn0: function () {
            return this.callFullScreenBtn7();
        },
        callFullScreenBtn5: function (playerBox) {
            return this.callFullScreenBtn7();
            let t = this;
            let player = t.player()

            function get_gPlayer() {
                // parent of same size as video player
                var q1 = JSON.stringify(player.getClientRects()[0])
                var pPlayer = player
                var qPlayer = playerBox
                for (var q3 = 0; q3 < 4; q3++) {
                    if (!qPlayer) break;
                    var q2 = JSON.stringify(qPlayer.getClientRects()[0])
                    if (q1 == q2) {
                        pPlayer = qPlayer
                        qPlayer = qPlayer.parentNode
                    } else {
                        break;
                    }
                }
                var gPlayer = pPlayer
                return gPlayer;
            }
            var gPlayer = get_gPlayer();
            console.log('gPlayer', gPlayer)
            console.log('DOM fullscreen')
            gPlayer.requestFullscreen()
        },
        callFullScreenBtn: function () {
            //return this.callFullScreenBtn7();
            //return this.callFullScreenBtn8();
            let t = this;
            let player = t.player()
            if (!player || !player.ownerDocument) return this.callFullScreenBtn0();
            let tcn = player.getAttribute('_h5player_tips') || (t.tipsClassName);
            console.log('tcn', tcn)
            let playerBox = player.ownerDocument.querySelector('.' + tcn)
            if (!playerBox) return this.callFullScreenBtn0();
            let chFull = gbl.checkFullScreen(player.ownerDocument);
            if (chFull === null) return (console.log('chFull', 'null'), this.callFullScreenBtn0());
            if (chFull === true) {
                console.log('chFull', 'true')
                player.ownerDocument.exitFullscreen();
                //  var el = [t.player_focus_input]; if (el) {el = el[0].click()}
            } else {
                console.log('chFull', 'false')
                var pPlayer = player
                var qPlayer = playerBox
                var clicked = false;
                var _gs1_tmp={};
                var _gs1_filter=function (elq) {
                    return _gs1_tmp.elm == elq.elm ? false : _gs1_tmp.elm.contains(elq.elm)
                };
                var _gs1_contains=function (elp) {
                    if (elp.childElementCount === 0) return false;
                    _gs1_tmp.elm = elp.elm;
                    elp.contains = gs1.filter(_gs1_filter).length > 0;
                }
                // try to find the fullscreen button
                for (var q3 = 0; q3 < 4; q3++) { //max 4 layers
                    if (!qPlayer) break;
                    var fs1 = qPlayer.querySelectorAll('[class*="fullscreen"]')
                    if (fs1.length > 0) {
                        // -- indiv-elm --
                        var gs1 = Array.prototype.map.call(fs1, function (elm) {
                            return {
                                elm: elm,
                                visible: null,
                                click: null,
                                childElementCount: null,
                                contains: null
                            }
                        });
                        if (('_listeners' in document)) {
                            gs1.forEach(function (elp) {
                                var elm = elp.elm;
                                elp.click = elm._listeners && elm._listeners.click && elm._listeners.click.funcCount > 0
                            })
                        }
                        if ('childElementCount' in player) {
                            gs1.forEach(function (elp) {
                                var elm = elp.elm;
                                elp.childElementCount = elm.childElementCount;
                            })
                        }
                        if ('getBoundingClientRect' in player) {
                            gs1.forEach(function (elp) {
                                var elm = elp.elm;
                                var rect = elm.getBoundingClientRect();
                                elp.visible = rect.height * rect.width > 0
                            })
                        }
                        gs1 = gs1.filter(function (elp) {
                            return elp.click
                        })
                        //console.log('gs1',gs1)
                        // -- inter-elm --
                        if ('contains' in player) {
                            gs1.forEach(_gs1_contains)
                        }
                        var gs2 = gs1.filter(function (elp) {
                            return !elp.contains && elp.visible
                        })
                        console.log('fullscreen btn',gs2)
                        //console.log('gs2',gs2)
                        if (gs2.length >= 1) {
                            var gs2_a = gs2.map(elp => elp.elm.className.length)
                            var gs2_b = Math.min.apply(Math, gs2_a)
                            var gs2_c = gs2_a.lastIndexOf(gs2_b)
                            // pick the last btn if there is more than one
                            gs2[gs2_c].elm.click();
                            clicked = true;
                            console.log('original fullscreen')
                            break;
                        }
                    }
                    pPlayer = qPlayer
                    qPlayer = qPlayer.parentNode
                }
                if (!clicked) {
                    //cannot find -> default
                    this.callFullScreenBtn5(playerBox);
                }
            }
        },
        /* 設置播放速度 */
        setPlaybackRate: function (num, notips) {
            let taskConf = TCC.getTaskConfig()
            if (taskConf.playbackRate) {
                TCC.doTask('playbackRate')
                return
            }
            let t = this
            let player = t.player()
            let curPlaybackRate
            if (num) {
                num = Number(num)
                if (Number.isNaN(num)) {
                    console.error('h5player: 播放速度轉換出錯')
                    return false
                }
                if (num <= 0) {
                    num = 0.1
                }
                num = Number(num.toFixed(1))
                curPlaybackRate = num
            } else {
                curPlaybackRate = t.getPlaybackRate()
            }
            /* 記錄播放速度的信息 */
            window.localStorage.setItem('_h5_player_playback_rate_', curPlaybackRate)
            t.playbackRate = curPlaybackRate
            player.playbackRate = curPlaybackRate
            /* 本身處於1被播放速度的時候不再提示 */
            if (!num && curPlaybackRate === 1) return;
            !notips && t.tips('Playback speed: ' + player.playbackRate + 'x')
        },
        /**
		 * 初始化自動播放邏輯
		 * 必須是配置了自動播放按鈕選擇器得的才會進行自動播放
		 */
        initAutoPlay: function (p) {
            let t = this
            let player = p || t.player()
            // 在輪詢重試的時候,如果實例變了,或處於隱藏頁面中則不進行自動播放操作
            if (!player || (p && p !== t.player()) || document.hidden) return
            let taskConf = TCC.getTaskConfig()
            if (player && taskConf.autoPlay && player.paused) {
                TCC.doTask('autoPlay')
                if (player.paused) {
                    // 輪詢重試
                    if (!player._initAutoPlayCount_) {
                        player._initAutoPlayCount_ = 1
                    }
                    player._initAutoPlayCount_ += 1
                    if (player._initAutoPlayCount_ >= 10) {
                        return false
                    }
                    setTimeout(function () {
                        t.initAutoPlay(player)
                    }, 200)
                }
            }
        },
        setWebFullScreen: function () {
            return this.callFullScreenBtn();
        },
        setCurrentTime: function (num, notips) {
            if (!num) return
            num = Number(num);
            let _num = Math.abs(Number(num.toFixed(1)));
            let t = this;
            let player = t.player();
            let taskConf = TCC.getTaskConfig();
            if (taskConf.currentTime) {
                TCC.doTask('currentTime');
                return
            }
            if (num > 0) {
                if (taskConf.addCurrentTime) {
                    TCC.doTask('addCurrentTime')
                } else {
                    player.currentTime += _num;
                    !notips && t.tips(_num + ' Sec. Forward')
                }
            } else {
                if (taskConf.subtractCurrentTime) {
                    TCC.doTask('subtractCurrentTime')
                } else {
                    player.currentTime -= _num;
                    !notips && t.tips(_num + ' Sec. Backward')
                }
            }
        },
        setVolume: function (num) {
            if (!num) return
            num = Number(num)
            let _num = Math.abs(Number(num.toFixed(2)))
            let t = this
            let player = t.player()
            if (num > 0) {
                if (player.volume < 1) {
                    player.volume += _num
                }
            } else {
                if (player.volume > 0) {
                    player.volume -= _num
                }
            }
            t.tips('Volume: ' + parseInt(player.volume * 100) + '%')
        },
        setFakeUA(ua) {
            ua = ua || UA.userAgentMap.iPhone.safari
            /* 記錄設定的ua信息 */
            window.localStorage.setItem('_h5_player_user_agent_', ua)
            UA.fakeUA(ua)
        },
        /* ua偽裝切換開關 */
        switchFakeUA(ua) {
            let customUA = window.localStorage.getItem('_h5_player_user_agent_')
            if (customUA) {
                window.localStorage.removeItem('_h5_player_user_agent_')
            } else {
                this.setFakeUA(ua)
            }
            debugMsg('ua', navigator.userAgent)
        },
        switchPlayStatus: function () {
            let t = this
            let player = t.player()
            let taskConf = TCC.getTaskConfig()
            if (taskConf.switchPlayStatus) {
                TCC.doTask('switchPlayStatus')
                return
            }
            if (player.paused) {
                if (taskConf.play) {
                    TCC.doTask('play')
                } else {
                    player.play()
                    t.tips('Playback resumed')
                }
            } else {
                if (taskConf.pause) {
                    TCC.doTask('pause')
                } else {
                    player.pause()
                    t.tips('Playback paused')
                }
            }
        },
        tipsClassName: 'html_player_enhance_tips',
        tips: function (str, duration) {
            let t = h5Player
            let player = t.player()
            if (!player) {
                console.log('h5Player Tips:', str)
                return true
            }
            let parentNode = player.parentNode
            let tipsSelector = '.' + (player.getAttribute('_h5player_tips') || t.tipsClassName)
            let tipsDom = player.ownerDocument.querySelector(tipsSelector) || (t.initTips(), player.ownerDocument.querySelector(tipsSelector))
            if (!tipsDom) {
                console.log('init h5player tips dom error...')
                return false
            }
            t.change_playerBox(tipsDom);
            let style = tipsDom.style
            if (str === false) {
                tipsDom.innerText = '';
                clearTimeout(this.on_off[0])
                clearTimeout(this.on_off[1])
                clearTimeout(this.on_off[2])
                style.display = 'none'
                style.opacity = 0
            } else {
                if (duration === undefined) duration = 2000
                tipsDom.innerText = str
                for (var i = 0; i < 3; i++) {
                    if (this.on_off[i]) {
                        clearTimeout(this.on_off[i])
                        this.on_off[i] = 0;
                    }
                }
                t.on_off[0] = setTimeout(function () {
                    style.display = 'block'
                    style.opacity = 0.9
                }, 15)
                t.on_off[1] = setTimeout(function () {
                    style.display = 0.7
                }, 50)
                if (duration > 0) {
                    t.on_off[2] = setTimeout(function () {
                        style.display = 'none'
                        style.opacity = 0
                    }, duration)
                }
            }
        },
        /* 設置提示DOM的樣式 */
        initTips: function () {
            let t = this
            let player = t.player()
            let parentNode = player.parentNode
            var tcn = player.getAttribute('_h5player_tips') || (t.tipsClassName + '_' + (+new Date));
            player.setAttribute('_h5player_tips', tcn)
            if (player.ownerDocument.querySelector('.' + tcn)) return
            let tipsStyle = `
position: absolute;
z-index: 999;
font-size: ${t.fontSize || 16}px;
padding: 10px;
background: rgba(0,0,0,0.4);
color:white;
top: 50%;
left: 50%;max-width:500px;max-height:50px;
transform: translate(-50%,-50%);
transition: all 500ms ease;
opacity: 0;
border-radius:3px;
display: none;
-webkit-font-smoothing: subpixel-antialiased;
-moz-font-smoothing: subpixel-antialiased;
-ms-font-smoothing: subpixel-antialiased;
font-smoothing: subpixel-antialiased;
font-family: 'microsoft yahei', Verdana, Geneva, sans-serif;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-drag: none;
-khtml-user-select: none;
-moz-user-select: none;
-moz-user-select: -moz-none;
-ms-user-select: none;
pointer-events: none;
user-select: none;
`.replace(/\r\n/g, '')
            let tips = document.createElement('div')
            tips.setAttribute('style', tipsStyle)
            tips.setAttribute('class', tcn)
            tips.setAttribute('unselectable', 'on')
            t.change_playerBox(tips);
        },
        on_off: new Array(3),
        rotate: 0,
        fps: 30,
        /* 濾鏡效果 */
        filter: {
            key: {},
            view_units: {
                'hue-rotate': 'deg',
                'blur': 'px'
            },
            setup: function () {
                var view = ''
                for (var view_key in this.key) {
                    var view_unit = this.view_units[view_key] || ''
                    view += view_key + '(' + (+this.key[view_key] || 0).toFixed(3) + view_unit + ') '
                    this.key[view_key] = Number(+this.key[view_key] || 0)
                }
                view += 'url("#unsharpen1")'
                h5Player.player().style.WebkitFilter = view
            },
            reset: function () {
                this.key['brightness'] = 1
                this.key['contrast'] = 1
                this.key['saturate'] = 1
                this.key['hue-rotate'] = 0
                this.key['blur'] = 0
                this.setup()
            }
        },
        _isFoucs: false,
        /* 播放器的聚焦事件 */
        isFoucs: function () {
            let t = h5Player
            let player = t.player()
            if (!player) return
            player.addEventListener('mouseenter', function (e) {
                h5Player._isFoucs = true
            })
            player.addEventListener('mouseleave', function (e) {
                h5Player._isFoucs = false
            })
        },
        keyCodeList: [13, 16, 17, 18, 27, 32, 37, 38, 39, 40, 49, 50, 51, 52, 67, 68, 69, 70, 73, 74, 75, 78, 79, 80, 81, 82, 83, 84, 85, 87, 88, 89, 90, 97, 98, 99, 100, 220],
        keyList: ['enter', 'shift', 'control', 'alt', 'escape', ' ', 'arrowleft', 'arrowright', 'arrowright', 'arrowup', 'arrowdown', '1', '2', '3', '4', 'c', 'd', 'e', 'f', 'i', 'j', 'k', 'o', 'p', 'q', 'r', 's', 't', 'u', 'w', 'x', 'y', 'z', '\\', '|'],
        keyMap: {
            'enter': 13,
            'shift': 16,
            'ctrl': 17,
            'alt': 18,
            'esc': 27,
            'space': 32,
            '←': 37,
            '↑': 38,
            '→': 39,
            '↓': 40,
            '1': 49,
            '2': 50,
            '3': 51,
            '4': 52,
            'c': 67,
            'd': 68,
            'e': 69,
            'f': 70,
            'i': 73,
            'j': 74,
            'k': 75,
            'n': 78,
            'o': 79,
            'p': 80,
            'q': 81,
            'r': 82,
            's': 83,
            't': 84,
            'u': 85,
            'w': 87,
            'x': 88,
            'y': 89,
            'z': 90,
            'pad1': 97,
            'pad2': 98,
            'pad3': 99,
            'pad4': 100,
            '\\': 220,
        },
        trigger_allowKeys: ['x', 'c', 'z', 'arrowright', 'arrowleft', 'arrowup', 'arrowdown'],
        /* 播放器事件響應器 */
        playerTrigger: function (player, event) {
            if (!player || !event) return
            let t = h5Player
            let keyCode = event.keyCode
            if (event.code == "Space" && keyCode > 128) keyCode = 32;

            //shift + key
            if (event.shiftKey && !event.ctrlKey && !event.altKey) {
                let key = event.key.toLowerCase()
                // 網頁FULLSCREEN
                if (key === 'enter') {
                    t.callFullScreenBtn()
                }
                // 進入或退出畫中畫模式
                else if (key === 'p') {
                    if (window._isPictureInPicture_) {
                        document.exitPictureInPicture().then(() => {
                            window._isPictureInPicture_ = null
                        }).catch(() => {
                            window._isPictureInPicture_ = null
                        })
                    } else {
                        player.requestPictureInPicture && player.requestPictureInPicture().then(() => {
                            window._isPictureInPicture_ = true
                        }).catch(() => {
                            window._isPictureInPicture_ = null
                        })
                    }
                }
                // 視頻畫面縮放相關事件
                else if (t.trigger_allowKeys.includes(key)) {
                    t.scale = Number(t.scale)
                    switch (key) {
                            // shift+X:視頻縮小 -0.1
                        case 'x':
                            t.scale -= 0.1
                            break
                            // shift+C:視頻放大 +0.1
                        case 'c':
                            t.scale += 0.1
                            break
                            // shift+Z:視頻恢復正常大小
                        case 'z':
                            t.scale = 1
                            t.translate = {
                                x: 0,
                                y: 0
                            }
                            break
                        case 'arrowright':
                            t.translate.x += 10
                            break
                        case 'arrowleft':
                            t.translate.x -= 10
                            break
                        case 'arrowup':
                            t.translate.y -= 10
                            break
                        case 'arrowdown':
                            t.translate.y += 10
                            break
                    }
                    let scale = t.scale = Number(t.scale).toFixed(1)
                    player.style.transform = `scale(${scale}) translate(${t.translate.x}px, ${t.translate.y}px)`
                    let tipsMsg = `視頻縮放率:${scale * 100}%`
                    if (t.translate.x) {
                        tipsMsg += `,水平位移:${t.translate.x}px`
                    }
                    if (t.translate.y) {
                        tipsMsg += `,垂直位移:${t.translate.y}px`
                    }
                    t.tips(tipsMsg)
                    // 阻止事件冒泡
                    event.stopPropagation()
                    event.preventDefault()
                    return true
                }
            }
            // 防止其它無關組合鍵衝突
            if (!event.altKey && !event.ctrlKey && !event.shiftKey) {
                var kControl = null
                console.log('keycode', keyCode)
                switch (keyCode) {
                        // 方向鍵右→:快進3秒
                    case 39:
                        t.setCurrentTime(t.skipStep)
                        break;
                        // 方向鍵左←:後退3秒
                    case 37:
                        t.setCurrentTime(-t.skipStep)
                        break;
                        // 方向鍵上↑:音量升高 1%
                    case 38:
                        t.setVolume(0.01)
                        break;
                        // 方向鍵下↓:音量降低 1%
                    case 40:
                        t.setVolume(-0.01)
                        break;
                        // 空格鍵:暫停/播放
                    case h5Player.keyMap.space:
                        t.switchPlayStatus()
                        break;
                        // 按鍵X:減速播放 -0.1
                    case h5Player.keyMap.x:
                        if (player.playbackRate > 0) {
                            t.setPlaybackRate(player.playbackRate - 0.1)
                        }
                        break;
                        // 按鍵C:加速播放 +0.1
                    case h5Player.keyMap.c:
                        if (player.playbackRate < 16) {
                            t.setPlaybackRate(player.playbackRate + 0.1)
                        }
                        break;
                        // 按鍵Z:正常速度播放
                    case h5Player.keyMap.z:
                        player.playbackRate = 1
                        t.setPlaybackRate(player.playbackRate)
                        break;
                        // 按鍵F:下一幀
                    case h5Player.keyMap.f:
                        if (window.location.hostname === 'www.netflix.com') return /* netflix 的F鍵是FULLSCREEN的意思 */
                        if (!player.paused) player.pause()
                        player.currentTime += Number(1 / t.fps)
                        t.tips('Jump to: Next frame')
                        break;
                        // 按鍵D:上一幀
                    case h5Player.keyMap.d:
                        if (!player.paused) player.pause()
                        player.currentTime -= Number(1 / t.fps)
                        t.tips('Jump to: Previous frame')
                        break;
                        // 按鍵E:亮度增加%
                    case h5Player.keyMap.e:
                        kControl = 'brightness'
                        t.filter.key[kControl] += 0.1
                        t.filter.key[kControl] = t.filter.key[kControl].toFixed(2)
                        t.filter.setup()
                        t.tips('Brightness: ' + parseInt(t.filter.key[kControl] * 100) + '%')
                        break;
                        // 按鍵W:亮度減少%
                    case h5Player.keyMap.w:
                        kControl = 'brightness'
                        if (t.filter.key[kControl] > 0) {
                            t.filter.key[kControl] -= 0.1
                            t.filter.key[kControl] = t.filter.key[kControl].toFixed(2)
                            t.filter.setup()
                        }
                        t.tips('Brightness: ' + parseInt(t.filter.key[kControl] * 100) + '%')
                        break;
                        // 按鍵T:對比度增加%
                    case h5Player.keyMap.t:
                        kControl = 'contrast'
                        t.filter.key[kControl] += 0.1
                        t.filter.key[kControl] = t.filter.key[kControl].toFixed(2)
                        t.filter.setup()
                        t.tips('Contrast: ' + parseInt(t.filter.key[kControl] * 100) + '%')
                        break;
                        // 按鍵R:對比度減少%
                    case h5Player.keyMap.r:
                        kControl = 'contrast'
                        if (t.filter.key[kControl] > 0) {
                            t.filter.key[kControl] -= 0.1
                            t.filter.key[kControl] = t.filter.key[1].toFixed(2)
                            t.filter.setup()
                        }
                        t.tips('Contrast: ' + parseInt(t.filter.key[kControl] * 100) + '%')
                        break;
                        // 按鍵U:飽和度增加%
                    case h5Player.keyMap.u:
                        kControl = 'saturate'
                        t.filter.key[kControl] += 0.1
                        t.filter.key[kControl] = t.filter.key[kControl].toFixed(2)
                        t.filter.setup()
                        t.tips('Saturate: ' + parseInt(t.filter.key[kControl] * 100) + '%')
                        break;
                        // 按鍵Y:飽和度減少%
                    case h5Player.keyMap.y:
                        kControl = 'saturate'
                        if (t.filter.key[kControl] > 0) {
                            t.filter.key[kControl] -= 0.1
                            t.filter.key[kControl] = t.filter.key[kControl].toFixed(2)
                            t.filter.setup()
                        }
                        t.tips('Saturate: ' + parseInt(t.filter.key[kControl] * 100) + '%')
                        break;
                        // 按鍵O:色相增加 1 度
                    case h5Player.keyMap.o:
                        kControl = 'hue-rotate'
                        t.filter.key['hue-rotate'] += 1
                        t.filter.setup()
                        t.tips('Hue: ' + t.filter.key[kControl] + ' deg')
                        break;
                        // 按鍵I:色相減少 1 度
                    case h5Player.keyMap.i:
                        kControl = 'hue-rotate'
                        t.filter.key['hue-rotate'] -= 1
                        t.filter.setup()
                        t.tips('Hue: ' + t.filter.key[kControl] + ' deg')
                        break;
                        // 按鍵K:模糊增加 0.1 px
                    case h5Player.keyMap.k:
                        kControl = 'blur'
                        t.filter.key[kControl] += 0.1
                        t.filter.key[kControl] = (+t.filter.key[kControl] || 0).toFixed(1)
                        t.filter.setup()
                        t.tips('Blur: ' + t.filter.key[kControl] + ' px')
                        break;
                        // 按鍵J:模糊減少 0.1 px
                    case h5Player.keyMap.j:
                        kControl = 'blur'
                        if (t.filter.key[kControl] > 0) {
                            t.filter.key[kControl] -= 0.1
                            t.filter.key[kControl] = (+t.filter.key[kControl] || 0).toFixed(1)
                            t.filter.setup()
                        }
                        t.tips('Blur: ' + t.filter.key[kControl] + ' px')
                        break;
                        // 按鍵Q:圖像復位
                    case h5Player.keyMap.q:
                        t.filter.reset()
                        t.tips('Video Filter Reset')
                        break;
                        // 按鍵S:畫面旋轉 90 度
                    case h5Player.keyMap.s:
                        t.rotate += 90
                        if (t.rotate % 360 === 0) t.rotate = 0;
                        player.style.transform = 'rotate(' + t.rotate + 'deg)'
                        t.tips('Rotation:' + t.rotate + ' deg')
                        break;
                        // 按鍵迴車,進入FULLSCREEN
                    case h5Player.keyMap.enter:
                        t.callFullScreenBtn();
                        break;
                    case h5Player.keyMap.n:
                        if (gv.player && gv.player.contains(player)) {} else {
                            gv.player = player;
                            gbl.setButton.init();
                        }
                        gbl.pictureInPicture();
                        break;
                    default:
                        // 按1-4設置播放速度 49-52;97-100
                        if ((keyCode >= 49 && keyCode <= 52) || (keyCode >= 97 && keyCode <= 100)) {
                            player.playbackRate = Number(event.key)
                            t.setPlaybackRate(player.playbackRate)
                        }
                }
                // 阻止事件冒泡
                event.stopPropagation()
                event.preventDefault()
                return true
            }
        },
        isRegister:function isRegister(event, registerList,key) {
            let list = registerList
            /* 當前觸發的組合鍵 */
            let combineKey = []
            if (event.ctrlKey) {
                combineKey.push('ctrl')
            }
            if (event.shiftKey) {
                combineKey.push('shift')
            }
            if (event.altKey) {
                combineKey.push('alt')
            }
            combineKey.push(key)
            /* 通過循環判斷當前觸發的組合鍵和已註冊的組合鍵是否完全一致 */
            let hasRegArr = list.filter((shortcut) => {
                let regKey = shortcut.split('+');
                if (combineKey.length === regKey.length) {
                    let allMatch = regKey.every(key=>combineKey.includes(key));
                    if (allMatch) {
                        return true;
                    }
                }
                return false;
            })
            return hasRegArr.length==1
        },

        /* 運行自定義的快捷鍵操作,如果運行了會返回true */
        runCustomShortcuts: function (player, event) {
            if (!player || !event) return

            let t = h5Player;

            let taskConf = TCC.getTaskConfig()
            let confIsCorrect = tool.isObj(taskConf.shortcuts) && Array.isArray(taskConf.shortcuts.register) && taskConf.shortcuts.callback instanceof Function
            /* 判斷當前觸發的快捷鍵是否已被註冊 */

            let key = event.key.toLowerCase()
            if (confIsCorrect && t.isRegister(event, taskConf.shortcuts.register, key)) {
                // 執行自定義快捷鍵操作
                TCC.doTask('shortcuts', {
                    event,
                    player,
                    h5Player
                })
                return true
            } else {
                return false
            }
        },
        /* 判斷焦點是否處於可編輯元素 */
        isEditableTarget: function (target) {
            let isEditable = target.getAttribute && target.getAttribute('contenteditable') === 'true';
            let isInputDom = /INPUT|TEXTAREA|SELECT/.test(target.nodeName);
            return isEditable || isInputDom;
        },
        /* 按鍵響應方法 */
        keydownEvent: function (event) {
            //console.log('-full', gbl.checkFullScreen(document))
            let t = h5Player
            let keyCode = event.keyCode
            let key = event.key.toLowerCase()
            let player = t.player()
            if (event.code == "Space" && keyCode > 128) keyCode = 32;
            /* 處於可編輯元素中不執行任何快捷鍵 */
            if (t.isEditableTarget(event.target)) return
            /* shift+f 切換UA偽裝 */
            if (event.shiftKey && keyCode === 70) {
                t.switchFakeUA()
            }
            /* 未用到的按鍵不進行任何事件監聽 */
            //console.log('-keycode-', keyCode)
            let isInUseCode = t.keyCodeList.includes(keyCode) || t.keyList.includes(key)
            if (!isInUseCode) return
            if (!player) {
                // console.log('無可用的播放,不執行相關操作')
                return
            }
            /* 切換插件的可用狀態 */
            if (event.ctrlKey && keyCode === 32) {
                t.enable = !t.enable;
                if (t.enable) {
                    t.tips('啟用h5Player插件')
                } else {
                    t.tips('禁用h5Player插件')
                }
            }
            if (!t.enable) {
                console.log('h5Player 已禁用~')
                return false
            }
            // 按ctrl+\ 鍵進入聚焦或取消聚焦狀態,用於視頻標籤被遮擋的場景
            if (event.ctrlKey && keyCode === 220) {
                t.globalMode = !t.globalMode
                if (t.globalMode) {
                    t.tips('全局模式')
                } else {
                    t.tips('禁用全局模式')
                }
            }
            /* 非全局模式下,不聚焦則不執行快捷鍵的操作 */
            if (!t.globalMode && !t._isFoucs) return
            /* 判斷是否執行了自定義快捷鍵操作,如果是則不再響應後面默認定義操作 */
            if (t.runCustomShortcuts(player, event) === true) return
            /* 響應播放器相關操作 */
            t.playerTrigger(player, event)
        },
        /**
		 * 獲取播放進度
		 * @param player -可選 對應的h5 播放器對象, 如果不傳,則獲取到的是整個播放進度表,傳則獲取當前播放器的播放進度
		 */
        getPlayProgress: function (player) {
            let progressMap = window.localStorage.getItem('_h5_player_play_progress_')
            if (!progressMap) {
                progressMap = {}
            } else {
                progressMap = JSON.parse(progressMap)
            }
            if (!player) {
                return progressMap
            } else {
                let keyName = window.location.href || player.src
                if (progressMap[keyName]) {
                    return progressMap[keyName].progress
                } else {
                    return player.currentTime
                }
            }
        },
        /* 播放進度記錄器 */
        playProgressRecorder: function (player) {
            let t = h5Player
            clearTimeout(player._playProgressTimer_)
            var _player = player;
            var func_tmp = {};
            func_tmp._recorder = null;
            func_tmp._timerFunc = null;
            func_tmp._timerFunc = function () {
                var player = _player
                let progressMap = t.getPlayProgress()
                let keyName = window.location.href || player.src
                let list = Object.keys(progressMap)
                /* 只保存最近10個視頻的播放進度 */
                if (list.length > 10) {
                    /* 根據更新的時間戳,取出最早添加播放進度的記錄項 */
                    let timeList = []
                    list.forEach(function (keyName) {
                        progressMap[keyName] && progressMap[keyName].t && timeList.push(progressMap[keyName].t)
                    })
                    timeList = tool.quickSort(timeList)
                    let timestamp = timeList[0]
                    /* 刪除最早添加的記錄項 */
                    list.forEach(function (keyName) {
                        if (progressMap[keyName].t === timestamp) {
                            delete progressMap[keyName]
                        }
                    })
                }
                /* 記錄當前播放進度 */
                progressMap[keyName] = {
                    progress: player.currentTime,
                    t: new Date().getTime()
                }
                /* 存儲播放進度表 */
                window.localStorage.setItem('_h5_player_play_progress_', JSON.stringify(progressMap))
                /* 循環偵聽 */
                func_tmp._recorder()
            };
            func_tmp._recorder = function () {
                var player = _player
                player._playProgressTimer_ = setTimeout(func_tmp._timerFunc, 1000 * 2)
            }
            func_tmp._recorder()
        },
        /* 設置播放進度 */
        setPlayProgress: function (player, time) {
            if (!player) return
            let t = h5Player
            let curTime = Number(t.getPlayProgress(player))
            if (!curTime || Number.isNaN(curTime)) return
            player.currentTime = curTime || player.currentTime
            if (curTime > 3) {
                t.tips('- Playback Progress is restored -')
            }
        },
        /**
		 * 檢測h5播放器是否存在
		 * @param callback
		 */
        detecH5Player: function () {
            let t = this
            let playerList = t.getPlayerList()
            if (playerList.length) {
                console.log(' - HTML5 Video is detected -')
                /* 單video實例標籤的情況 */
                if (playerList.length === 1) {
                    t.playerInstance = playerList[0]
                    t.initPlayerInstance(true)
                } else {
                    /* 多video實例標籤的情況 */
                    playerList.forEach(function (player) {
                        /* 鼠標移到其上面的時候重新指定實例 */
                        if (player._hasMouseRedirectEvent_) return
                        player.addEventListener('mouseenter', function (event) {
                            t.playerInstance = event.target
                            t.initPlayerInstance(false)
                        })
                        player._hasMouseRedirectEvent_ = true
                        /* 播放器開始播放的時候重新指向實例 */
                        if (player._hasPlayingRedirectEvent_) return
                        player.addEventListener('playing', function (event) {
                            t.playerInstance = event.target
                            t.initPlayerInstance(false)
                            /* 同步之前設定的播放速度 */
                            t.setPlaybackRate()
                        })
                        player._hasPlayingRedirectEvent_ = true
                    })
                }
            }
        },
        /* 綁定相關事件 */
        bindEvent: function () {
            var t = this
            if (t._hasBindEvent_) return
            console.log('keydown bind')
            document.removeEventListener('keydown', t.keydownEvent)
            document.addEventListener('keydown', t.keydownEvent, true)
            /* 相容iframe操作 */
            if (window.top !== window && window.top.document) {
                window.top.document.removeEventListener('keydown', t.keydownEvent)
                window.top.document.addEventListener('keydown', t.keydownEvent, true)
            }
            t._hasBindEvent_ = true
        },
        load: false
    }



    hackAttachShadow()
    hackEventListener()
    gbl.h5Player = h5Player;

    function wVideo(video) {

        wVideo._readyCount++;
        /* 檢測是否存在H5播放器 */
        h5Player.detecH5Player()

        var pdd = document.createElement('div')
        pdd.id = 'mysvg'
        pdd.innerHTML = `
<svg id='image' version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="sharpen1">
<feConvolveMatrix filterRes="100 100" style="color-interpolation-filters:linearrgb" order="3" kernelMatrix="` + `
-0.3 -0.3 -0.3
-0.3 3.4 -0.3
-0.3 -0.3 -0.3`.replace(/[\n\r]+/g, '  ').trim() + `"  preserveAlpha="true"/>
</filter>
<filter id="unsharpen1">
<feConvolveMatrix style="color-interpolation-filters:sRGB;color-rendering: optimizeQuality;color-interpolation: sRGB;" order="5" kernelMatrix="` + `  -0.00391  -0.01563  -0.02344  -0.01563  -0.00391
-0.01563  -0.06250  -0.09375  -0.06250  -0.01563
-0.02344  -0.09375   1.85980  -0.09375  -0.02344
-0.01563  -0.06250  -0.09375  -0.06250  -0.01563
-0.00391  -0.01563  -0.02344  -0.01563  -0.00391`.replace(/[\n\r]+/g, ' ').trim() + `"  preserveAlpha="true"/>
</filter>
</defs>
</svg>
`;
        video = video||document.querySelector('video');

        if (video) {
            if(video.parentNode) video.parentNode.appendChild(pdd);
            video.addEventListener('loadedmetadata', function () {
                console.log('video size', video.videoWidth + ' x ' + video.videoHeight);
            }, true);
        }
    }


    wVideo._readyCount=0;

    try {

        /* 檢測到有視頻標籤就進行初始化 */
        ready('video', function (elm) {
            wVideo(elm);
        })
        /* 檢測shadow dom 下面的video */
        document.addEventListener('addShadowRoot', function (e) {
            ready('video', function (elm) {
                wVideo(elm);
            }, e.detail.shadowRoot)
        })
    } catch (e) {
        console.error('h5player:', e)
    }

})();