Greasy Fork

Greasy Fork is available in English.

我只想好好观影

为了想看就看

当前为 2023-08-26 提交的版本,查看 最新版本

在您安装前,Greasy Fork 希望您知道此脚本声明其包含了一些负面功能。这些功能也许会使脚本作者获利,而不能给您带来任何直接的金钱收益。

此脚本含有追踪您的操作的代码。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        我只想好好观影
// @namespace   liuser.betterworld.love
// @match       https://movie.douban.com/subject/*
// @match       https://m.douban.com/movie/*
// @grant       GM_addStyle
// @grant       GM_xmlhttpRequest
// @grant       GM_registerMenuCommand
// @grant       GM_setValue
// @grant       GM_getValue
// @antifeature tracking
// @connect     *
// @run-at      document-end
// @require     https://cdn.jsdelivr.net/npm/[email protected]/dist/artplayer.js
// @require     https://cdn.jsdelivr.net/npm/[email protected]/dist/artplayer-plugin-control.js
// @require     https://cdn.staticfile.org/hls.js/1.4.3/hls.min.js
// @require     https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js
// @version     3.2
// @author      liuser, collaborated with ray
// @description 为了想看就看
// @license MIT
// ==/UserScript==

//按钮样式
GM_addStyle(
  `
  .liu-btn{
    cursor:pointer;
    font-size:1rem;
    padding: 0.6rem 1.2rem;
    border: 1px solid transparent;
    border-radius: 3px;
  }

  .play-btn {
    border-radius: 8px;
    cursor: pointer;
    font-weight: bolder;
    background-color:#e8f5e9;
  }
  .play-btn:hover {
    background-color:#c8e6c9;
  }
  .play-btn:active{
    background-color: #81c784;
  }

  .source-selector{
    background-color: #141414;
    color: #99a2aa;
    padding:0.6rem 0.8rem;
    margin:0.5rem 0.875rem;
    border-radius:4px;
  }
  .source-selector:hover{
    background-color: #1f1f1f;
  }

  .series-selector{
    background-color: #141414;
    border-radius:3px;
    color: #99a2aa;
    width:3.5rem;
    height:3.5rem;
    font-size:0.9rem;
    padding:0;
  }
  .series-selector:hover{
    background-color: #153a1d;
  }

  .playing{
    border:2px solid #007011;
  }

  .selected{
    border:2px solid #007011;
  }


  .liu-closePlayer{
    border-radius:3px;
    background-color: #141414;
      float:right;
    color: #99a2aa;
    width:2rem;
    height:2rem;
    line-height:2rem;
    padding:0;
    margin:0.5rem 1rem;
  }
  .liu-closePlayer:hover{
    background-color:#1f1f1f;
    color:white;
  }

  .love-support{
    background-color:tranparent;
    margin-right:1rem;
  }
  a:hover{
    font-weight:bold;
    background:none;
  }


  `
);

//剧集选择器布局
GM_addStyle(
  `
  .series-contianer{
    display:grid;
    grid-template-columns: repeat(5,1fr);
    grid-column-gap:0.5rem;
    grid-row-gap:0.5rem;
    margin-top:1rem;
  }
  @media screen and (max-width: 1025px) {
  .series-contianer{
    display:grid;
    grid-template-columns: repeat(5,1fr);
    grid-column-gap:0.5rem;
    grid-row-gap:0.5rem;
    margin-top:1rem;
  }

  }


  `
);

//布局
GM_addStyle(
  `

  :root{
    font-size:16px;
    font-family: BlinkMacSystemFont,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue",sans-serif !important;
  }
  :root::-webkit-scrollbar {
    display: none;
  }

  .TalionNav{
      z-index:10;
  }
  .speed-slow{
      color:#9e9e9e;
  }
  .speed-fast{
      color:#4aa150;
  }



  .mannul{
    margin-top:2rem;
    margin-bottom:3rem;
    font-size:1rem;
    display:flex;
    flex-wrap:wrap;
  }



  .liu-playContainer{
      width:100%;
      height:100%;
      background-color:#1c2022;
      position:fixed;
      top:0;
      z-index:11;
      overflow:auto;
  }
  .liu-playContainer::-webkit-scrollbar {
    display: none;
  }



  .video-selector{
      display:flex;
      flex-wrap:wrap;
      margin-top:1rem;
  }

  .liu-selector:hover{
      color:#aed0ee;
      background-color:none;
  }

  .liu-selector{
      color:black;
      cursor:pointer;
      padding:3px;
      margin:5px;
      border-radius:2px;
  }

  .liu-rapidPlay{
      color: #007722;
  }

  .liu-light{
      background-color:#7bed9f;
  }

  .artplayer-app{
    height:600px;
  }


  .playSpace{
      display: grid;
  /* 	height:400px; */
    margin:1rem;
      grid-template-columns: 2fr 1fr;
      grid-row-gap:0px;
      grid-column-gap:1rem;
    margin-top:2rem;
    clear: both;
  }



  @media screen and (max-width: 1025px) {
      .playSpace{
          display: grid;
  /* 		height:600px; */
          grid-template-rows: 1fr 0.5fr;
          grid-template-columns:1fr;
          grid-row-gap:10px;
          grid-column-gap:0px;
      }
  }


  .seletor-title{
    height:3rem;
    line-height:3rem;
    background-color: #141414;
    color:#fafafa;
    font-size:1.25rem;
    padding: 0 1rem;
  }
  `
);

// 上传源地址
const sourceupload = () => {
  let sourceAdded = prompt(
    "请输入自定义源,名称与链接用|隔开,每一项用英文逗号隔开"
  );
  GM_setValue("sourceAdded", sourceAdded);
};
// 加入额外的源
GM_registerMenuCommand("自定义源", sourceupload);

(function () {
  const _debug = 0;
  let seriesNum = 0;
  const { query: $, queryAll: $$, isMobile } = Artplayer.utils;
  const tip = (message) => alert(message);
  //vue的实例
  let vueInstance = {};

  //app的整体结构,作为vue的渲染模板
  const appTemplate = `
  <div class="liu-playContainer" id="app">
    <div v-show="ok">
      <button class="liu-closePlayer liu-btn" @click="closePlayer">
        <svg class="icon" width="50%" height="50%" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4012"><path d="M587.19 506.246l397.116-397.263a52.029 52.029 0 0 0 0-73.143l-2.194-2.194a51.98 51.98 0 0 0-73.143 0l-397.068 397.8-397.068-397.8a51.98 51.98 0 0 0-73.143 0l-2.146 2.194a51.054 51.054 0 0 0 0 73.143l397.069 397.263L39.544 903.461a52.029 52.029 0 0 0 0 73.142l2.146 2.195a51.98 51.98 0 0 0 73.143 0L511.9 581.583l397.068 397.215a51.98 51.98 0 0 0 73.143 0l2.194-2.146a52.029 52.029 0 0 0 0-73.143L587.19 506.246z" p-id="4013" data-spm-anchor-id="a313x.search_index.0.i1.10e63a81F8aVUU" class="selected" fill="#ffffff"></path></svg>
      </button>

      <!-- 播放模块 -->
      <div class="playSpace">
        <!-- 视频容器 -->
        <div class="artplayer-app"></div>
        <!-- 选集模块 -->
        <div class="series">
          <div class="seletor-title">选集</div>
          <div class="series-contianer">
            <button
              class="series-selector liu-btn"
              :class="{'playing':index==playingIndex}"
              style="color: #a3a3a3"
              v-for="(item,index) in playingList"
              :key="index"
              @click="playListSelect(index)"
            >
              {{item.name.slice(0,4)}}
              <!-- 固定宽度不够长 -->
            </button>
          </div>
        </div>
      </div>

      <!-- 源选择模块 -->
      <div class="sourceButtonList">
        <button
          class="source-selector liu-btn"
          v-for="(item,index) in sources"
          :key="index"
          :class="{'selected':index==selectedSource,'speed-fast':item.speed>1}"
          @click="sourceSelect(index)"
        >
          {{item.name}} {{item.speed}} m/s
        </button>
      </div>
      <!-- 一些说明 -->
      <div class="mannul">
        <a
          class="love-support"
          href="https://memos.babelgo.cn/m/1"
          target="_blank"
          style="color: #4aa150"
          >☕赏作者喝一杯咖啡?

          </a>
        <a
          class="love-support"
          href="https://t.me/wzxhhgy"
          target="_blank"
          style="color: #4aa150"
          >电报群</a
        >
        <a
          class="love-support"
          href="http://greasyfork.icu/zh-CN/scripts/459540-%E6%88%91%E5%8F%AA%E6%83%B3%E5%A5%BD%E5%A5%BD%E8%A7%82%E5%BD%B1/feedback"
          target="_blank"
          style="color: #4aa150"
          >👉反馈</a
        >
      </div>
    </div>
  </div>

    `;

  //构建vue的实例
  function initVue() {
    const e = htmlToElement(appTemplate);
    document.body.appendChild(e);
    vueInstance = new Vue({
      el: "#app",
      data: {
        //artplayer实例
        art: {},
        //标明是否搜到影片
        ok: false,
        //搜索源
        searchSource: [
          {
            name: "红牛资源",
            searchUrl:
              "https://www.hongniuzy2.com/api.php/provide/vod/from/hnm3u8/",
          },
          {
            name: "非凡资源",
            searchUrl: "http://cj.ffzyapi.com/api.php/provide/vod/",
          },
          {
            name: "量子资源",
            searchUrl: "https://cj.lziapi.com/api.php/provide/vod/",
          },
          {
            name: "ikun资源",
            searchUrl:
              "https://ikunzyapi.com/api.php/provide/vod/from/ikm3u8/at/json/",
          },
          {
            name: "光速资源",
            searchUrl:
              "https://api.guangsuapi.com/api.php/provide/vod/from/gsm3u8/",
          },
          {
            name: "高清资源",
            searchUrl: "https://api.1080zyku.com/inc/apijson.php/",
          },
          {
            name: "188资源",
            searchUrl: "https://www.188zy.org/api.php/provide/vod/",
          },
          {
            name: "天空资源",
            searchUrl:
              "https://m3u8.tiankongapi.com/api.php/provide/vod/from/tkm3u8/",
          }, //有防火墙,垃圾
          {
            name: "闪电资源",
            searchUrl: "https://sdzyapi.com/api.php/provide/vod/",
          }, //不太好,格式经常有错
          // { "name": "飞速资源", "searchUrl": "https://www.feisuzyapi.com/api.php/provide/vod/" },//经常作妖或者没有资源
          // { "name": "卧龙资源", "searchUrl": "https://collect.wolongzyw.com/api.php/provide/vod/" }, 非常恶心的广告
          // { "name": "8090资源", "searchUrl": "https://api.yparse.com/api/json/m3u8/" },垃圾 可能有墙
          // { "name": "百度云资源", "searchUrl": "https://api.apibdzy.com/api.php/provide/vod/" },
          // { "name": "酷点资源", "searchUrl": "https://kudian10.com/api.php/provide/vod/" },
          // { "name": "淘片资源", "searchUrl": "https://taopianapi.com/home/cjapi/as/mc10/vod/json/" },
          // { "name": "ck资源", "searchUrl": "https://ckzy.me/api.php/provide/vod/" },
          // { "name": "快播资源", "searchUrl": "https://caiji.kczyapi.com/api.php/provide/vod/" },
          // { "name": "海外看资源", "searchUrl": "http://api.haiwaikan.com/v1/vod/" }, // 说是屏蔽了所有中国的IP,所以如果你有外国的ip可能比较好
          // { "name": "68资源", "searchUrl": "https://caiji.68zyapi.com/api.php/provide/vod/" },
          // {"name":"鱼乐资源","searchUrl":"https://api.yulecj.com/api.php/provide/vod/"},//速度太慢
          // {"name":"无尽资源","searchUrl":"https://api.wujinapi.me/api.php/provide/vod/"},//资源少
        ],

        //所有搜索到的资源 [{name:"..资源",playList:[{name:"第一集",url:""}]}]
        sources: [],
        //标记选择的源
        selectedSource: 0,
        //正在播放的选集总表
        playingList: [],
        //正在播放哪一集
        playingIndex: 0,
        //咖啡地址
        coffeeUrl: "http://babelgo.cn:5230/m/1",
        //telegram地址
        telegramUrl: "https://t.me/wzxhhgy",
        //greasyfork反馈地址
        feedbackUrl:
          "http://greasyfork.icu/zh-CN/scripts/459540-%E6%88%91%E5%8F%AA%E6%83%B3%E5%A5%BD%E5%A5%BD%E8%A7%82%E5%BD%B1/feedback",
      },
      methods: {
        //merge自定义源
        mergeSource() {
          let sourceAdded = GM_getValue("sourceAdded", "");
          sourceAdded.split(",").forEach((item) => {
            if (item === "") return;
            name_url = item.split("|");
            this.searchSource.push({
              name: name_url[0],
              searchUrl: name_url[1],
            });
          });
        },
        //测试速度
        testSpeed() {
          //this.sources //所有搜索到的资源 [{name:"..资源",playList:[{name:"第一集",url:""}]}]
          this.sources.forEach(async (source, index) => {
            let name = source.name;
            let url = source.playList[this.playingIndex].url;
            let tsList = await downloadM3u8(url);
            let downLoadList = [];
            //随机选择8个切片下载
            for (let i = 0; i < 8; i++) {
              downLoadList.push(
                tsList[Math.floor(Math.random() * tsList.length)]
              );
            }
            let downloadSize = 0;
            let startTime = Date.now();

            for (item of downLoadList) {
              log("正在下载" + item);
              let r = await getBuffer(item);
              downloadSize += r.byteLength / 1024 / 1024;
            }
            let endTime = Date.now();
            let duration = (endTime - startTime) / 1000;
            let speed = downloadSize / duration ? downloadSize / duration : 0;
            this.sources[index].speed = Number(speed.toFixed(2));
            // 因为index会冲突所以暂时不用
            // this.sources.sort((a, b) => {
            //   return b.speed - a.speed;
            // });
            this.$forceUpdate();
            // log(this.sources[index]);
            log(`${name}的速度为${speed}mb/s`);
          });
        },
        //选择源的按钮行为
        sourceSelect(index) {
          let ct = this.art.currentTime
          this.selectedSource = index;
          this.playingList = this.sources[index].playList;
          this.switchUrl(this.playingList[this.playingIndex].url);
          this.art.once("video:canplay",()=>{
            this.art.seek = ct
          });
          this.testSpeed();
        },
        //选择剧集的按钮行为
        playListSelect(index) {
          this.playingIndex = index;
          //打印playlist
          log(this.playingList);
          this.switchUrl(this.playingList[this.playingIndex].url);
        },
        // 初始化Art播放器
        initArt(url) {
          this.art = new Artplayer({
            container: ".artplayer-app",
            url: url,
            pip: true,
            fullscreen: true,
            fullscreenWeb: true,
            autoMini: true,
            screenshot: true,
            hotkey: true,
            airplay: true,
            playbackRate: true,
            setting: true,
            miniProgressBar: true,
            theme: "#00981a",
            moreVideoAttr: {
              crossOrigin: "anonymous",
            },
            controls: [
              {
                name: "resolution",
                html: "分辨率",
                position: "right",
              },
            ],
            customType: {
              m3u8(video, url, art) {
                if (Hls.isSupported()) {
                  if (art.hls) art.hls.destroy();
                  const hls = new Hls();
                  hls.loadSource(url);
                  hls.attachMedia(video);
                  art.hls = hls;
                  art.on("destroy", () => hls.destroy());
                } else if (video.canPlayType("application/vnd.apple.mpegurl")) {
                  video.src = url;
                } else {
                  art.notice.show = "Unsupported playback format: m3u8";
                }
              },
            },
            plugins: [artplayerPluginControl()],
          });
          this.art.once("destroy", () => this.art.hls.destroy());
          this.art.on("video:loadedmetadata", () => {
            this.art.controls.resolution.innerText =
              this.art.video.videoHeight + "P";
          });
          log("初始化art实例完成,art:");
          log(this.art);
        },
        //切换播放器的播放url
        switchUrl(url) {
          this.art.switchUrl(url);
          //兼容safari
          if (this.art.video.src != url) {
            this.art.video.src = url;
          }
        },

        //关闭页面
        closePlayer() {
          $("#app").remove();
        },
      },
      async created() {
        //初始化时开始搜索所有资源, 初始化sources 数组
        //merge 用户添加的地址
        this.mergeSource();
        // this.searchSource.forEach();
        await Promise.all(
          this.searchSource.map(async (item, index) => {
            try {
              // search里自带解析函数,所以只要通过就说明搜索到了
              let playList = await search(item.searchUrl);
              log(`正在搜索${item.name}`);
              log(`搜索结果为:${JSON.stringify(playList)}`);
              if (playList != 0) {
                this.sources.push({ name: item.name, playList });
              }
              if (this.ok == false && playList != 0) {
                this.playingList = playList;
                this.initArt(this.playingList[0].url);
                this.ok = true;
              }
            } catch (e) {
              log(`${item.name}搜索出现错误${e}`);
            }
          })
        );
        // 结束以后ok还为false说明没搜索
        if (this.ok === false) {
          tip("未搜索到资源");
        } else {
          //开始测速
          this.testSpeed();
        }
      },
    });
  }

  //--------------------------全局方法

  //获取豆瓣影片名称
  const videoName = isMobile
    ? $(".sub-title").innerText
    : document.title.slice(0, -5);

  // debug方法
  const log = (function () {
    if (_debug) return console.log.bind(console);
    return function () {};
  })();

  function htmlToElement(html) {
    //将html转为element
    const template = document.createElement("template");
    template.innerHTML = html.trim();
    return template.content.firstChild;
  }

  //添加百度统计脚本统计使用情况
  function addScript() {
    let statistic = document.createElement("script");
    statistic.setAttribute(
      "src",
      "https://hm.baidu.com/hm.js?f02301d8266631b0285c3e325c9a574b"
    );
    document.head.appendChild(statistic);
  }

  //速度为0不一定是无法播放,可能是源的防火墙阻止了测速,也可以试试。

  //处理搜索到的结果:从返回结果中找到对应片子
  function handleResponse(r) {
    if (!r || r.list.length == 0) {
      log("未搜索到结果");
      return 0;
    }
    let video,
      found = false;
    for (let item of r.list) {
      log("正在对比剧集年份和演员");
      let yearEqual = getVideoYear(item.vod_year);
      let actorContain = videoActor(item.vod_actor.split(",")[0]);

      if (yearEqual === true || actorContain === true) {
        video = item;
        found = true;
        log("找到啦");
        break;
      }
    }
    if (found == false) {
      log("没有找到匹配剧集的影片,怎么回事哟!");
      return 0;
    }

    let playList = video.vod_play_url
      .split("$$$")
      .filter((str) => str.includes("m3u8"));
    if (playList.length == 0) {
      log("没有m3u8资源, 无法测速, 无法播放");
      return 0;
    }
    playList = playList[0].split("#");
    playList = playList.map((str) => {
      let index = str.indexOf("$");
      return {
        name: str.slice(0, index),
        url: str.slice(index + 1),
        speed: -1,
      };
    });

    return playList;
  }

  //发送搜索请求
  const search = (url) =>
    new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        method: "GET",
        url: encodeURI(`${url}?ac=detail&wd=${videoName}`),
        timeout: 3000,
        responseType: "json",
        onload(r) {
          try {
            resolve(handleResponse(r.response, videoName));
          } catch (e) {
            log(e);
            reject("垃圾资源,解析失败了,可能有防火墙");
          }
        },
        onerror: reject,
        ontimeout: reject,
      });
    });

  //播放按钮
  class PlayBtn {
    constructor() {
      const e = htmlToElement(
        `<button class="liu-btn play-btn">一键播放</button>`
      );
      $(isMobile ? ".sub-original-title" : "h1").appendChild(e);

      e.onclick = function () {
        initVue();
      };
    }
  }

  //获取电影的年份
  function getVideoYear(outYear) {
    const e = $(isMobile ? ".sub-original-title" : ".year");
    if (!e) {
      log("获取年份失败,请检查!");
      return 0;
    }
    return e.innerText.includes(outYear);
  }

  //对比电影演员
  function videoActor(outActor) {
    const e = $(isMobile ? ".bd" : ".actor");
    if (!e) {
      log("获取演员失败,请检查!");
      return 0;
    }
    //log(`${outActor}:匹配结果${e.innerText.includes(outActor)}`)
    return e.innerText.includes(outActor);
  }

  //下载
  const get = (url) => {
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        method: "GET",
        url: encodeURI(url),
        timeout: 10000,
        onload: function (r) {
          resolve(r.response);
        },
        onerror: function (e) {
          resolve("html");
        },
        ontimeout: function (o) {
          resolve("html");
        },
      });
    });
  };

  //下载m3u8的内容,返回片段列表
  async function downloadM3u8(url) {
    let domain = url.split("/")[0];
    let baseUrl = url.split("/")[2];
    let downLoadList = [];
    log(`正在获取index.m3u8 ${url}`);
    let downloadContent = await get(url);

    if (downloadContent.includes("html")) {
      log(`下载失败,被反爬虫了${downloadContent}`);
      return [];
    }

    if (downloadContent.includes("index.m3u8")) {
      //如果是m3u8地址
      let lines = downloadContent.split("\n");
      for (let item of lines) {
        if (/^[#\s]/.test(item)) continue; //跳过注释和空白行
        if (/^\//.test(item)) {
          downLoadList = await downloadM3u8(domain + "//" + baseUrl + item);
        } else if (/^(http)/.test(item)) {
          downLoadList = await downloadM3u8(item);
        } else {
          downLoadList = await downloadM3u8(url.replace("index.m3u8", item));
        }
      }
    } else {
      //如果是ts地址
      let lines = downloadContent.split("\n");
      for (let item of lines) {
        if (/^[#\s]/.test(item)) continue; //跳过注释和空白行
        if (/^(http)/.test(item)) {
          //如果是http直链
          downLoadList.push(item);
        } else if (/^\//.test(item)) {
          //如果是绝对链接
          downLoadList.push(domain + "//" + baseUrl + item);
        } else {
          downLoadList.push(url.replace("index.m3u8", item));
        }
      }
    }
    // log(`测试列表为${downLoadList}`)
    return downLoadList;
  }

  //对资源进行测速
  function speedTest() {
    // tip("脚本自动测试源的速度,随后请自行切换源进行尝试")
    let sourceButtons = $$(".source-selector");
    //log(sourceButtons)
    sourceButtons.forEach(async (e) => {
      let url = e._playList[seriesNum].url;
      let tsList = await downloadM3u8(url);
      let downloadList = [];
      for (let i = 0; i < 8; i++) {
        downloadList.push(tsList[Math.floor(Math.random() * tsList.length)]);
      }

      let downloadSize = 0;
      let startTime = Date.now();

      for (item of downloadList) {
        log("正在下载" + item);
        let r = await getBuffer(item);
        downloadSize += r.byteLength / 1024 / 1024;
      }
      let endTime = Date.now();
      let duration = (endTime - startTime) / 1000;
      let speed = downloadSize / duration ? downloadSize / duration : 0;

      log(`速度为${speed}mb/s`);

      e.innerText = e._sourceName + " " + speed.toFixed(2) + "mb/s";
      let state = speed > 1 ? "fast" : "slow";
      e.classList.add(`speed-${state}`);
    });
  }

  //将GM_xmlhttpRequest改造为Promise
  function getBuffer(url) {
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        method: "GET",
        timeout: 3000,
        url: encodeURI(url),
        responseType: "arraybuffer",
        onload: function (r) {
          resolve(r.response);
        },
        onerror: function (error) {
          log("速度太慢了或无法测速");
          resolve({ byteLength: 0 });
        },
        ontimeout: function (out) {
          log("速度太慢了或无法测速");
          resolve({ byteLength: 0 });
        },
      });
    });
  }

  new PlayBtn();
  //添加统计代码
  //   addScript();
})();