🎉 欢迎访问GreasyFork.Org 镜像站!本镜像站由公众号【爱吃馍】搭建,用于分享脚本。联系邮箱📮

Greasy fork 爱吃馍镜像

🔥【就是爽】【改进版】某度网盘 破解SVIP&&倍速播放&&文稿字幕&&样式&&解锁&&修复自动开启字幕,删广告样式等bug(360+chrome已测)....看网课专用!

直接上效果图,见下面........修复自动开启字幕和导出doc等bug,取消视频控制栏的阴影等等 (20240825修正到1.3.7→1.6,修正错误:开启脚本后无法加载字幕)

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

🚀 安装遇到问题?关注公众号获取帮助

公众号二维码

扫码关注【爱吃馍】

回复【脚本】获取最新教程和防失联地址

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

🚀 安装遇到问题?关注公众号获取帮助

公众号二维码

扫码关注【爱吃馍】

回复【脚本】获取最新教程和防失联地址

// ==UserScript==
// @name       🔥【就是爽】【改进版】某度网盘 破解SVIP&&倍速播放&&文稿字幕&&样式&&解锁&&修复自动开启字幕,删广告样式等bug(360+chrome已测)....看网课专用!
// @namespace   http://tampermonkey.net/
// @match       https://pan.baidu.com/
// @match       https://pan.baidu.com/*
// @exclude     https://pan.baidu.com/aipan/search
// @grant       unsafeWindow
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_addStyle
// @run-at      document-start
// @connect     pan.baidu.com
// @require     https://lib.baomitu.com/hls.js/latest/hls.js
// @version     1.6
// @description 直接上效果图,见下面........修复自动开启字幕和导出doc等bug,取消视频控制栏的阴影等等 (20240825修正到1.3.7→1.6,修正错误:开启脚本后无法加载字幕)
// @license      none
// @author      Gwen
// ==/UserScript==

(function() {
  //GM_addStyle('#app{width:100%!important}.vp-header{min-width:0!important}.video-js .vjs-control-bar {height: auto;display: block;position: relative; top:96.5%}.vp-personal-video-play{min-width:0}.vp-personal-home-layout__video{min-width:30vw;padding-top:10px!important;height:80vh!important}.vp-personal-home-layout{padding:0 20px!important}.vp-personal-home-layout .vp-aside{padding-top:0!important}.vp-tabs{min-width:10vw!important}')

  let originalAddEventListener = EventTarget.prototype.addEventListener;
  let hookAddEventListener = function(...args) {
    if (args[0] != "keydown" && args[0] != "keyup" && args[0] != "keypress") {
    	return originalAddEventListener.apply(this, args);
    }
  }
  //EventTarget.prototype.addEventListener = hookAddEventListener; //向文山修改处 解决了快捷键失效的问题
  //document.addEventListener = hookAddEventListener; //向文山修改处 解决了快捷键失效的问题
  document.documentElement.addEventListener = hookAddEventListener;

  var settings = {
    solve_subtitle: true, //处理文稿字幕
    subtitles: null,
    subtitle_enabled: false,
    subtitleAutoEnable: GM_getValue('subtitleAutoEnable', true), //是否自动开启字幕 向文山修改处20240325 设为true和false,都一样
    longPressRate: 2, //长按加速倍速,Safari受设备影响,我的设备最高只能2倍速
    lastPlaybackRate: GM_getValue('lastPlaybackRate', 1),
    lastCurrentTime: 0,
    lastVideoWidth: GM_getValue('lastVideoWidth', null),
    lastTabWidth: GM_getValue('lastTabWidth', null),
    resolution: null,
    failCountThreshold: 15, //视频加载几秒仍未加载插件则手动发送获取视频m3u8的请求
    path: null,
    isSvip: null,
    adToken: null,
    bdstoken: null,
    globalVideo: null,
    hls: null,
    histories: GM_getValue('histories', []),
  }
  if (location.href.indexOf('https://pan.baidu.com/disk/main') != -1) {
    function wait() {
      let center = document.head && document.body && document.querySelector('.wp-s-header__center')
      if (!center) {
        setTimeout(wait, 300)
      } else {
        initPlayHistory()
        let historyBtn = document.createElement('a')
        historyBtn.href = '#'
        historyBtn.innerText = '播放历史'
        historyBtn.onclick = e => {
          e.preventDefault()
          document.querySelector('.history-wrapper').style.display='block';
          loadHistories()
        }
        center.appendChild(historyBtn)
      }
    }
    wait()
    return;
  } else if (location.href.indexOf('/pfile') == -1) {
      hookRequest()
      let localsTimer = setInterval(() => {
        if (!unsafeWindow.locals) return
        clearInterval(localsTimer)
        console.log('设置window.locas', unsafeWindow.locals)
        let originalSet = unsafeWindow.locals.set
        unsafeWindow.locals.set = function(n, t) {
          console.log('%c[hook]' + n + ': ' + t, 'color:blue;')
          if (['is_vip', 'is_svip'].indexOf(n) != -1) {
            t = 1
          } else if (n == 'vip_level') {
            t = 10
          } else if (n == 'v10_id') {
            t = '666666'
          }
          console.log(arguments)
          originalSet.apply(this, [n, t])
        }
        if (unsafeWindow.locals.userInfo) {
          unsafeWindow.locals.userInfo.vip_level = 8
          unsafeWindow.locals.userInfo.vip_identity = 21
          unsafeWindow.locals.userInfo.username = "GwenCrackヾ(-_-;)"
        } else if(unsafeWindow.locals.mset) {
          unsafeWindow.locals.mset({
            'is_vip': 1,
            'is_svip': 1,
            'vip_level': 10,
            'show_vip_ad': 0
          })
        } else {
          unsafeWindow.locals.vip_level = 8
          unsafeWindow.locals.is_vip = 1
          unsafeWindow.locals.is_svip = 1
          unsafeWindow.locals.is_evip = 0
          unsafeWindow.locals.show_vip_ad = 0
        }
      }, 10)
    return
  }

  //兼容某些浏览器无GMapi
  function GM_setValue(key, value) {
    settings[key] = value
    if (typeof value === 'string') {
      localStorage.setItem(key, value);
    } else if (typeof value === 'number' || typeof value === 'boolean') {
      localStorage.setItem(key, value.toString());
    } else {
      localStorage.setItem(key, JSON.stringify(value));
    }
  }
  function GM_getValue(key, defaultValue = null) {
    const value = localStorage.getItem(key);
    if (value === null || value === undefined) {
      return defaultValue;
    }
    try {
      return JSON.parse(value);
    } catch (error) {
      alert(`Error parsing stored value for key '${key}': ${error}`);
      return defaultValue;
    }
  }
  function throttle(fn, delay) {
    var ctx;
    var args;
    var previous = Date.now();
    var later = function () {
      fn.apply(ctx, args);
    };
    return function () {
      ctx = this;
      args = arguments;
      var now = Date.now();
      var diff = now - previous - delay;
      if (diff >= 0) {
        previous = now;
        setTimeout(later, delay);
      }
    };
  }

  var $msg = {success:console.log,error:console.log,info:console.log}
  let h0x00=setInterval(()=>{
    if(document&&document.head&&document.body) {
      clearInterval(h0x00)
      function useMessage(){function n(n){for(var o=10,e=0;e<f.length;e++){var t=f[e];if(n&&n===t)break;o+=t.clientHeight+20}return o}function o(o){for(var e=0;e<f.length;e++){if(f[e]===o){f.splice(e,1);break}}o.classList.add(a.hide),f.forEach(function(o){o.style.top=n(o)+"px"})}function e(e){function i(){p.removeEventListener("animationend",i),setTimeout(o,x||t.duration||3e3,p)}function s(){"0"===getComputedStyle(p).opacity&&(p.removeEventListener("transitionend",s),p.remove())}var d=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"info",x=arguments[2],p=r.createElement("div");p.className=a.box+" "+d,p.style.top=n()+"px",p.style.zIndex=c,p.innerHTML='\n    <span class="'+a.icon+'"></span>\n    <span class="'+a.text+'">'+e+"</span>\n    ",c++,f.push(p),r.body.appendChild(p),p.addEventListener("animationend",i),p.addEventListener("transitionend",s)}var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},r=document,i="__"+Math.random().toString(36).slice(2,7),a={box:"msg-box"+i,hide:"hide"+i,text:"msg-text"+i,icon:"msg-icon"+i},s=r.createElement("style");s.textContent=("\n  ."+a.box+", ."+a.icon+", ."+a.text+" {\n    padding: 0;\n    margin: 0;\n    box-sizing: border-box;\n  }\n  ."+a.box+" {\n    position: fixed;\n    top: 0;\n    left: 50%;\n    display: flex;\n    padding: 12px 16px;\n    border-radius: 2px;\n    background-color: #fff;\n    box-shadow: 0 3px 3px -2px rgba(0,0,0,.2),0 3px 4px 0 rgba(0,0,0,.14),0 1px 8px 0 rgba(0,0,0,.12);\n    white-space: nowrap;\n    animation: "+a.box+"-move .4s;\n    transition: .4s all;\n    transform: translate3d(-50%, 0%, 0);\n    opacity: 1;\n    overflow: hidden;\n  }\n  ."+a.box+'::after {\n    content: "";\n    position: absolute;\n    left: 0;\n    top: 0;\n    height: 100%;\n    width: 4px;\n  }\n  @keyframes '+a.box+"-move {\n    0% {\n      opacity: 0;\n      transform: translate3d(-50%, -100%, 0);\n    }\n    100% {\n      opacity: 1;\n      transform: translate3d(-50%, 0%, 0);\n    }\n  }\n  ."+a.box+"."+a.hide+" {\n    opacity: 0;\n    /* transform: translate3d(-50%, -100%, 0); */\n    transform: translate3d(-50%, -100%, 0) scale(0);\n  }\n  ."+a.icon+" {\n    display: inline-block;\n    width: 18px;\n    height: 18px;\n    border-radius: 50%;\n    overflow: hidden;\n    margin-right: 6px;\n    position: relative;\n  }\n  ."+a.text+" {\n    font-size: 14px;\n    line-height: 18px;\n    color: #555;\n  }\n  ."+a.icon+"::after,\n  ."+a.icon+'::before {\n    position: absolute;\n    content: "";\n    background-color: #fff;\n  }\n  .'+a.box+".info ."+a.icon+", ."+a.box+".info::after {\n    background-color: #1890ff;\n  }\n  ."+a.box+".success ."+a.icon+", ."+a.box+".success::after {\n    background-color: #52c41a;\n  }\n  ."+a.box+".warning ."+a.icon+", ."+a.box+".warning::after {\n    background-color: #faad14;\n  }\n  ."+a.box+".error ."+a.icon+", ."+a.box+".error::after {\n    background-color: #ff4d4f;\n  }\n  ."+a.box+".info ."+a.icon+"::after,\n  ."+a.box+".warning ."+a.icon+"::after {\n    top: 15%;\n    left: 50%;\n    margin-left: -1px;\n    width: 2px;\n    height: 2px;\n    border-radius: 50%;\n  }\n  ."+a.box+".info ."+a.icon+"::before,\n  ."+a.box+".warning ."+a.icon+"::before {\n    top: calc(15% + 4px);\n    left: 50%;\n    margin-left: -1px;\n    width: 2px;\n    height: 40%;\n  }\n  ."+a.box+".error ."+a.icon+"::after, \n  ."+a.box+".error ."+a.icon+"::before {\n    top: 20%;\n    left: 50%;\n    width: 2px;\n    height: 60%;\n    margin-left: -1px;\n    border-radius: 1px;\n  }\n  ."+a.box+".error ."+a.icon+"::after {\n    transform: rotate(-45deg);\n  }\n  ."+a.box+".error ."+a.icon+"::before {\n    transform: rotate(45deg);\n  }\n  ."+a.box+".success ."+a.icon+"::after {\n    box-sizing: content-box;\n    background-color: transparent;\n    border: 2px solid #fff;\n    border-left: 0;\n    border-top: 0;\n    height: 50%;\n    left: 35%;\n    top: 13%;\n    transform: rotate(45deg);\n    width: 20%;\n    transform-origin: center;\n  }\n  ").replace(/(\n|\t|\s)*/gi,"$1").replace(/\n|\t|\s(\{|\}|\,|\:|\;)/gi,"$1").replace(/(\{|\}|\,|\:|\;)\s/gi,"$1"),r.head.appendChild(s);var c=t.zIndex||1e4,f=[];return{show:e,info:function(n){e(n,"info")},success:function(n){e(n,"success")},warning:function(n){e(n,"warning")},error:function(n){e(n,"error")}}}
      $msg=useMessage();
      $msg.success('脚本开始运行')
    }
  },100)

  //为解决某些我无法解决的视频加载错误或手机端插件各种bug的循环检错器
  var failCount = 0
  var failChecker = null
  //手动请求视频资源
  function fetchVideoM3U8() {
    settings.lastCurrentTime = settings.globalVideo ? settings.globalVideo.currentTime : 0
    let xhr = new XMLHttpRequest()
    let url = `https://pan.baidu.com/api/streaming?app_id=250528&clienttype=0&channel=chunlei&web=1&isplayer=1&check_blue=1&type=M3U8_AUTO_${settings.resolution?settings.resolution:'480'}&trans=&vip=0` +
          `&bdstoken=${settings.bdstoken||unsafeWindow.locals.bdstoken}&path=${settings.path}&jsToken=${unsafeWindow.jsToken}`
    xhr.open("GET", url)
    xhr.manual = true
    if (settings.adToken) {
      xhr.callback = function() {
        $msg.info('开始获取m3u8')
        fetchVideoM3U8()
      }
    }
    xhr.send()
  }
  function fetchResolution() {
    let xhr = new XMLHttpRequest()
    let url = `https://pan.baidu.com/api/filemetas?clienttype=0&app_id=250528&web=1&channel=chunlei`
    let body = `dlink=1&target=${encodeURIComponent('["' + settings.path + '"]')}`
    xhr.open("POST", url)
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
    xhr.send(body)
  }
  function startFailChecker() {
    if (failChecker != null) {
      $msg.error('failChecker已在启动')
      return
    }
    failChecker = setInterval(() => {
      if (settings.globalVideo.readyState == 0) { //视频未成功加载
        failCount++
        if (failCount == settings.failCountThreshold) {
          $msg.error('视频未成功加载')
          clearInterval(failChecker)
          fetchVideoM3U8()
          failCount = 0
        }
      } else {
        $msg.success('视频成功加载')
        clearInterval(failChecker)
      }
    }, 1000)
  }

  function init() {
    let video = document.querySelector('video')
    if (!video) {
      console.log('still loading...')
      setTimeout(init, 400)
    } else if (video.id == 'vjs_video_3_html5_api') {
      video.remove()
      console.log('%removed beforeplay animation', 'color:blue')
      setTimeout(init, 400)
    } else {
      console.log('%cloaded!', 'color:red')
      $msg.success('加载成功')
      settings.globalVideo = video
      settings.path = new URLSearchParams(new URL(location.href).search).get('path');
      settings.isSvip = settings.isSvip
        || (document.querySelector('.vp-personal-userinfo__vip-icon')&&document.querySelector('.vp-personal-userinfo__vip-icon').src.indexOf('svip') != -1)
        || unsafeWindow?.locals?.is_svip
      fetchResolution()
      // if (settings.hls == null) {
      //   bindHls(video.src)
      // }

      video.parentElement.onselectstart = function(){return false;};
      video.autoplay = 'true'
      var scrubber = document.createElement("div");
      scrubber.style = 'text-align: center; width: 100%; z-index: 1000; color: white; font-weight: bold; text-shadow: black 0px 0px 10px;position: absolute;top: 50%;font-size: 30px;'
      var speedAlert = document.createElement('div')
      speedAlert.innerText = settings.longPressRate + '倍速播放中'
      speedAlert.style = 'text-align: center; width: 100%;position: absolute; top: 20px;z-index: 100; font-size: 30px;color: orange; text-shadow: black 0px 0px 20px;'
      speedAlert.style.display = 'none'
      video.parentElement.appendChild(scrubber);
      video.parentElement.appendChild(speedAlert)

      let scrubbing = false;
      let scrubStartX = 0;
      let scrubStartTime = 0;
      let deltaX = 0

      function updateScrubber() {
          var currentTime = video.currentTime;
          var duration = video.duration;
          scrubber.textContent = formatTime(currentTime) + " / " + formatTime(duration);
      }

      function formatTime(time) {
          var hours = Math.floor(time / 3600);
          var minutes = Math.floor((time % 3600) / 60);
          var seconds = Math.floor(time % 60);
          return (hours > 0 ? hours + ":" : "") + (minutes < 10 ? "0" : "") + minutes + ":" + (seconds < 10 ? "0" : "") + seconds;
      }

      let isLongPress = false
      let longPressTimer;
      let longPressThreshold = 500; // milliseconds

      video.addEventListener("touchstart", function(event) {
          if (event.touches.length == 1) {
              isLongPress = false;
              scrubbing = true;
              scrubStartX = event.touches[0].pageX;
              scrubStartTime = video.currentTime;

              longPressTimer = setInterval(function() {
                  GM_setValue('lastPlaybackRate', video.playbackRate)
                  video.playbackRate = settings.longPressRate;
                  speedAlert.style.display = 'block'
                  isLongPress = true;
                  clearInterval(longPressTimer);
              }, longPressThreshold);
          }
      });

      video.addEventListener("touchmove", function(event) {
          if (scrubbing && event.touches.length == 1 && !isLongPress) {
              deltaX = event.touches[0].pageX - scrubStartX;
              var scrubTime = scrubStartTime + deltaX * video.duration / video.clientWidth * 0.08;
              if (scrubTime < 0) {
                  scrubTime = 0;
              } else if (scrubTime > video.duration) {
                  scrubTime = video.duration;
              }
              clearInterval(longPressTimer)
              scrubber.style.display = "block";
              scrubber.textContent = formatTime(scrubTime) + " / " + formatTime(video.duration);
          }
      });

      video.addEventListener("touchend", function(event) {
          if (scrubbing && event.touches.length == 0) {
              scrubbing = false;
              if (!isLongPress) {
                deltaX = event.changedTouches[0].pageX - scrubStartX;
                if (deltaX != 0) {
                  var scrubTime = scrubStartTime + deltaX * video.duration / video.clientWidth * 0.08;
                  if (scrubTime < 0) {
                      scrubTime = 0;
                  } else if (scrubTime > video.duration) {
                      scrubTime = video.duration;
                  }
                  video.currentTime = scrubTime;
                  scrubber.style.display = "none";
                }
              }

              clearInterval(longPressTimer);
              isLongPress = false
              speedAlert.style.display = 'none'
              video.playbackRate = settings.lastPlaybackRate;
          }
      });

      video.addEventListener("touchcancel", function(event) {
          if (scrubbing && event.touches.length == 0) {
              scrubbing = false;
              scrubber.style.display = "none";

              clearInterval(longPressTimer);
              isLongPress = false
              speedAlert.style.display = 'none'
              video.playbackRate = settings.lastPlaybackRate;
          }
      });

      //播放历史模块
      initPlayHistory()
      let playHistoryButton = document.createElement('li')
      playHistoryButton.className = 'vp-menu-item'
      playHistoryButton.innerHTML = `<a class="vp-link" href="#"><span>播放历史</span></a>`
      document.querySelector('.vp-menu').appendChild(playHistoryButton)
      playHistoryButton.onclick = e => {e.preventDefault();loadHistories();document.querySelector('.history-wrapper').style.display='block';}
      historyUpdateCount = 0

      //FIX: 替换自带progressBar修复手动使用Hls播放高清视频后的点击bug
      let progressBarHtml = `<div class="vjs-progress-control vjs-control vp-video-progress-control"><div tabindex="0" class="vjs-progress-holder vjs-slider vjs-slider-horizontal" role="slider" aria-valuenow="0.00" aria-valuemin="0" aria-valuemax="100" aria-label="进度条" aria-valuetext="2:01/-:-">
<div class="vjs-play-progress vjs-slider-bar" aria-hidden="true" style="width: 0%;"><div class="vjs-time-tooltip" aria-hidden="true" style="right: -18.5px;">0:00</div></div>
</div></div>`
      let progressParent = document.querySelector('.vjs-progress-control').parentElement
      progressParent.children[0].remove()
      progressParent.insertAdjacentHTML('afterbegin', progressBarHtml)
      let progressHolder = progressParent.children[0].children[0]
      let progressBar = progressHolder.children[0]
      let progressToolTip = progressBar.children[0]
      let subtitleElement = document.querySelector('.vp-video__subtitle-text-first')
      let subtitleTab = null
      let subtitleWrapper = null
      let subtitleContent = null
      let subtitleInitTimer = setInterval(() => {
        if (!subtitleTab) {
          const elements = document.querySelectorAll('.vp-tabs__header-item');
          elements.forEach(elem => {
            if (elem.innerText.trim() == '文稿') {
              console.log('已获取到文稿元素')
              subtitleTab = elem
            }
          })
        } else {
          if (subtitleTab.className.indexOf('active')) {
            if (!subtitleWrapper) {
              subtitleWrapper = document.querySelector('.ai-draft__wrap-list')
            } else {
              console.log('%c加载文稿列表', 'color:pink;')
              subtitleContent = subtitleWrapper.parentElement
              clearInterval(subtitleInitTimer)
            }
          }
        }
      }, 400)

      progressHolder.addEventListener('click', updateProgress);
      progressHolder.addEventListener('touchstart', updateProgress);
      subtitleElement.style.fontSize = '20px' // 向文山修改处 将24改回
      // initDraftExport()

      //点击更新进度条位置
      function updateProgress(event) {
        var totalWidth = progressHolder.offsetWidth;
        var offsetX = 0;
        if (event.type === 'click') {
          offsetX = event.offsetX;
        } else if (event.type === 'touchstart') {
          offsetX = event.touches[0].clientX - progressHolder.getBoundingClientRect().left;
        }
        var percentage = (offsetX / totalWidth) * 100;
        progressBar.style.width = percentage + '%';
        let currentTime = (percentage / 100) * video.duration;
        progressToolTip.innerText = formatTime(currentTime)
        video.currentTime = currentTime;
      }
      progressHolder.addEventListener('touchmove', function(event) {
        event.preventDefault();
      });
      //寻找字幕
      function showSubtitle(subtitles, currentTime) {
        let left = 0;
        let right = subtitles.length - 1;
        while (left <= right) {
          let middle = Math.floor((left + right) / 2);
          let subtitle = subtitles[middle];
          if (currentTime >= subtitle.startTime && currentTime <= subtitle.endTime) {
            subtitleElement.innerHTML = subtitle.text;
            return;
          } else if (currentTime < subtitle.startTime) {
            right = middle - 1;
          } else {
            left = middle + 1;
          }
        }
        subtitleElement.innerHTML = '';
      }
      //video时间变化执行内容
      function videoTimeUpdate() {
        let currentTime = video.currentTime;
        // 更新自定义进度条的位置
        let percentage = (currentTime / video.duration) * 100;
        progressBar.style.width = percentage + '%';
        //如果开启了字幕,显示字幕
        if (settings.subtitle_enabled && settings.subtitles) {
          if (settings.subtitles) {
            showSubtitle(settings.subtitles, this.currentTime);
          } else {
            subtitleElement.innerText = '字幕正在加载中...'
          }
        }
        historyUpdateCount++
        if (historyUpdateCount == 25) {
          historyUpdateCount = 0
          let lastIdx = settings.path.lastIndexOf('/')
          let title = settings.path
          if (lastIdx != -1)
            title = settings.path.substring(lastIdx + 1)
          let history = {
            path: settings.path,
            title: title,
            timestamp: new Date().getTime(),
            duration: video.duration,
            current: Math.floor(currentTime)
          }
          updateHistory(history)
        }
        //破解Svip打开文稿后同步显示字幕位置
        if (!settings.isSvip && subtitleTab && subtitleTab.className.indexOf('active') != -1 && subtitleWrapper) {
          const paragraphs = subtitleWrapper.children
          let currentIndex = 0
          for (let i = 0; i < paragraphs.length; i++) {
            const paragraph = paragraphs[i]
            if (currentTime * 1000 >= paragraph.dataset.starttime) {
              currentIndex = i
            } else {
              break;
            }
          }
          //取消当前高亮
          try {
            subtitleWrapper.querySelectorAll('.ai-draft__p-sentence--fouce').forEach(node => node.className = 'ai-draft__p-sentence')
          } catch(err) {}
          const subtitles = settings.subtitles
          for (let i = currentIndex * 15; i < (currentIndex + 1) * 15 && i < settings.subtitles.length; i++) {
            if (currentTime > subtitles[i].startTime && currentTime < subtitles[i].endTime) {
              let paragraph = paragraphs[currentIndex].children[i % 15]
              paragraph.className = 'ai-draft__p-sentence ai-draft__p-sentence--fouce'
              paragraph.scrollIntoView({ behavior: "smooth", block: "start", inline: "nearest" });
              break;
            }
          }
        }
      }
      video.addEventListener('timeupdate', throttle(videoTimeUpdate, 500));

      //前进后退倍速字幕
      //FIX: 修复替换进度条后键盘控制
      let longPressRightArrowTimer = null
      document.onkeydown = function(e) {
        if (e.key === 'ArrowLeft') {
          if (video.currentTime - 15 >= 0) {
            video.currentTime -= 15;
          } else {
            video.currentTime = 0;
          }
        } else if (e.key === 'ArrowRight') {
          if (video.currentTime + 15 < video.duration) {
            video.currentTime += 15;
          } else {
            video.currentTime = video.duration;
          }
        } else if (e.code === 'Space') {
          if (video.paused) {
            video.play()
          } else {
            video.pause()
          }
        }
      };

      let toolBox = document.createElement('div')
      toolBox.style='width:100%;margin:10px 0;display:flex;justify-content:space-between;flex-flow:row wrap;'
      toolBox.id = 'toolBox'
      let reloadBtn = createButton('重新加载', e => {
        fetchVideoM3U8()
      })
      let subtitleBtn = createButton(settings.subtitle_enabled ? '关闭字幕' : '开启字幕', e => {
        if (settings.subtitle_enabled) { //已经开启了,点击关闭
          subtitleBtn.textContent = '开启字幕'
          document.querySelector('.vp-video__subtitle-text').style.display = 'none'
          GM_setValue('subtitleAutoEnable', false)
        } else { //点击开启字幕
          subtitleBtn.textContent = '关闭字幕'
          //document.querySelector('.vp-video__subtitle-text').style = 'display: block;opacity: 0.8;pointer-events:none' //这是1.37版 //向文山备注处
          setTimeout(() => { // 向文山修改处20240325 可实现凡是之前上一次设置过开启字幕,下一次加载视频按钮会显示"关闭字幕",就会自动点击字幕分栏,设置了setTimeout才会反复检查是否切换字幕分栏成功
            document.querySelector('.vp-tabs__header-item:nth-child(4)').click();
          }, 500)
            document.querySelector('.vp-video__subtitle-text').style.display = 'block'
          if (!settings.subtitles) {
            $msg.info('开始加载字幕文件')
            document.querySelector('.vp-video__subtitle-text-first').innerText = '字幕正在加载中...'
            document.querySelector('.vp-tabs__header-item:nth-child(2)').click();
          }
          if (!settings.subtitleAutoEnable && confirm('是否设置自动开启字幕?')) {
            GM_setValue('subtitleAutoEnable', true)
          }
        }
        settings.subtitle_enabled = !settings.subtitle_enabled
      })
      let rewindBtn = createButton('←15s', e => {
        if (video.currentTime - 15 >= 0) {
          video.currentTime -= 15;
        } else {
          video.currentTime = 0;
        }
      })
      let forwardBtn = createButton('15s→', e => {
        if (video.currentTime + 15 < video.duration) {
          video.currentTime += 15;
        } else {
          video.currentTime = video.duration;
        }
      })
      toolBox.appendChild(reloadBtn)
      toolBox.appendChild(subtitleBtn)
      toolBox.appendChild(rewindBtn)
      toolBox.appendChild(forwardBtn)
      let speedBox = document.createElement('select')
      speedBox.value = settings.lastPlaybackRate
      let speeds = [3, 2.25, 2, 1.75, 1.5, 1.25, 1, 0.75] //向文山修改处
      speeds.forEach(speed => {
        const option = document.createElement('option');
        option.textContent = speed == 1.5 ? '正常' : (speed + 'X'); //向文山修改处 设置option的文本值
        option.value = speed;
        speedBox.appendChild(option);
      });
      speedBox.addEventListener('change', event => {
        const selectedSpeed = event.target.value;
        settings.globalVideo.playbackRate = selectedSpeed;
        GM_setValue('lastPlaybackRate', selectedSpeed)
      });
      toolBox.appendChild(speedBox)
      let resolutionBox = document.createElement('select')
      resolutionBox.id = 'resolution-box'
      resolutionBox.addEventListener('change', event => {
        const selectedResolution = event.target.value;
        if (selectedResolution == '1080') {
          alert('1080无法播放好像。。。')
          settings.resolution = '720'
        } else {
          settings.resolution = selectedResolution;
        }
        fetchVideoM3U8()
      })
      toolBox.appendChild(resolutionBox)
      video.parentElement.parentElement.parentElement.appendChild(toolBox)
      initDraftExport()
       // 向文山修改处
       //设置字幕位置和大小
      // 设置字幕的位置(设置为相对位置,能解决全屏但是不能解决字幕栏的出现因此设置,由于是相对位置可以删除left:30%;)(设置absolute不能设置字幕位置)
      //(设置fixed或者absolute,能解决存在字幕栏的问题)(移到外面去字幕没有那么长,不过上下位置不能调节,20240122能调节上下位置)(看清来刺眼:background: #fff; color: #030b1a; opacity: 0.4;)
        //display: block会导致没有字幕时显示ai字样,  top: 94%在主屏合适,在副屏要设置为95%,height: fit-content;会导致ai字幕老是显示2行,还删除了left: 50%;  max-width: 70%; opacity: 0.8;
       var parentElement2 = document.querySelector('.vp-video__subtitle-text');
      //if (parentElement2) {
         // parentElement2.style = 'position: absolute;  font-size: 20px; padding-top:5px;  padding-bottom:25px; height: fit-content; left: 50%; top: 94%;  opacity: 0.8;';
        parentElement2.style = 'position: absolute;  top: 94%; font-size: 20px; padding-top:5px;  padding-bottom:25px;';
       //}


/* 已经转移到css中
// 隐藏控制栏(包含进度条和播放工具条)(只显示字幕清晰度 全屏三个按钮)
                        var parentElement4 = document.querySelector('.vp-video__control-bar');
                        parentElement4.style = 'position: absolute; color: brown; bottom:-15%; ';
                       var parentElement1 = document.querySelector('.vp-video .vp-video__control-bar--button.is-text');
                        parentElement1.style = 'color: blue';
                       var parentElement0 = document.querySelector('.vp-video .vp-video__control-bar--button');
                        parentElement0.style = 'color: blue';
*/

       //(颜色设置见后面)设置当前时间的在屏幕中的上下位置(不能设置; display: block)(若设置left:-1.5%,会导致全屏的时间看不清)
       var parentElement5= document.querySelector(".vp-video__control-bar--play-time-current");
       //parentElement5.style = 'position: relative;color: yellowgreen; top: 97%; opacity: 1'; //显示在视频中
      parentElement5.style = 'position: absolute; right: 50%;top: 100.4%; opacity: 1'; //显示在屏幕下方
       //(颜色设置见后面)设置视频总时间的在屏幕中的上下位置(不能设置; display: block)
       var parentElement6= document.querySelector(".vp-video__control-bar--play-time-all");
       //parentElement6.style = 'position: relative; left:93%;color: yellowgreen; top: 94.5%; opacity: 1';//显示在视频中
        parentElement6.style = 'position: relative; left:93%;color: yellowgreen; top: 100.4%; opacity: 1';//显示在屏幕下方

      // 设置进度条的颜色为没有颜色(注意style在语句中是style=background,属性中是vs下面就是background)(不能同时搜索多个class)()
      // var parentElement13 = document.querySelector('.vp-video__control-bar--play-time');
      //parentElement13.style = 'background: 0; color: yellowgreen; opacity: 0';//注释此句后可以看到蓝色进度条
      var parentElement14 = document.querySelector("#vjs_video_596 > div.vp-video__control-bar--play-time-all > div");
      parentElement14.style = 'background: 0; color: brown; font-size: 15px ; opacity: 1';
      var parentElement15 = document.querySelector("#vjs_video_596 > div.vp-video__control-bar--play-time-current > div")
      parentElement15.style = 'background: 0; color: brown; font-size: 15px; opacity: 1 ';

/* 已经写入css      //设置视频标题的位置和文本大小       //<div class="vp-toolsbar" style="">
       var parentElement16 = document.querySelector("#app > div > div.vp-personal-video-main > section > section > section > main > section > div.vp-toolsbar")
       //parentElement16.style = 'position: absolute; top: -4.6%; left: 50%; transform: translate(-50%, -20%);justify-content: center; font-size: 14px;' // 视频标题显示在顶部 //被css代替
       parentElement16.style = 'position: relative; left: 70%; transform: translate(-50%, -20%);justify-content: center; font-size: 14px;' // 视频标题显示在底部 //不用设置:视频标题本来就在底部
       var parentElement17 = document.querySelector("#app > div > div.vp-personal-video-main > section > section > section > main > section > div.vp-toolsbar > section.vp-toolsbar__title")
       parentElement17.style = 'font-size: 14px;'
*/


/* (无效)设置字幕位置
       var parentElement20 = document.querySelector("#vjs_video_596 > div.vp-video__subtitle-text")
      parentElement20.style = 'height: fit-content;left: 50%;top: 94%;max-width: 70%;opacity: 0.8;' // 视频标题显示在顶部
    //#vjs_video_596 > div.vp-video__subtitle-text{height: fit-content;left: 50%;top: 94%;max-width: 70%;opacity: 0.8;}
       var parentElement21 = document.querySelector("#vjs_video_596 > div.vp-video__subtitle-text.show")
      parentElement21.style = 'height: fit-content;left: 50%;top: 94%;max-width: 70%;opacity: 0.8;' // 视频标题显示在顶部
*/
        //移除广告
       //案例var parentElement22 = document.querySelectorAll("[class*='current' i][class*='time' i],[class*='cur' i][class*='time' i],[class*='vjs'][class*='time'], *[class*='display'], *[class*='tooltip'], *[class*='playtime'], .hv_time span:first-child");
      //var parentElement22 = document.querySelector("#app > div > div.vp-personal-video-main > section > section > section > main > section > div > section.vp-toolsbar__tools-block")
      //var parentElement22 = document.querySelectorAll("#app > div > div.vp-personal-video-main > section > section > section > main > section > div > section.vp-toolsbar__tools-block")
        var parentElement22 = [
            //用于百度网盘,不能颠倒顺序,否则,会导致分享页面的视频的当前时间读取为0:0:0
             document.querySelector("#app > div > div.vp-personal-video-main > section > section > section > main > section > div > section.vp-toolsbar__tools-block"),
            //用于百度网盘,
            document.querySelector(".vjs-time-tooltip"),
            //用于bilibili的
            document.querySelector(".bpx-player-ctrl-time-current")
        ];
      parentElement22.style = ' display: none;'

/* .vp-header{display: none !important;}没有必要(因为顶部栏取消不了)

         .vjs-progress-control{
            display: none !important;
        }
.video-js .vjs-control-bar  取消视频控制栏的阴影
vp-toolsbar__tools-block      屏蔽分享下载按钮
li.vp-menu-item:nth-of-type(1)   屏蔽标题栏中的消息按钮 //不屏蔽
.ai-course__feedback-container  课件中的反馈符号
.vp-vip-pri   屏蔽视频页中的右下角特权介绍
 .vp-personal-aside.vp-aside  屏蔽百度网盘标题栏右侧
        */




        if (settings.subtitleAutoEnable) {
        setTimeout(() => {
          subtitleBtn.click();
            (function() {
        'use strict';

        // 创建一个 MutationObserver 实例-
        const observer = new MutationObserver(function(mutationsList) {
            for (let mutation of mutationsList) {
                // 检查是否有新添加的 ai-draft__wrap-list 子元素
                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                    mutation.addedNodes.forEach(function(addedNode) {
                        // 获取 ai-draft__wrap-list 元素
                        var parentElement = document.querySelector('.ai-draft__wrap-list');
                        // 设置字幕为可编辑
                         parentElement.contentEditable = true;
/*已经移到外面去了
                    // 设置字幕的位置(设置为相对位置,能解决全屏但是不能解决字幕栏的出现因此设置,由于是相对位置可以删除left:30%;)(设置absolute不能设置字幕位置)
                         //(设置fixed或者absolute,能解决存在字幕栏的问题)(移到外面去字幕没有那么长,不过上下位置不能调节,20240122能调节上下位置)(看清来刺眼:background: #fff; color: #030b1a; opacity: 0.4;)
                     //parentElement2.style = 'position: relative; display: block;  top: 85%; font-size: 15px; opacity: 0.4;'; //这是原来的,会导致字幕栏一行铺满视频
                         var parentElement2 = document.querySelector('.vp-video__subtitle-text');
                      parentElement2.style = 'position: absolute; display: block;  top: 94%; font-size: 20px; padding-top:5px;  padding-bottom:25px';
*/
/* 已经移动到脚本开始处
                        // 设置进度条的颜色为没有颜色(注意style在语句中是style=background,属性中是vs下面就是background)(不能同时搜索多个class)(青蓝色aqua aquamarine 紫色blueviolet 棕红色brown 黄色burlywood 深绿cadetblue 墨水蓝cornflowerblue 灰色gray 淡绿yellowgreen)
                        var parentElement3 = document.querySelector('.vjs-control-bar');
                         parentElement3.style = 'background: 0;opacity: 0.5; position: relative; bottom:1%;';//注释此句后可以看到蓝色进度条


                         // 隐藏控制栏(包含进度条和播放工具条)
                        var parentElement4 = document.querySelector('.vp-video__control-bar');
                        parentElement4.style = 'position: absolute; color: brown; bottom:-15%; ';
                       var parentElement1 = document.querySelector('.vp-video .vp-video__control-bar--button.is-text');
                        parentElement1.style = 'color: blue';
                       var parentElement0 = document.querySelector('.vp-video .vp-video__control-bar--button');
                        parentElement0.style = 'color: blue';
*/


/*

                          //(不用管进度条了,调节不了算了) 显示进度条(设为relative为79%)(为了可以避免进度点动态显示设为absolute为79%,但是设置不了位置)
                        var parentElemen7 = document.querySelector("#vjs_video_596 > div.vjs-control-bar > div.vjs-control-bar > div.vjs-progress-control.vjs-control.vp-video-progress-control");
                         parentElemen7.style = 'position: relative;top:90%; opacity: 1';
                          // 显示当前进度条-显示蓝色进度
                        var parentElemen8 = document.querySelector(".vjs-play-progress.vjs-slider-bar");
                         parentElemen8.style = 'position: relative;top:90%; opacity: 1';
                        // 显示当前进度条-解锁进度条的点击功能(用了relative,进度条不能拖动)(设为relative为79%)
                        var parentElemen9 = document.querySelector(".vjs-progress-holder.vjs-slider.vjs-slider-horizontal");
                         parentElemen9.style = 'position: relative; top:90%; opacity: 1';
                        // 显示当前进度条-暂停时进度原点自动调到对应位置
                         //var parentElemen10 = document.querySelector(".vp-video-progress-control");
                        //var parentElemen10 = document.querySelector(".video-js.vjs-control-bar.vp-video-progress-control.vjs-play-progress:before");
                      // parentElemen10.style = 'position: relative; top:79%; opacity:1';
                        // 无效

                       //var parentElemen11 = document.querySelector(".vp-video-progress-control.vjs-control.vjs-progress-control");
                         //parentElemen11.style = 'position: relative; top:90%; opacity: 1';

                       //var parentElemen16 = document.querySelector(".vp-video-progress-control.vjs-control.vjs-progress-control > .vjs-slider-horizontal.vjs-slider.vjs-progress-holder > .vjs-slider-bar.vjs-play-progress");
                         //parentElemen16.style = 'position: relative; top:90%; opacity: 1';
                       //var parentElemen17 = document.querySelector(".vp-video-progress-control.vjs-control.vjs-progress-control > .vjs-slider-horizontal.vjs-slider.vjs-progress-holder");
                         //parentElemen17.style = 'position: relative; top:90%; opacity: 1';
                        //var parentElemen12 = document.querySelector(".vjs-play-progress .vjs-slider-bar");
                         //parentElemen12.style = 'position: relative; top:90%; opacity: 1';
*/



    // 获取音量控制面板元素
   var volumePane2 = document.querySelector('.vjs-volume-panel.vjs-control.vjs-volume-panel-vertical');

    // 如果找到了音量控制面板元素,则隐藏它
    if (volumePane2) {
        volumePane2.style = 'opacity: 1';
       volumePane2.style.display = 'none';
    }



     //获取视频播放器控制栏元素(查询多类,不用空格分隔类)
    //var volumePanel = document.querySelectorAll('.vjs-control-bar.video-js');

    //无效-如果找到了音量控制面板元素,则隐藏它
   // if (volumePanel) {
        // volumePanel.style = 'opacity: 0';
         //volumePanel.style.display = 'none';

   // }


                    });
                }
            }
        });

        // 在整个文档上启动 MutationObserver
        observer.observe(document.documentElement, { childList: true, subtree: true });
    })();

        }, 1500)
      }

      //FIX: 修改部分css修复宽度小时无法显示全的问题
      document.getElementById('app').style.width = '100%'
      document.querySelector('.vp-toolsbar').remove()
      document.querySelector('.vp-header').style.minWidth = '0'
      document.querySelector('.vp-personal-video-play').style.minWidth = '0'
      document.querySelector('.vp-personal-home-layout__video').style.minWidth = '30vw'
      document.querySelector('.vp-personal-home-layout__video').style.paddingTop = '10px'
      document.querySelector('.vp-personal-home-layout__video').style.height = '80vh'
      document.querySelector('.vp-personal-home-layout').style.padding = '0 20px'
      document.querySelector('.vp-personal-home-layout .vp-aside').style.paddingTop = '0'
      document.querySelector('.vp-tabs').style.minWidth = '10vw'
      if (settings.lastTabWidth) {
        document.querySelector('.vp-personal-home-layout .vp-aside').style.width = settings.lastTabWidth
      }
      function addSplitScreenAdjustment(element1Selector, element2Selector) {
        const element1 = document.querySelector(element1Selector);
        const element2 = document.querySelector(element2Selector);
        let scriptChanging = false;
        const handle = document.createElement('div');
        handle.style.width = '15px';
        handle.style.cursor = 'ew-resize';
        handle.style.backgroundColor = '#ccc';
        handle.style.margin = '0 5px';
        let startX, startWidth1, startWidth2;
        const handleMouseDown = (event) => {
          startX = event.clientX;
          startWidth1 = element1.offsetWidth;
          startWidth2 = element2.offsetWidth;
          scriptChanging = true
          document.addEventListener('mousemove', handleMouseMove);
          document.addEventListener('mouseup', handleMouseUp);
        };
        const handleMouseMove = throttle((event) => {
          const dx = event.clientX - startX;
          const newWidth1 = startWidth1 + dx;
          const newWidth2 = startWidth2 - dx;
          element1.style.width = newWidth1 + 'px';
          element2.style.width = newWidth2 + 'px';
        }, 100);
        const handleMouseUp = () => {
          document.removeEventListener('mousemove', handleMouseMove);
          document.removeEventListener('mouseup', handleMouseUp);
          scriptChanging = false
          GM_setValue('lastTabWidth', '"' + element2.style.width + '"')
        };
        handle.addEventListener('mousedown', handleMouseDown);
        const handleTouchStart = (event) => {
          startX = event.touches[0].clientX;
          startWidth1 = element1.offsetWidth;
          startWidth2 = element2.offsetWidth;
          scriptChanging = true
          document.addEventListener('touchmove', handleTouchMove);
          document.addEventListener('touchend', handleTouchEnd);
        };
        const handleTouchMove = throttle((event) => {
          const dx = event.touches[0].clientX - startX;
          const newWidth1 = startWidth1 + dx;
          const newWidth2 = startWidth2 - dx;
          element1.style.width = newWidth1 + 'px';
          element2.style.width = newWidth2 + 'px';
        }, 100);
        const handleTouchEnd = () => {
          document.removeEventListener('touchmove', handleTouchMove);
          document.removeEventListener('touchend', handleTouchEnd);
          scriptChanging = false
          GM_setValue('lastTabWidth', '"' + element2.style.width + '"')
        };
        handle.addEventListener('touchstart', handleTouchStart);
        element2.parentNode.insertBefore(handle, element2);
        //防止百度网盘自动调整他的宽度
        var observer = new MutationObserver(function(mutations) {
          mutations.forEach(function(mutation) {
            const oldStyle = mutation.oldValue;
            if (!scriptChanging) {
              observer.disconnect()
              setTimeout(() => {
                element2.style = oldStyle;
                observer.observe(element2, {attributes: true, attributeOldValue: true})
              }, 50)
            }
          });
        });
        observer.observe(element2, { attributes: true, attributeOldValue: true });
      }
      addSplitScreenAdjustment('main', '.vp-personal-home-layout .vp-aside')

      let rateOptions = document.querySelector('.vp-video__control-bar--playback-rates').children
      for (let i = 0; i < rateOptions.length; i++) {
        let option = rateOptions[i]
        if (option.classList[1] != 'is-svip-guide') {
          option.onclick = function(e) {
            e.stopPropagation()
            GM_setValue('lastPlaybackRate', Number.parseFloat(option.innerText.replace('X', '')))
            video.playbackRate = settings.lastPlaybackRate
          }
        }
      }

      //双击暂停
      let container = video.parentElement;
      let pauseThreshold = 500; // milliseconds
      let lastClickTime = 0;
      let pauseTimer;
      container.addEventListener("touchstart", function(event) {
        let currentTime = new Date().getTime();
        if (currentTime - lastClickTime < pauseThreshold) {
          clearTimeout(pauseTimer);
          if (video.paused) {
            video.play();
          } else {
            video.pause();
          }
        } else {
          lastClickTime = currentTime;
          pauseTimer = setTimeout(function() {
            lastClickTime = 0;
          }, pauseThreshold);
        }
      });

      //FIX: 替换自带全屏修复Focus浏览器全屏使用iOS默认播放器
      let vpVideo = document.querySelector('.vp-video')
      let controllBar = document.querySelector('.vp-video__control-bar--setup')
      controllBar.children[4].remove()
      controllBar.insertAdjacentHTML('beforeend', `<div class="vp-video__control-bar--button-group">
  <div class="vp-video__control-bar--button is-icon">
    <i class="u-icon-screen"></i></div></div>`)
      let fullScreenBtn = controllBar.children[4]
      let fullScreenIcon = fullScreenBtn.children[0].children[0]
      fullScreenBtn.onclick = e => {
        if (fullScreenIcon.className == 'u-icon-screen') {
          //点击全屏
          vpVideo.style.zIndex = '99999'
          vpVideo.style.position = 'fixed';
          vpVideo.style.top = '0';
          vpVideo.style.left = '0';
          fullScreenIcon.className = 'u-icon-exit-screen'
        } else {
          vpVideo.style.position = 'unset';
          fullScreenIcon.className = 'u-icon-screen'
        }
      }

      //准备完毕,开始检查视频状态
      startFailChecker()
    }
  }

  function createButton(textContent, callback) {
    let btn = document.createElement('div')
    btn.style = 'padding: 10px 10px; text-align: center; background: rgb(6, 167, 255); color: white;font-size: 14px;cursor:pointer'
    btn.textContent = textContent
    if (callback) {
      btn.onclick = e => {
        callback(e)
      }
    }
    return btn;
  }

  function formatPlayTime(timestamp) {
      let current = new Date().getTime()
      let gap = Math.floor((current - timestamp) / 1000)
      if (gap < 60) {
          return `${gap}秒前`
      } else if (gap < 60 * 60) {
          return `${Math.floor(gap/60)}分钟前`
      } else if (gap < 60 * 60 * 24) {
          return `${Math.floor(gap/60/60)}小时前`
      } else {
          return new Date(timestamp).toLocaleString()
      }
  }
  function initPlayHistory() {
      let style = document.createElement('style')
      style.textContent = '.history-wrapper{display:none;background-color:#fefefe;position:fixed;z-index:99999;top:40%;left:50%;transform:translate(-50%,-50%);padding:5px 10px 10px;border:1px solid #888;width:420px;}.history-list::-webkit-scrollbar{width:4px;}.history-list::-webkit-scrollbar-track{background-color:#f1f1f1;}.history-list::-webkit-scrollbar-thumb{background-color:#888888;}.history-list{min-height:100px;max-height:300px;overflow-y:auto;scrollbar-width:thin;}.history{padding:8px 4px;transition:.2s;cursor:pointer;border-bottom:1px dashed lightgray;}.history:hover{background-color:rgb(240,240,240);}.history-time{color:rgb(100,100,100);font-size:small;}.history-title{font-size:16px;}.history-progress-bar{width:280px;height:6px;background-color:#f1f1f1;border-radius:10px;overflow:hidden;display:inline-block;}.history-progress{width:0%;height:100%;background-color:#007bff;transition:width 0.3s ease-in-out;}.close{color:#aaa;float:right;font-size:28px;font-weight:bold;}.close:hover,.close:focus{color:black;text-decoration:none;cursor:pointer;}.history-end{color:rgb(150,150,150);text-align:center;font-size:small;}';
      document.head.append(style)
      let historyWrapper = document.createElement('div')
      historyWrapper.className = 'history-wrapper'
      historyWrapper.innerHTML = `<span style="float:left;margin-top: 5px;font-weight: bold;">播放历史(保留20条)</span>
          <span class="close" onclick="closeModal()">&times;</span>
          <div style="clear:both;"></div>
          <div class="history-list"></div>`
      historyWrapper.querySelector('.close').onclick = function(e) {
          historyWrapper.style.display = 'none'
      }
      document.body.append(historyWrapper)
      var existingHistoryIndex = settings.histories.findIndex(function(item) {
        return item.path === settings.path;
      });
      if (existingHistoryIndex != -1 && settings.globalVideo) {
        settings.globalVideo.currentTime = settings.histories[existingHistoryIndex].current
      }
      loadHistories()
  }
  function loadHistories() {
      let historyList = document.querySelector('.history-list')
      historyList.innerHTML = ''
      for (let i = settings.histories.length - 1; i >= 0; i--) {
        let history = settings.histories[i]
        let historyElem = document.createElement('div')
        historyElem.className = 'history'
        historyList.append(historyElem)
        historyElem.innerHTML = `<div class="history-time">${formatPlayTime(history.timestamp)}</div>
          <div class="history-title">${history.title}</div>
          <div class="history-progress-bar">
            <div class="history-progress" style="width:${100*history.current/history.duration}%"></div>
          </div>
          <span style="color:rgb(100,100,100);font-size:12px;">观看至${formatTime(history.current)}</span>`
        historyElem.setAttribute('path', history.path)
      }
      historyList.onclick = function(e) {
        const elem = event.target.closest('.history');
        if (elem) {
          const path = elem.getAttribute('path')
          if (confirm('跳转视频' + path + '?')) {
            location.href = 'https://pan.baidu.com/pfile/video?path=' + encodeURIComponent(path)
          }
        }
      }
      historyList.innerHTML += `<div class="history-end">—————— 已经到底了哦 ——————</div>`
  }
  function updateHistory(history) {
    const histories = settings.histories;
    var existingHistoryIndex = histories.findIndex(function(item) {
      return item.path === history.path;
    });
    if (existingHistoryIndex !== -1) {
      histories.splice(existingHistoryIndex, 1)[0];
      histories.push(history);
    } else {
      histories.push(history);
      if (histories.length > 20) {
        histories.splice(0, 1)
      }
    }
    GM_setValue('histories', histories);
  }

  function hookRequest() {
      var originOpen = XMLHttpRequest.prototype.open;
      XMLHttpRequest.prototype.open = function (method, url) {
        if (url.indexOf('netdisk-subtitle') != -1 && settings.solve_subtitle) { //获取字幕信息
          this.addEventListener('readystatechange', function() {
            if (this.readyState == 4) {
              var blobData = this.response;
              var reader = new FileReader();
              reader.onloadend = function() {
                var textContent = reader.result;
                let arr = textContent.split('\n')
                setTimeout(() => {
                  let wrapper = document.querySelector('.ai-draft__wrap-list')
                  try {
                    document.querySelector('.ai-draft__wrap-content').style = 'padding-right:12px!important;'
                    document.querySelector('.ai-draft__wrap-content').contentEditable = true //向文山修改处-在1.35及之前需要注销此句,否则会导致视频倍数插件的c键失效,现在复制文本,可以复制到此处,也可以复制到计时器中
                    document.querySelector('.ai-draft__filter').remove()
                    document.querySelector('.ai-draft__svip-guide').remove()
                  } catch(err) {
                  }
                  let subtitles = []
                  let paragraph
                  console.log('开始解析字幕')
                  for (let i = 1; i < arr.length / 4; i++) {
                    if (arr[i * 4] == '')
                      break;
                    let lines = [arr[i * 4], arr[i * 4 + 1], arr[i * 4 + 2]]
                    let timeParts = lines[1].split(' --> ');
                    let startTime = srtTimeToSeconds(timeParts[0].trim());
                    let endTime = srtTimeToSeconds(timeParts[1].trim());
                    if (!settings.isSvip && i >= 15 * 3 + 1) {
                      if (i % 15 == 1) {
                        paragraph = document.createElement('p')
                        paragraph.className = 'ai-draft__p-paragraph'
                        paragraph.setAttribute('data-starttime', startTime * 1000)
                      } else if (i % 15 == 0) {
                        wrapper.appendChild(paragraph)
                      }
                      let span = document.createElement('span')
                      span.className = 'ai-draft__p-sentence'
                      span.setAttribute('data-index', i - 1)
                      span.innerText = lines[2]
                      span.onclick = e => {
                        settings.globalVideo.currentTime = startTime
                      }
                      paragraph.appendChild(span)
                    }
                    subtitles.push({
                      index: i - 1,
                      startTime: startTime,
                      endTime: endTime,
                      text: lines[2]
                    })
                  }
                  settings.subtitles = subtitles
                }, 1000)
              };
              reader.readAsText(blobData);
            }
          });
          originOpen.apply(this, arguments);
        } else if (url.indexOf('/api/loginStatus') != -1) { //伪造svip信息
          this.addEventListener('readystatechange', function() {
            if (this.readyState == 4) {
              let res = JSON.parse(this.responseText)
              settings.isSvip = res.login_info.vip_type == '21'
              res.login_info.vip_type = '21'
              res.login_info.vip_identity = '21'
              res.login_info.vip_level =  8
              res.login_info.vip_point = 99999
              res.login_info.username = 'sout(\'GwenCrack\')'
              settings.bdstoken = res.login_info.bdstoken
              Object.defineProperty(this, "responseText", {
                  writable: true,
              });
              this.responseText = JSON.stringify(res)
            }
          })
          originOpen.apply(this, arguments);
        } else if (url.indexOf('/user/info') != -1) {
          this.addEventListener('readystatechange', function() {
            if (this.readyState == 4) {
              let res = JSON.parse(this.responseText)
              res.user_info.is_vip = 1
              res.user_info.is_svip = 1
              res.user_info.is_plus_buy =	1
              Object.defineProperty(this, "responseText", {
                  writable: true,
              });
              this.responseText = JSON.stringify(res)
            }
          })
          originOpen.apply(this, arguments);
        } else if (url.indexOf('/membership/user') != -1) {
          this.addEventListener('readystatechange', function() {
            if (this.readyState == 4) {
              let res = JSON.parse(this.responseText)
              res.reminder = {
                "svip": {
                  "leftseconds": 9999999999,
                  "nextState": "normal"
                }
              }
              res.level_info = {
                "current_value": 12090,
                "current_level": 10,
                "history_value": 11830,
                "history_level": 10,
                "v10_id": "666666",
                "last_manual_collection_time": 0
              }
              res.product_infos = [{
                "product_id": "",
                "start_time": 1685635199,
                "end_time": 1888227199,
                "buy_time": 0,
                "cluster": "vip",
                "detail_cluster": "svip",
                "auto_upgrade_to_svip": 0,
                "product_name": "svip2_nd",
                "status": 0,
                "function_num": 0,
                "buy_description": "",
                "product_description": "",
                "cur_svip_type": "month"
              }]
              res.current_product = {
                "cluster": "vip",
                "detail_cluster": "svip",
                "product_type": "vip2_1m_auto",
                "product_id": "12187135090581539740"
              }
              res.current_product_v2 = {
                "cluster": "vip",
                "detail_cluster": "svip",
                "product_type": "vip2_1m_auto",
                "product_id": "12187135090581539740"
              }
              Object.defineProperty(this, "responseText", {
                  writable: true,
              });
              this.responseText = JSON.stringify(res)
            }
          })
          originOpen.apply(this, arguments);
        } else if (url.indexOf('/api/streaming') != -1 && url.indexOf('M3U8_SUBTITLE_SRT') == -1) { //获取视频m3u8接口
          let modifiedUrl = url.replace(/vip=2/, 'vip=0')
                  // .replace(/M3U8_.*?&/, 'M3U8_AUTO_1080&')
          if (settings.adToken) {
            modifiedUrl += ('&adToken=' + encodeURIComponent(settings.adToken))
            settings.adToken = null
          }
          originOpen.call(this, method, modifiedUrl, arguments[2]);
          this.addEventListener('readystatechange', function() {
            if (this.readyState == 4) {
              if (this.responseText[0] == '{') {
                let res = JSON.parse(this.responseText)
                settings.adToken = res.adToken
                res.ltime = 0.001
                res.adTime = 0.001
                Object.defineProperty(this, "responseText", {
                  writable: true,
                });
                this.responseText = JSON.stringify(res)
                if (settings.isSvip) {
                  settings.lastCurrentTime = settings.globalVideo ? settings.globalVideo.currentTime : 0
                  let xhr = new XMLHttpRequest()
                  let url = `https://pan.baidu.com/api/streaming?app_id=250528&clienttype=0&channel=chunlei&web=1&isplayer=1&check_blue=1&type=M3U8_AUTO_${settings.resolution?settings.resolution:'480'}&trans=&vip=0` +
                        `&bdstoken=${settings.bdstoken||unsafeWindow.locals.bdstoken}&path=${settings.path}&jsToken=${unsafeWindow.jsToken}`
                  xhr.open("GET", url, false)
                  xhr.send()
                  this.responseText = xhr.responseText
                } else if (this.callback) {
                  this.callback()
                }
              } else {
                let m3u8Content = this.responseText
                let blob = new Blob([this.responseText], { type: 'application/vnd.apple.mpegurl' });
                let url = URL.createObjectURL(blob);
                bindHls(url)
              }
            }
          })
        } else if (url.indexOf('/api/filemetas') != -1) {
          this.addEventListener('readystatechange', function() {
            if (this.readyState == 4) {
              let res = JSON.parse(this.responseText)
              if (res.info.length != 1)
                return
              let resolution = res.info[0].resolution
              console.log('分辨率'+ resolution)
              let resolutionOptions = []
              let match = false
              switch(resolution) {
                case 'width:1920,height:1080':
                  match = true
                  resolutionOptions.push('1080')
                case 'width:1280,height:720':
                  match = true
                  resolutionOptions.push('720')
                case 'width:720,height:480':
                  match = true
                  resolutionOptions.push('480')
                default:
                  resolutionOptions.push('360');
              }
              if (!match) {
                resolutionOptions = ['720', '480', '360']
              }
              console.log(resolutionOptions)
              let waitTimer = setInterval(() => {
                let box = document.getElementById('resolution-box')
                if (box) {
                  clearInterval(waitTimer)
                  box.innerHTML = ''
                  resolutionOptions.forEach(resolution => {
                    const option = document.createElement('option')
                    option.textContent = resolution + 'P'
                    option.value = resolution
                    box.appendChild(option)
                  })
                }
              }, 400)
            }
          })
          originOpen.apply(this, arguments);
        } else if (url.indexOf('/msg/streaming') != -1 || url.indexOf('/share/streaming') != -1) {
          this.addEventListener('readystatechange', function() {
            if (this.readyState == 4) {
              if (this.responseText[0] != '{')
                return
              let res = JSON.parse(this.responseText)
              res.ltime = 0.000001
              res.adTime = 0.000001
              Object.defineProperty(this, 'responseText', {
                writable: true,
              })
              this.responseText = JSON.stringify(res)
            }
          })
          originOpen.apply(this, arguments);
        } else {
          originOpen.apply(this, arguments);
        }
      }
    }

  function bindHls(url) {
    if (!Hls.isSupported()) {
      $msg.error('浏览器不支持播放')
      return
    }
    if (settings.hls) {
      try {
        settings.hls.destroy()
        settings.hls = null
      } catch(err) {
        console.error(err)
      }
    }
    settings.hls = new Hls({
      autoStartLoad: true,
      autoplay: true
    })
    let hls = settings.hls
    let video = settings.globalVideo
    let vpError = document.querySelector('.vp-error')
    if (vpError) {
      vpError.remove()
    }
    hls.on(Hls.Events.MEDIA_ATTACHED, function() {
      hls.loadSource(url);
    });
    hls.on(Hls.Events.MANIFEST_PARSED, function(event, data) {
      console.log('上次加载到' + settings.lastCurrentTime)
      video.currentTime = settings.lastCurrentTime
      video.play();
      video.playbackRate = settings.lastPlaybackRate
      let checkDurationTimer = setInterval(() => {
        if (video.readyState > 0) {
          document.querySelector('.vp-video__control-bar--play-time-all>div').innerText = formatTime(video.duration)
          clearInterval(checkDurationTimer)
        }
      }, 100)
    });
    hls.attachMedia(video);
  }
  function srtTimeToSeconds(timeString) {
    var timeParts = timeString.split(':');
    var hours = parseInt(timeParts[0]);
    var minutes = parseInt(timeParts[1]);
    var secondsAndMilliseconds = timeParts[2].split('.');
    var seconds = parseInt(secondsAndMilliseconds[0]);
    var milliseconds = parseInt(secondsAndMilliseconds[1]);
    var totalSeconds = (hours * 3600) + (minutes * 60) + seconds + (milliseconds / 1000);
    return totalSeconds;
  }

  function formatTime(totalSeconds, requireMil = false) {
    var hours = Math.floor(totalSeconds / 3600);
    var minutes = Math.floor((totalSeconds % 3600) / 60);
    var seconds = Math.floor(totalSeconds % 60);
    var formattedTime = hours.toString().padStart(2, '0') + ':' +
                        minutes.toString().padStart(2, '0') + ':' +
                        seconds.toString().padStart(2, '0');
    if (requireMil) {
      formattedTime += ',' + (seconds % 1).toFixed(3).substring(2)
    }
    return formattedTime;
  }
  function copyToClipboard(txt){
    if (navigator.clipboard?.writeText)
      navigator.clipboard.writeText(txt)
    else {
      input = document.createElement('textarea')
      input.setAttribute('readonly', 'readonly')
      input.value = txt
      document.body.appendChild(input)
      input.select()
      if (document.execCommand('copy'))
        document.execCommand('copy')
      document.body.removeChild(input)
    }
  }
  function initDraftExport() {
    function getDefaultFilename(ext) {
      var videoNameElement = document.querySelector('div.vp-video-page-card span.is-playing.vp-video-page-card__video-name');
      if (videoNameElement) {
        var originalFilename = videoNameElement.innerText.trim();
        var newFilename = originalFilename.replace(/\.[^/.]+$/, '') + ext; // 去掉原始文件名的后缀,并添加新的后缀名
        return newFilename;
      }
      return 'subtitle' + ext;
    }
    let toolBox = document.getElementById('toolBox')
    // 创建复制字幕按钮
    function createDraftButton(id, textContent, left, callback) {
      const btn = document.createElement('button');
      btn.id = id;
      btn.innerText = textContent;
      // btn.style = `position:fixed;left:${left};bottom:3px;z-index:9999;padding:10px;background:#fff;border:1px solid #ccc;cursor:pointer;`
      btn.style = 'padding: 3px 10px;font-size: 14px;background:#fff;border:1px solid #ccc;cursor:pointer;'
      // 复制字幕按钮点击事件处理函数
      btn.addEventListener('click', function() {
        if (!settings.subtitles) {
          $msg.info('视频文稿未加载,开始加载...')
          document.querySelector('.vp-tabs__header-item:nth-child(4)').click(); //向文山修改处 4表示字幕分栏在右侧导航栏中的位置,ps:要全部搜索修改.vp-tabs__header-item:nth-child(2)和(1),共修改5次
          setTimeout(() => {
            document.querySelector('.vp-tabs__header-item:nth-child(1)').click();
          }, 500)
          return
        }
        callback()
      });
      toolBox.append(btn)
    }
    createDraftButton('copySubtitleBtn', '复制字幕', '40px', function() {
      const subtitleElements = document.querySelectorAll('.ai-draft__wrap-list p.ai-draft__p-paragraph'); // 获取所有段落元素
      const subtitleText = [];
      for (let i = 0; i < subtitleElements.length; i++) {
        subtitleText.push(subtitleElements[i].innerText.trim()); // 将每个段落的文本添加到字幕数组中
      }
      copyToClipboard(subtitleText.join('\n\n')); // 将字幕数组以空行连接起来并返回
      $msg.success('字幕已复制')
    })
    createDraftButton('exportToDocBtn', '导出文稿doc', '120px', function() {
      $msg.info('正在导出文稿...')
      const subtitleElements = document.querySelectorAll('.ai-draft__wrap-list p.ai-draft__p-paragraph'); // 获取所有段落元素
      const subtitleText = [];
      for (let i = 0; i < subtitleElements.length; i++) {
        subtitleText.push(subtitleElements[i].innerText.trim()); // 将每个段落的文本添加到字幕数组中
      }
      const subtitle = subtitleText.join('\n\n'); // 获取字幕内容
      const filename = getDefaultFilename('.doc');
      var blob = new Blob([subtitle], {type: 'text/plain;charset=utf-8'});
      var downloadLink = document.createElement("a");
      downloadLink.href = URL.createObjectURL(blob);
      downloadLink.download = filename;
      document.body.appendChild(downloadLink);
      downloadLink.click();
      document.body.removeChild(downloadLink);
      URL.revokeObjectURL(downloadLink.href);
      //saveAs(blob, filename); // 使用FileSaver.js保存文件
      $msg.success('导出成功')
    })
    createDraftButton('exportToSrtBtn', '导出字幕srt', '220px', function() {
      $msg.info('正在导出文稿...')
      const blobArray = [];
      settings.subtitles.forEach(function(subtitle) {
        const srtText =
          (subtitle.index + 1) + "\n" +
          formatTime(subtitle.startTime, true) + " --> " + formatTime(subtitle.endTime, true) + "\n" +
          subtitle.text + "\n\n";
        console.log(srtText)
        const srtBlob = new Blob([srtText], { type: "text/plain;charset=utf-8" });
        blobArray.push(srtBlob);
      });
      var combinedBlob = new Blob(blobArray, { type: "text/plain;charset=utf-8" });
      var downloadLink = document.createElement("a");
      downloadLink.href = URL.createObjectURL(combinedBlob);
      downloadLink.download = getDefaultFilename('.srt');
      document.body.appendChild(downloadLink);
      downloadLink.click();
      document.body.removeChild(downloadLink);
      URL.revokeObjectURL(downloadLink.href);
      //saveAs(combinedBlob, getDefaultFilename('.srt'));
      $msg.success('导出成功')
    })
  }

  hookRequest()
  init()
  let localsTimer = setInterval(() => {
    if (!unsafeWindow.locals) return
    clearInterval(localsTimer)
    console.log('设置window.locas', unsafeWindow.locals)
    if (unsafeWindow.locals.userInfo) {
      unsafeWindow.locals.userInfo.vip_level = 8
      unsafeWindow.locals.userInfo.vip_identity = 21
      unsafeWindow.locals.userInfo.username = "GwenCrackヾ(-_-;)"
    } else if(unsafeWindow.locals.mset) {
      unsafeWindow.locals.mset({
        'is_vip': 1,
        'is_svip': 1,
        'vip_level': 8,
        'show_vip_ad': 0
      })
    } else {
      unsafeWindow.locals.vip_level = 8
      unsafeWindow.locals.is_vip = 1
      unsafeWindow.locals.is_svip = 1
      unsafeWindow.locals.is_evip = 0
      unsafeWindow.locals.show_vip_ad = 0
    }
  }, 100)
  let lastUrl = location.href
  setInterval(() => {
    if (lastUrl != location.href) {
      lastUrl = location.href
      console.log('%cURL变化为' + location.href, 'color:purple;')
      settings.path = new URLSearchParams(new URL(lastUrl).search).get('path');
      setTimeout(() => {
        $msg.info('重新加载字幕')
        settings.subtitles = null
        document.querySelector('.vp-tabs__header-item:nth-child(2)').click();
        setTimeout(() => {
          document.querySelector('.vp-tabs__header-item:nth-child(1)').click();
        }, 500)
      }, 2500)

    }
  }, 500)

})()