Greasy Fork is available in English.

youtube中文字幕

油管自动跳广告,机翻中文本字幕,视频下载,srt字幕下载, 不常看greasyfork,有问题请发邮件:vsq_kuangqi@qq.com

// ==UserScript==
// @name         youtube中文字幕
// @namespace    https://github.com/wlpha/Youtube-Automatic-Translation-V2
// @match        *://www.youtube.com/watch?v=*
// @match        *://www.youtube.com
// @match        *://www.youtube.com/*
// @author       wlpha
// @version      0.2.9
// @run-at       document-start
// @description  油管自动跳广告,机翻中文本字幕,视频下载,srt字幕下载, 不常看greasyfork,有问题请发邮件:vsq_kuangqi@qq.com
// @homepage     https://greasyfork.org/zh-CN/scripts/406994-youtube%E4%B8%AD%E8%8B%B1%E5%8F%8C%E8%AF%AD%E5%AD%97%E5%B9%95
// ==/UserScript==


(function() {
    var captionWidget = null;
    var captionSubWidget = null;
    var captionConrtol_1 = null;
    var captionConrtol_2 = null;
    var captionUrl_1 = null;
    var captionUrl_2 = null;
    var visiableCaption = false;
    var captionTranslateContent_1 = null;
    var captionTranslateContent_2 = null;
    var hiddenCaptionsTimer_1 = null;
    var hiddenCaptionsTimer_2 = null;
    var captionDelayLoadTime = 1;
    var checkTranslateCaptionTimer = null;
    var hasFetchTranslationButton = null;
    var hasSubtitleLabel = null;
    var saveEnSubtitleButton = null;
    var saveEnSubtitleButton = null;
    var downloadVideoList = null;
    var saveDownloadVideoButton = null;

    var captionFontBottomPosition = window.localStorage.getItem('double-translation-font-position');
    captionFontBottomPosition = captionFontBottomPosition ? captionFontBottomPosition : '60px';
    var captionFontMiniBottomPosition = window.localStorage.getItem('double-translation-font-mini-position');
    captionFontMiniBottomPosition = captionFontMiniBottomPosition ? captionFontMiniBottomPosition : '10px';
    var captionFontSize = window.localStorage.getItem('double-translation-font-size')
    captionFontSize = captionFontSize ? captionFontSize : '2.0em';
    var captionFontMiniSize = window.localStorage.getItem('double-translation-font-mini-size')
    captionFontMiniSize = captionFontMiniSize ? captionFontMiniSize :'1.4em';
    var captionFontPadding = '0px 6px';
    var captionFontColor = '#ffffff';
    var captionFontTextShadow = '0 0 2px #000';
    var captionFontTextStroke= '#00000f0 3px';

    window.doubleTranlationPluginState = {};
    window.currentVideoId =  null;

    function GenerateCaptionsUrl(toLang)
    {
        var url = window.ytInitialPlayerResponse.captions.playerCaptionsTracklistRenderer.captionTracks[0].baseUrl;
        /*
        if(url.indexOf('&lang=zh-Hant') !== -1)
        {
            url = url.replace('&lang=zh-Hant', '&lang=zh-Hans');
        }
        */

        url += '&fmt=json3&xorb=2&xobt=3&xovt=3';
        // url += '&cbr=Chrome&cbrver=115.0.0.0&c=WEB&cver=2.20230724.01.00&cplayer=UNIPLAYER&cos=Windows&cosver=10.0&cplatform=DESKTOP';

        if (url.indexOf('&lang=' + toLang) === -1) {
            url += '&tlang=' + toLang;
        }
       return url;
    }

    function HasTranslateCaption()
    {
        try
        {
            window.ytInitialPlayerResponse.captions.playerCaptionsTracklistRenderer.captionTracks;
            return true;
        }catch(err)
        {
            return false;
        }
    }

    function GetCurrentVideoPlayerUrls()
    {
        var url = window.location.href + '&pbj=1';
        var headers = {
                    'x-youtube-client-name': 1,
                    'x-youtube-client-version': window.ytcfg.data_.INNERTUBE_CONTEXT_CLIENT_VERSION,
        };
        if(window.ytcfg.data_.ID_TOKEN) {
          headers['x-youtube-identity-token'] =  window.ytcfg.data_.ID_TOKEN
        };

        fetch(url,
            {
                headers: headers
            })
        .then(function(response)
        {
            response.json().then(function(response)
            {
                var videoListInfo = [];

                if(typeof response === 'string')
                {
                    response = JSON.parse(response);
                }

                for(var i=0; i<response.length; i++)
                {
                    // debugger
                    var item = response[i];
                    if(item.playerResponse)
                    {
                        var data = item.playerResponse;
                        if(data && data.streamingData && data.streamingData.adaptiveFormats)
                        {
                            var dataList = data.streamingData.adaptiveFormats;
                            for(var x=0; x<dataList.length; x++)
                            {
                                var dataItem = dataList[x];

                                if(!dataItem.url)
                                {
                                    if(dataItem.signatureCipher)
                                    {
                                        var dict = DecodeURIParamToDict(dataItem.signatureCipher);
                                        dataItem.url = dict.url + '&' + dict.sp + '=' + encodeURIComponent(DecryptBySignatureCipher(dict.s));
                                    } else
                                    {
                                        continue;
                                    }
                                }

                                var label = '';

                                if(dataItem.mimeType.indexOf('video/') > -1)
                                {
                                    var videoType = dataItem.mimeType.match(/(\w+)\/(.*?);/i);
                                    var videoCodec = dataItem.mimeType.match(/codecs="(.*?)"/i)[1];
                                    label = '[视频]' + dataItem.qualityLabel + ';' + videoType[1] + '/' + videoType[2] + ';' + videoCodec;

                                } else
                                {
                                    var videoType = dataItem.mimeType.match(/(\w+)\/(.*?);/i);
                                    var videoCodec = dataItem.mimeType.match(/codecs="(.*?)"/i)[1];
                                    label = '[未知]' + videoType[1] + '/' + videoType[2] + ';' + videoCodec;
                                }

                                var itemInfo = {
                                    'label': label,
                                    'url': dataItem.url
                                }
                                // console.log(dataItem);
                                videoListInfo.push(itemInfo);

                            }
                        }
                    }
                }

                // 移除原来的
                downloadVideoList.innerHTML = '';
                var option = document.createElement('option');
                option.setAttribute('value', 'none');

                if(videoListInfo.length > 0)
                {
                    option.innerText = '选择视频下载';
                } else
                {
                    option.innerText = '获取不到链接';
                }

                downloadVideoList.appendChild(option);
                // 添加到列表
                var index = 0;
                for(var i=0; i<videoListInfo.length; i++)
                {
                    var itemInfo = videoListInfo[i];
                    option = document.createElement('option');
                    option.setAttribute('value', itemInfo.url);
                    option.innerText = (++index) + '.' + itemInfo.label;
                    downloadVideoList.appendChild(option);
                }

                downloadVideoList.style.display = 'inline-block';
                saveDownloadVideoButton.style.display = 'inline-block';

            });

        }).catch(function(err)
        {

        });
    }

    function GetTranslateCaptionsUrl()
    {

        var videoId = window.ytInitialPlayerResponse.videoDetails.videoId;
        var videoTitle = window.ytInitialPlayerResponse.videoDetails.title;
        var videoAuthor = window.ytInitialPlayerResponse.videoDetails.author;
        var videoViewCount = window.ytInitialPlayerResponse.videoDetails.viewCount;

        var captionTracks = window.ytInitialPlayerResponse.captions.playerCaptionsTracklistRenderer.captionTracks;
        var hasZhHans = false;
        var hasZhHant = false;
        var hasEn = false;
        var defaultLang = null;
        var selectLang = null;

        // 防止重复获取视频
        if(window.currentVideoId !== null && window.currentVideoId ===  videoId) {
          return;
        } else {
          window.currentVideoId = videoId;
          ResetTranslateCaptionsUrl();
        }

        try
        {
            for(var i=0; i< captionTracks.length; i++)
            {
                var item = captionTracks[i];
                switch(item.languageCode)
                {
                    case 'zh-Hans':
                        hasZhHans = true;
                        break;
                    case 'zh-Hant':
                        hasZhHant = true;
                        break;
                    case 'en':
                        hasEn = true;
                        break;
                    default:
                        defaultLang = item.languageCode;
                        break;
                }
            }

            if(hasZhHans)
            {
                selectLang = 'zh-Hans';
            }
            else if(hasZhHant)
            {
                selectLang = 'zh-Hant';
            }
            else if(hasEn)
            {
                selectLang = 'en';
            }
            else if(defaultLang !== null)
            {
                selectLang = defaultLang;
            }

            // 没有可用翻译字幕
            if(selectLang === null)
            {
                console.log('没有可用翻译字幕');
                return;
            }

            // 双语字幕
            captionUrl_1 = GenerateCaptionsUrl('zh-Hans');
            // 屏蔽英文
            // captionUrl_2 = GenerateCaptionsUrl('en');
            captionUrl_2 = null;

            console.log('-----------------youtube自动切换中文字幕(油猴插件)-----------------------');
            console.log('视频标题:' + videoTitle);
            console.log('视频作者:' + videoAuthor);
            console.log('视频ID:' + videoId);
            console.log('播放数:' + videoViewCount);
            console.log('中文字幕:' + captionUrl_1);
            // 屏蔽英文
            // console.log('英文字幕:' + captionUrl_2);
        }catch(e)
        {

        }

    }

    function ResetTranslateCaptionsUrl()
    {
        captionUrl_1 = null;
        captionUrl_2 = null;
    }

    function FetchTranslateCaptionsContent()
    {
        visiableCaption = false;
        captionTranslateContent_1 = null;
        captionTranslateContent_2 = null;

        if (captionUrl_1 !== null) {
            fetch(captionUrl_1)
            .then(function(response)
            {
                response.json().then(function(response)
                {
                    visiableCaption = true;
                    captionTranslateContent_1 = GenerateSRTArray(response.events);

                    if(hasFetchTranslationButton !== null) {
                        hasFetchTranslationButton.innerText = '正常';
                    }

                    // 显示字幕保存按钮
                    var subtitleContent = GenerateSRTFromZhhans();
                    var fileContent = 'data:text/plain;charset=utf-8,' + encodeURIComponent(subtitleContent);
                    var filename = '[中字]' +window.ytInitialPlayerResponse.videoDetails.title + '.srt';
                    saveZhhansSubtitleButton.setAttribute('href', fileContent);
                    saveZhhansSubtitleButton.setAttribute('download', filename);

                    hasSubtitleLabel.style.display = 'inline-block';
                    saveZhhansSubtitleButton.style.display = 'inline-block';

                    console.log('获取到字幕【中文】:成功');

                }).catch(function(err)
                {
                    captionTranslateContent_1 = null;

                    if(hasFetchTranslationButton !== null) {
                        hasFetchTranslationButton.innerText = '失败';
                    }
                    console.log('获取到字幕【中文】:失败2');
                });

            }).catch(function(err)
            {
                captionTranslateContent_1 = null;

                if(hasFetchTranslationButton !== null) {
                    hasFetchTranslationButton.innerText = '失败';
                }
                console.log('获取到字幕【中文】:失败');
            });
        }

        if (captionUrl_2 !== null) {
            fetch(captionUrl_2)
            .then(function(response)
            {
                response.json().then(function(response)
                {
                    visiableCaption = true;
                    captionTranslateContent_2 = GenerateSRTArray(response.events);
                    // 显示字幕保存按钮
                    var subtitleContent = GenerateSRTFromEn();
                    var fileContent = 'data:text/plain;charset=utf-8,' + encodeURIComponent(subtitleContent);
                    var filename = '[英字]' + window.ytInitialPlayerResponse.videoDetails.title + '.srt';
                    saveEnSubtitleButton.setAttribute('href', fileContent);
                    saveEnSubtitleButton.setAttribute('download', filename);

                    hasSubtitleLabel.style.display = 'inline-block';
                    saveEnSubtitleButton.style.display = 'inline-block';

                    console.log('获取到字幕【英文】:成功');

                }).catch(function(err)
                {
                    console.log(err);
                    captionTranslateContent_2 = null;
                    console.log('获取到字幕【英文】:失败2');
                });

            }).catch(function(err)
            {
                captionTranslateContent_2 = null;
                console.log('获取到字幕【英文】:失败');
            });
        }

    }

    function CreateCaptionControl()
    {
        var mainPlayer = document.querySelector('#ytd-player .html5-video-player');
        if(mainPlayer === null)
        {
            return false;
        }


        captionWidget = document.querySelector('#captionWidget');
        captionSubWidget = document.querySelector('#captionSubWidget');
        captionConrtol_1 = document.querySelector('#captionConrtol_1');
        captionConrtol_2 = document.querySelector('#captionConrtol_2');

        if (captionConrtol_1 !== null) {
            captionConrtol_1.style.display = 'none';
        }
        if (captionConrtol_2 !== null) {
            captionConrtol_2.style.display = 'none';
        }

        if(captionWidget !== null)
        {
            return true;
        } else
        {
            captionWidget = document.createElement('div');
            captionSubWidget = document.createElement('div');
            captionConrtol_1 = document.createElement('p');
            captionConrtol_2 = document.createElement('p');

            captionWidget.id = 'captionWidget';
            captionSubWidget.id = 'captionSubWidget';
            captionConrtol_1.id = 'captionConrtol_1';
            captionConrtol_2.id = 'captionConrtol_2';


            mainPlayer.parentElement.style.height = '100%';

            captionWidget.style.pointerEvents = 'none';
            captionWidget.style.position = 'absolute';
            captionWidget.style.zIndex = 99999999;
            captionWidget.style.width = '100%';
            captionWidget.style.height = '100%';


            captionSubWidget.style.position = 'absolute';
            captionSubWidget.style.width = '100%';
            captionSubWidget.style.height = 'fit-content';
            captionSubWidget.style.bottom = captionFontBottomPosition;

            captionConrtol_1.style.width = captionConrtol_2.style.width = 'fit-content';
            captionConrtol_1.style.width = captionConrtol_2.style.width = '-moz-fit-content';
            captionConrtol_1.style.margin = captionConrtol_2.style.margin = '0 auto';
            captionConrtol_1.style.padding = captionConrtol_2.style.padding = captionFontPadding;
            captionConrtol_1.style.fontSize = captionConrtol_2.style.fontSize = captionFontSize;
            captionConrtol_1.style.backgroundColor = captionConrtol_2.style.backgroundColor = 'rgb(0 0 0 / 0.6)';
            captionConrtol_1.style.color = captionConrtol_2.style.color = captionFontColor;
            captionConrtol_1.style.textShadow = captionConrtol_2.style.textShadow = captionFontTextShadow;
            captionConrtol_1.style.webkitTextStroke = captionConrtol_2.style.webkitTextStroke = captionFontTextStroke;
            captionConrtol_1.style.fontWeight = captionConrtol_2.style.fontWeight = 'bold';
            captionConrtol_1.style.display = captionConrtol_2.style.display = 'none';
            captionConrtol_1.style.wordBreak = captionConrtol_2.style.wordBreak = 'keep-all';

            captionSubWidget.appendChild(captionConrtol_1);
            captionSubWidget.appendChild(captionConrtol_2);
            captionWidget.appendChild(captionSubWidget);

            mainPlayer.prepend(captionWidget);

            captionWidget = document.querySelector('#captionWidget');
            captionSubWidget = document.querySelector('#captionSubWidget');
            captionConrtol_1 = document.querySelector('#captionConrtol_1');
            captionConrtol_2 = document.querySelector('#captionConrtol_2');
        }
    }

    function Wrap()
    {
        return arguments;
    }

    function HiddenDownloadSubtitleButton()
    {
        if(hasSubtitleLabel.style.display === 'inline-block')
        {
            hasSubtitleLabel.style.display = 'none';
        }
        if(saveEnSubtitleButton.style.display === 'inline-block')
        {
            saveEnSubtitleButton.style.display = 'none';
        }
        if(saveZhhansSubtitleButton.style.display === 'inline-block')
        {
            saveZhhansSubtitleButton.style.display = 'none';
        }
    }

    function HiddenDownloadVideoButton()
    {
        if(downloadVideoList.style.display === 'inline-block')
        {
            downloadVideoList.style.display = 'none';
        }
        if(saveDownloadVideoButton.style.display === 'inline-block')
        {
            saveDownloadVideoButton.style.display = 'none';
        }
    }

    function ShowCaption()
    {
        var createElement = document.createElement;
        document.createElement = function(tagName, options)
        {
            var domObject = createElement.apply(document, Wrap(tagName, options));
            if(tagName.toLowerCase() === 'video')
            {
                domObject.addEventListener('ended', function()
                {
                    if(this.classList.contains('html5-main-video'))
                    {
                        HiddenDownloadSubtitleButton();
                        HiddenDownloadVideoButton();
                    }
                });

                domObject.addEventListener('loadstart', function()
                {
                    if(this.classList.contains('html5-main-video'))
                    {

                        if(checkTranslateCaptionTimer !== null)
                        {
                            clearInterval(checkTranslateCaptionTimer);
                            checkTranslateCaptionTimer = null;
                        }
                        checkTranslateCaptionTimer = setInterval(function()
                        {
                            if(HasTranslateCaption())
                            {
                                clearInterval(checkTranslateCaptionTimer);
                                checkTranslateCaptionTimer = null;

                                setTimeout(function()
                                {
                                    try
                                    {
                                        var mainPlayer = domObject;

                                        // 创建字幕控件
                                        CreateCaptionControl();
                                        // 字幕下载按钮隐藏,可能没有字幕
                                        HiddenDownloadSubtitleButton();
                                        // 隐藏下载视频按钮
                                        HiddenDownloadVideoButton();

                                        // 获取字幕url
                                        GetTranslateCaptionsUrl();
                                        FetchTranslateCaptionsContent();

                                        var lastTime = (+new Date());
                                        mainPlayer.addEventListener('timeupdate', function()
                                        {
                                            // 优化一下,限制速率30帧, 不要频繁进入
                                            var currentTime = (+new Date());
                                            if (currentTime - lastTime > 1000 / 30) {
                                                lastTime = currentTime;
                                            } else
                                            {
                                                return;
                                            }
                                            // 获取字幕失败
                                            if(!visiableCaption)
                                            {
                                                return;
                                            }
                                            // 主动关闭字幕
                                            if(!GetTranslationState())
                                            {
                                                return;
                                            }
                                            // 创建字幕控件
                                            CreateCaptionControl();

                                            // 有些视频没有字幕, 所以导致显示上个视频字幕, 判断按钮隐藏字幕
                                            var subtitlesBtn = document.querySelector('.ytp-subtitles-button');
                                            if(subtitlesBtn === null || subtitlesBtn.style.display === 'none')
                                            {
                                                return;
                                            }

                                            // 判断迷你模式
                                            var miniPlayer = document.querySelector('.ytp-miniplayer-ui');
                                            if(miniPlayer && miniPlayer.style.display !== 'none')
                                            {
                                                // 迷你模式
                                                captionSubWidget.style.bottom = captionFontMiniBottomPosition;
                                                captionConrtol_1.style.fontSize = captionConrtol_2.style.fontSize = captionFontMiniSize;
                                            } else
                                            {
                                                // 正常模式
                                                captionSubWidget.style.bottom = captionFontBottomPosition;
                                                captionConrtol_1.style.fontSize = captionConrtol_2.style.fontSize = captionFontSize;
                                            }


                                            var time = mainPlayer.currentTime * 1000;

                                            // 中文字幕
                                            if(captionTranslateContent_1 != null)
                                            {
                                                for(var i=0; i<captionTranslateContent_1.length; i++)
                                                {
                                                    var item = captionTranslateContent_1[i];
                                                    if(item.start === 0 && item.end === 0 && item.text === '\n') {
                                                        continue;
                                                    }

                                                    if(time >= item.start && time <= item.end)
                                                    {
                                                        var endTime = item.end;
                                                        if(captionConrtol_1 !== null)
                                                        {
                                                            try
                                                            {
                                                                var displayText = item.text;
                                                                if(captionConrtol_1.innerHTML !== displayText)
                                                                {
                                                                    captionConrtol_1.innerHTML = displayText;
                                                                }
                                                                if(captionConrtol_1.style.display !== 'block')
                                                                {
                                                                    captionConrtol_1.style.display = 'block';
                                                                    if (hiddenCaptionsTimer_1 !== null) {
                                                                        clearTimeout(hiddenCaptionsTimer_1);
                                                                    }
                                                                    hiddenCaptionsTimer_1 = setTimeout(function()
                                                                    {
                                                                        captionConrtol_1.style.display = 'none';
                                                                        clearTimeout(hiddenCaptionsTimer_1);
                                                                        hiddenCaptionsTimer_1 = null;
                                                                    }, endTime);
                                                                }
                                                            }catch(err)
                                                            {
                                                                continue;
                                                            }
                                                        }
                                                        break;
                                                    }
                                                }
                                            }

                                            // 英文字幕
                                            if(captionTranslateContent_2 != null)
                                            {
                                                for(var i=0; i<captionTranslateContent_2.length; i++)
                                                {
                                                    var item = captionTranslateContent_2[i];
                                                    if(item.start === 0 && item.end === 0 && item.text === '\n') {
                                                        continue;
                                                    }
                                                    if(time >= item.start && time <= item.end)
                                                    {
                                                        var endTime = item.end;
                                                        if(captionConrtol_2 !== null)
                                                        {
                                                            try
                                                            {
                                                                var displayText = item.text;
                                                                if(captionConrtol_2.innerHTML !== displayText)
                                                                {
                                                                    captionConrtol_2.innerHTML = displayText;
                                                                }

                                                                if(captionConrtol_2.style.display !== 'block')
                                                                {
                                                                    captionConrtol_2.style.display = 'block';
                                                                    if (hiddenCaptionsTimer_2 !== null) {
                                                                        clearTimeout(hiddenCaptionsTimer_2);
                                                                    }
                                                                    hiddenCaptionsTimer_2 = setTimeout(function()
                                                                    {
                                                                        captionConrtol_2.style.display = 'none';
                                                                        clearTimeout(hiddenCaptionsTimer_2);
                                                                        hiddenCaptionsTimer_2 = null;
                                                                    }, endTime);
                                                                }
                                                            }catch(err)
                                                            {
                                                                continue;
                                                            }
                                                        }
                                                        break;
                                                    }
                                                }
                                            }


                                        });
                                    }catch(err)
                                    {

                                    }

                                }, captionDelayLoadTime);
                            }
                        }, 300);

                    }
                });
            }
            return domObject;
        }

    }

    function isVideoAdsTime(){
        var ad = document.querySelector('.ad-showing');
        var skipAdButton = document.querySelector('.ytp-ad-skip-button');

        var volumeOpenState = document.querySelector("#ytp-svg-volume-animation-mask");
        var volumeButton = document.querySelector('.ytp-mute-button');

        // 底部广告
        var ads = document.querySelector('.ytp-ad-overlay-container');
        if(ads !== null)
        {
            ads.style.display = 'none';
        }

        /*
        // 判断有没有广告
        if(ad){
            // 关闭音量
            if(volumeOpenState && volumeButton)
            {
                volumeButton.click();
            }
        } else {
            // 正常视频,打开音量
            if(volumeOpenState == null && volumeButton){
                volumeButton.click();
            }
        }
        */

        // 跳过广告
        if(skipAdButton)
        {
            skipAdButton.click();
        }
        return ad != null;
    }

    function FuckAds()
    {
        setInterval(function()
        {
            try
            {
                isVideoAdsTime();
            }catch(err)
            {

            }

        }, 200);
    }

    function HookDataUpdate()
    {
        setInterval(function()
        {
            try
            {
                var pageManger = document.querySelector('#page-manager');
                if(pageManger !== null)
                {
                    if(pageManger.isHook !== undefined)
                    {
                        return;
                    }
                    pageManger.isHook = true;
                    var oldUpdatePageData = pageManger.updatePageData;
                    var updatePageData = function(data)
                    {
                        try
                        {
                            window.ytInitialPlayerResponse = data.playerResponse;

                            GetTranslateCaptionsUrl();
                            FetchTranslateCaptionsContent();
                        }catch(err)
                        {

                        }

                        return oldUpdatePageData.apply(this, arguments);
                    }

                    if(oldUpdatePageData !== updatePageData)
                    {
                       pageManger.updatePageData = updatePageData;
                    }
                }
            }catch(err)
            {

            }
        }, 1000);

    }

    function GetTranslationState()
    {
        return window.localStorage.getItem('double-translation-plugin-state') === 'on';
    }

    function AddTranslationButton()
    {
        var coltrolPanel = document.querySelector('.ytp-chrome-controls .ytp-right-controls');
        if(coltrolPanel && coltrolPanel.querySelector('.double-translation-plugin-btn') == null) {
            // 开启字幕
            var translationButton = document.createElement('button');
            translationButton.className = 'double-translation-plugin-btn';
            translationButton.style = 'position: relative;top: -36%; margin-right:10px; border-radius: 25px;border: none; opacity: 0.95; background-color: #fff; outline:none;';

            translationButton.onclick = function(){
                var translationState = window.localStorage.getItem('double-translation-plugin-state');
                if(translationState == 'on') {
                    window.localStorage.setItem('double-translation-plugin-state', 'off');
                    translationButton.innerText = '开启字幕';
                    // 隐藏字幕
                    captionConrtol_1.style.display = captionConrtol_2.style.display = 'none';
                } else {
                    window.localStorage.setItem('double-translation-plugin-state', 'on');
                    translationButton.innerText = '关闭字幕';
                }
            }

            if(GetTranslationState()){
                translationButton.innerText = '关闭字幕';
            } else {
                translationButton.innerText = '开启字幕';
            }
            // 屏蔽youtube强行添加的事件
            translationButton.addEventListener = function() {};


            // 添加设置事件

            // 字体减大小
            var fontDecSizeButton = document.createElement('button');
            fontDecSizeButton.className = 'double-translation-plugin-font-dec-size-btn';
            fontDecSizeButton.style = 'position: relative;top: -36%; margin-right:3px; border-radius: 25px;border: none; opacity: 0.95; background-color: #fff; outline:none;';
            fontDecSizeButton.innerText = '-';
            fontDecSizeButton.onclick = function() {
                var miniPlayer = document.querySelector('.ytp-miniplayer-ui');
                if(miniPlayer && miniPlayer.style.display !== 'none')
                {
                    // 迷你模式
                    var fontSize = parseFloat(captionFontMiniSize) - 0.1;
                    if(fontSize <= 0.1)
                    {
                        fontSize = 0.1
                    }
                    fontSize = fontSize + 'em';
                    captionFontMiniSize = fontSize;
                    window.localStorage.setItem('double-translation-font-mini-size', captionFontMiniSize);
                    captionConrtol_1.style.fontSize = captionConrtol_2.style.fontSize = captionFontMiniSize;
                } else
                {
                    // 正常模式
                    var fontSize = parseFloat(captionFontSize) - 0.1;
                    if(fontSize <= 0.1)
                    {
                        fontSize = 0.1
                    }
                    fontSize = fontSize + 'em';
                    captionFontSize = fontSize;
                    window.localStorage.setItem('double-translation-font-size', captionFontSize);
                    captionConrtol_1.style.fontSize = captionConrtol_2.style.fontSize = captionFontSize;
                }
            }

            // 字体加大小
            var fontAddSizeButton = document.createElement('button');
            fontAddSizeButton.className = 'double-translation-plugin-font-add-size-btn';
            fontAddSizeButton.style = 'position: relative;top: -36%; margin-right:3px; border-radius: 25px;border: none; opacity: 0.95; background-color: #fff; outline:none;';
            fontAddSizeButton.innerText = '+';
            fontAddSizeButton.onclick = function() {
                var miniPlayer = document.querySelector('.ytp-miniplayer-ui');
                if(miniPlayer && miniPlayer.style.display !== 'none')
                {
                    // 迷你模式
                    var fontSize = parseFloat(captionFontMiniSize) + 0.1;
                    if(fontSize >= 10)
                    {
                        fontSize = 10
                    }
                    fontSize = fontSize + 'em';
                    captionFontMiniSize = fontSize;
                    window.localStorage.setItem('double-translation-font-mini-size', captionFontMiniSize);
                    captionConrtol_1.style.fontSize = captionConrtol_2.style.fontSize = captionFontMiniSize;
                } else
                {
                    // 正常模式
                    var fontSize = parseFloat(captionFontSize) + 0.1;
                    if(fontSize >= 10)
                    {
                        fontSize = 10
                    }
                    fontSize = fontSize + 'em';
                    captionFontSize = fontSize;
                    window.localStorage.setItem('double-translation-font-size', captionFontSize);
                    captionConrtol_1.style.fontSize = captionConrtol_2.style.fontSize = captionFontSize;
                }
            }
            // 字体向上
            var fontUpButton = document.createElement('button');
            fontUpButton.className = 'double-translation-plugin-font-add-size-btn';
            fontUpButton.style = 'position: relative;top: -36%; margin-right:3px; border-radius: 25px;border: none; opacity: 0.95; background-color: #fff; outline:none;';
            fontUpButton.innerText = '▲';
            fontUpButton.onclick = function() {
                var miniPlayer = document.querySelector('.ytp-miniplayer-ui');
                if(miniPlayer && miniPlayer.style.display !== 'none')
                {
                    // 迷你模式
                    var position = parseFloat(captionFontMiniBottomPosition) + 2;
                    position = position + 'px';
                    captionFontMiniBottomPosition = position;
                    window.localStorage.setItem('double-translation-font-mini-position', captionFontMiniBottomPosition);
                    captionSubWidget.style.bottom = captionFontMiniBottomPosition;
                } else
                {
                    // 正常模式
                    var position = parseFloat(captionFontBottomPosition) + 2;
                    position = position + 'px';
                    captionFontBottomPosition = position;
                    window.localStorage.setItem('double-translation-font-position', captionFontBottomPosition);
                    captionSubWidget.style.bottom = captionFontBottomPosition;
                }
            }
            // 字体向下
            var fontDownButton = document.createElement('button');
            fontDownButton.className = 'double-translation-plugin-font-add-size-btn';
            fontDownButton.style = 'position: relative;top: -36%; margin-right:3px; border-radius: 25px;border: none; opacity: 0.95; background-color: #fff; outline:none;';
            fontDownButton.innerText = '▼';
            fontDownButton.onclick = function() {
                var miniPlayer = document.querySelector('.ytp-miniplayer-ui');
                if(miniPlayer && miniPlayer.style.display !== 'none')
                {
                    // 迷你模式
                    var position = parseFloat(captionFontMiniBottomPosition) - 2;
                    position = position + 'px';
                    captionFontMiniBottomPosition = position;
                    window.localStorage.setItem('double-translation-font-mini-position', captionFontMiniBottomPosition);
                    captionSubWidget.style.bottom = captionFontMiniBottomPosition;
                } else
                {
                    // 正常模式
                    var position = parseFloat(captionFontBottomPosition) - 2;
                    position = position + 'px';
                    captionFontBottomPosition = position;
                    window.localStorage.setItem('double-translation-font-position', captionFontBottomPosition);
                    captionSubWidget.style.bottom = captionFontBottomPosition;
                }
            }


            // 重置按钮
            var fontResetButton = document.createElement('button');
            fontResetButton.className = 'double-translation-plugin-font-reset-btn';
            fontResetButton.style = 'position: relative;top: -36%; margin-right:3px; border-radius: 25px;border: none; opacity: 0.95; background-color: #fff; outline:none;';
            fontResetButton.innerText = '重置';
            fontResetButton.onclick = function() {

                var miniPlayer = document.querySelector('.ytp-miniplayer-ui');
                if(miniPlayer && miniPlayer.style.display !== 'none')
                {
                    // 迷你模式
                    captionFontMiniBottomPosition = '10px';
                    window.localStorage.setItem('double-translation-font-mini-position', captionFontMiniBottomPosition);
                    captionFontMiniSize = '1.4em';
                    window.localStorage.setItem('double-translation-font-mini-size', captionFontMiniSize);

                    captionConrtol_1.style.fontSize = captionConrtol_2.style.fontSize = captionFontMiniSize;
                    captionSubWidget.style.bottom = captionFontMiniBottomPosition;
                }
                else
                {
                    // 正常模式
                    captionFontBottomPosition = '60px';
                    window.localStorage.setItem('double-translation-font-position', captionFontBottomPosition);
                    captionFontSize = '2.0em';
                    window.localStorage.setItem('double-translation-font-size', captionFontSize);

                    captionConrtol_1.style.fontSize = captionConrtol_2.style.fontSize = captionFontSize;
                    captionSubWidget.style.bottom = captionFontBottomPosition;
                }
            }

            // 保存中文字幕
            saveZhhansSubtitleButton = document.createElement('a');
            saveZhhansSubtitleButton.style.display = 'none';
            var _saveZhhansSubtitleButton = document.createElement('button');
            _saveZhhansSubtitleButton.className = 'double-translation-plugin-download-zhhans-subtitle-btn';
            _saveZhhansSubtitleButton.style = 'position: relative;top: -36%; margin-right:3px; border-radius: 25px;border: none; opacity: 0.95; background-color: #fff; outline:none;';
            _saveZhhansSubtitleButton.innerText = '中字';
            saveZhhansSubtitleButton.appendChild(_saveZhhansSubtitleButton);

            // 保存英文字幕
            saveEnSubtitleButton = document.createElement('a');
            saveEnSubtitleButton.style.display = 'none';
            var _saveEnSubtitleButton  = document.createElement('button');
            _saveEnSubtitleButton.className = 'double-translation-plugin-download-en-subtitle-btn';
            _saveEnSubtitleButton.style = 'position: relative;top: -36%; margin-right:3px; border-radius: 25px;border: none; opacity: 0.95; background-color: #fff; outline:none;';
            _saveEnSubtitleButton.innerText = '英字';
            saveEnSubtitleButton.appendChild(_saveEnSubtitleButton);


            hasSubtitleLabel = document.createElement('span');
            hasSubtitleLabel.className = 'double-translation-plugin-download-label';
            hasSubtitleLabel.style = 'position: relative;top: -36%; margin-right:3px; color: #fff; outline:none; font-weight: bold;display:none;';
            hasSubtitleLabel.innerText = '字幕下载:';


            hasFetchTranslationButton = document.createElement('button');
            hasFetchTranslationButton.className = 'double-translation-plugin-has-translation-btn';
            hasFetchTranslationButton.style = 'position: relative;top: -36%; margin-right:3px; border-radius: 25px;border: none; opacity: 0.95; background-color: #fff; outline:none;';
            hasFetchTranslationButton.innerText = '正常';

            // 视频下载
            downloadVideoList = document.createElement('select');
            downloadVideoList.className = 'double-translation-plugin-download-video-list';
            downloadVideoList.style = 'position: relative;top: -36%; margin-right:3px; opacity: 0.95; display:none;';
            downloadVideoList.onchange = function()
            {
                if(this.value === 'none')
                {
                    return;
                } else
                {
                    // window.open(this.value, '_new' , 'video download');
                    var fileContent =  this.value;
                    var filename = window.ytInitialPlayerResponse.videoDetails.title + '.mp4';
                    saveDownloadVideoButton.setAttribute('download-url', fileContent);
                    saveDownloadVideoButton.setAttribute('download', filename);
                }
            }

            saveDownloadVideoButton = document.createElement('a');
            saveDownloadVideoButton.style.display = 'none';
            saveDownloadVideoButton.href = 'javascript:;';

            var _saveDownloadVideoButton  = document.createElement('button');
            _saveDownloadVideoButton.className = 'double-translation-plugin-download-en-subtitle-btn';
            _saveDownloadVideoButton.style = 'position: relative;top: -36%; margin-right:3px; border-radius: 25px;border: none; opacity: 0.95; background-color: #fff; outline:none; ';
            _saveDownloadVideoButton.innerText = '下载';
            _saveDownloadVideoButton.onclick = function()
            {
                var downUrl = this.parentElement.getAttribute('download-url');
                if(!downUrl)
                {
                    alert('请先选择需要下载的视频');
                } else
                {
                    prompt('请复制地址到迅雷进行下载', downUrl);
                }

            }
            saveDownloadVideoButton.appendChild(_saveDownloadVideoButton);

            // 刷新视频下载地址
            var refreshVideoButton = document.createElement('a');
            refreshVideoButton.href = 'javascript:;';
            var _refreshVideoButton  = document.createElement('button');
            _refreshVideoButton.className = 'double-translation-plugin-download-en-subtitle-btn';
            _refreshVideoButton.style = 'position: relative;top: -36%; margin-right:3px; border-radius: 25px;border: none; opacity: 0.95; background-color: #fff; outline:none;';
            _refreshVideoButton.innerText = '刷新';
            _refreshVideoButton.onclick = function()
            {
                HiddenDownloadVideoButton();
                GetCurrentVideoPlayerUrls();
            }
            refreshVideoButton.appendChild(_refreshVideoButton);


            // 管理面板
            var panel = document.createElement('div');
            panel.style.position = 'absolute';
            panel.style.bottom = '100%';
            panel.style.right = '0';


            panel.prepend(translationButton);
            panel.prepend(hasFetchTranslationButton);
            panel.prepend(fontResetButton);
            panel.prepend(fontAddSizeButton);
            panel.prepend(fontDecSizeButton);
            panel.prepend(fontDownButton);
            panel.prepend(fontUpButton);

            panel.prepend(saveZhhansSubtitleButton);
            panel.prepend(saveEnSubtitleButton);
            // panel.prepend(hasSubtitleLabel);

            panel.prepend(saveDownloadVideoButton);
            panel.prepend(downloadVideoList);

            panel.prepend(refreshVideoButton);
            coltrolPanel.prepend(panel);
            return true;
        }
    }

    function CalcTime(time)
    {
        var second = Math.floor(time / 1000);
        var minute = Math.floor(second / 60);
        var hour = Math.floor(minute / 60);
        minute = Math.floor(minute - hour * 60);
        second = Math.floor(second - minute * 60 - hour * 60 * 60);
        ms = Math.floor(time - (second + minute * 60 + hour * 60 * 60));

        hour = '00' + hour
        minute = '00' + minute
        second = '00' + second
        ms = ms + '000'

        hour = hour.substr(hour.length - 2, hour.length);
        minute = minute.substr(minute.length - 2, minute.length);
        second = second.substr(second.length - 2, second.length);
        ms = ms.substr(0, 3);
        return [hour, minute, second].join(':') + '.' + ms;
    }
    // 生成字幕对象
    function GenerateSRTArray(events) {
        var results_list = [];
        function AppendText(startMs, durationMs, text) {
            var last_item = null;
            if(results_list.length > 0) {
                var index = results_list.length - 1;
                last_item = results_list[index];
                if(last_item.start === startMs && last_item.end === startMs + durationMs) {
                    if(!(last_item.text === '\n' && text === '\n')) {
                        last_item.text += text;
                    }
                    results_list[index] = last_item;
                    return;
                }
            }

            last_item = {
                start: startMs,
                end: startMs + durationMs,
                text: text
            };
            results_list.push(last_item);
        }

        var rbRubys = new Map();
        for (var i = 0; i < events.length; i++) {
            var event = events[i];
            var pParentId = event.rbRuby;
            if(pParentId !== 10) {
                pParentId = 10 < pParentId ? pParentId - 1: pParentId;
                rbRubys.set(i, {rf: pParentId});
            }
        }

        for (var i = 0; i < events.length; i++) {
            var event = events[i];
            var startMs = event.tStartMs;
            var durationMs = event.dDurationMs;
            if(event.id) {
                continue;
            }
            if(event.dDurationMs === 0) {
                event.dDurationMs = 5e3;
            }
            var segs = event.segs;
            for (var l = 0; l < segs.length; l++) {
                var seg = segs[l];
                if(seg.utf8) {
                    if(seg.utf8 === '\n') {
                        AppendText(0,0, '\n');
                        continue;
                    }
                    var pPenId = seg.pPenId;
                    seg = null;
                    if(rbRubys.get(pPenId) && rbRubys.get(pPenId).rf === 1) {
                        if(l + 3 >= segs.length || !segs[l + 1].pPenId || !segs[l + 2].pPenId || !segs[l + 3].pPenId) {
                            seg = false;
                        } else {
                            var pPenId = segs[l + 1].pPenId;
                            (pPenId = rbRubys.get(pPenId)) && pPenId && 2 === pPenId.rf ? (pPenId = segs[l + 2].pPenId,
                            pPenId = rbRubys.get(pPenId),
                            !pPenId || !pPenId.rf || 3 > pPenId.rf ? seg = !1 : (pPenId = segs[l + 3].pPenId,
                                seg = (pPenId = rbRubys.get(pPenId)) && pPenId.rf && 2 === pPenId.rf ? !0 : !1)) : seg = !1
                        }
                    }
                    if(seg) {
                        AppendText(startMs, startMs + durationMs, [segs[l + 1].utf8, segs[l + 2].utf8, segs[l + 3].utf8].join(' '));
                    } else {
                        AppendText(startMs, startMs + durationMs, segs[l ].utf8);
                    }

                }
            }
        }

        function GetNext(k) {
            for (var i = k + 1; i < results_list.length; i++) {
                if(results_list[i].start !== 0 && results_list[i].end !==0 && results_list[i].text !== '\n') {
                    return results_list[i];
                }
            }
            return undefined;
        }

        function GetPrev(k) {
            for (var i = k - 1; i > 0; i--) {
                if(results_list[i].start !== 0 && results_list[i].end !==0 && results_list[i].text !== '\n') {
                    return results_list[i];
                }
            }
            return undefined;
        }

        var start = 0;
        var end = 0;
        for (var i = 0; i < results_list.length; i++) {
            if(results_list[i].start === 0 && results_list[i].end ===0 && results_list[i].text === '\n') {
                continue;
            }
            if(start === 0 && results_list[i].start !== 0) {
                start = results_list[i].start;
            }
            if(end === 0 && results_list[i].end !== 0) {
                end = results_list[i].end;
            }
            if(GetNext(i)) {
                results_list[i].end = GetNext(i).start - 1;
            } else {
                results_list[i].end = end;
            }
            start = results_list[i].start;
            end = results_list[i].end;
        }

        return results_list;
    }

    // 生成字幕文本
    function GenerateSRTString(events) {
        var res = [];
        var arr = (events);
        for(var i=0;i<arr.length;i++) {
            if(arr[i].start === 0 && arr[i].end === 0 && arr[i].text === '\n') {
                continue;
            }
            res.push((i+1).toString());
            res.push([CalcTime(parseInt(arr[i].start)), ' --> ', CalcTime(parseInt(arr[i].end))].join(''));
            res.push(arr[i].text);
            res.push('');
        }
        return res.join('\n');
    }

    // 生成中文字幕文本
    function GenerateSRTFromZhhans() {
        return GenerateSRTString(captionTranslateContent_1);
    }

    // 生成英文字幕文本
    function GenerateSRTFromEn() {
        return GenerateSRTString(captionTranslateContent_2);
    }

    function DecodeURIParamToDict(a)
    {
        a = a.split("&");
        for (var b = {}, c = 0, d = a.length; c < d; c++) {
            var e = a[c].split("=");
            if (1 == e.length && e[0] || 2 == e.length)
                try {
                    var f = decodeURIComponent(e[0] || "")
                      , h = decodeURIComponent(e[1] || "");
                    f in b ? Array.isArray(b[f]) ? vb(b[f], h) : b[f] = [b[f], h] : b[f] = h
                } catch (m) {
                    if ("q" != e[0]) {
                        var l = Error("Error decoding URL component");
                        l.params = {
                            key: e[0],
                            value: e[1]
                        };
                        throw(l)
                    }
                }
        }
        return b
    };

    // youtube视频解密
    function DecryptBySignatureCipher(s)
    {
        var Vu = {
            oG: function(a) {
                a.reverse()
            },
            Eu: function(a, b) {
                var c = a[0];
                a[0] = a[b % a.length];
                a[b % a.length] = c
            },
            zH: function(a, b) {
                a.splice(0, b)
            }
        };

        a = s.split("");
        Vu.Eu(a, 29);
        Vu.oG(a, 9);
        Vu.Eu(a, 38);
        Vu.oG(a, 66);
        return a.join("")
    }

    function Main()
    {
        FuckAds();
        // 没法hook ajax,所以只能这么这样更新字幕数据
        setInterval(AddTranslationButton, 500);
        HookDataUpdate();
        ShowCaption();
    }


    Main();

})();