Sleazy Fork is available in English.

91-plus

多线程下载91porn视频,跳过广告,清除页面广告。

// ==UserScript==
// @name         91-plus
// @namespace    jiuyi
// @version      17
// @author       uh58fg
// @description  多线程下载91porn视频,跳过广告,清除页面广告。
// @license      WTFPL
// @icon         https://img-blog.csdnimg.cn/20181221195058594.gif
// @match        *91porn.com/view_video.php?*
// @match        *91porn.com/index.php
// @require      https://cdn.bootcdn.net/ajax/libs/hls.js/8.0.0-beta.3/hls.min.js
// @require      https://cdn.bootcdn.net/ajax/libs/dplayer/1.26.0/DPlayer.min.js
// @require      https://greasyfork.org/scripts/466106-91-plus-mux-mp4/code/91-plus-mux-mp4.js?version=1189391
// @require      https://cdn.bootcdn.net/ajax/libs/vue/3.2.47/vue.runtime.global.prod.min.js
// @grant        window.close
// ==/UserScript==

(t=>{const e=document.createElement("style");e.dataset.source="vite-plugin-monkey",e.textContent=t,document.head.append(e)})(' .row2[data-v-56d4409a]{display:flex;justify-content:flex-end}.big-btn[data-v-56d4409a]{position:relative;display:inline-flex;justify-content:center;align-items:center;margin-left:1rem;font-size:1.6rem;min-width:10rem;color:#fff;cursor:pointer;border-radius:4px;border:1px solid #eeeeee;background-color:#3d8ac7;opacity:1;transition:.3s all;padding:.5rem 1rem}.big-btn[data-v-56d4409a]:hover{opacity:.9}.big-btn.disable[data-v-56d4409a]{cursor:not-allowed;background-color:#ddd;color:#000}.m-p-input-container[data-v-56d4409a]{display:flex}.m-p-input-container input[data-v-56d4409a]{flex:1;display:block;padding:0 1rem;font-size:1.8rem;border-radius:4px;box-shadow:none;color:#fff;background:rgba(239,239,239,.3);border:1px solid #cccccc}.m-p-tips[data-v-56d4409a]{width:100%;color:#999;text-align:left;font-style:italic;word-break:break-all}.m-p-tips p[data-v-56d4409a]{width:100px;display:inline-block}.m-p-tips.error-tips[data-v-56d4409a]{color:#dc5350}.m-p-segment[data-v-56d4409a]{text-align:left}.m-p-segment .item[data-v-56d4409a]{display:inline-block;margin:10px 6px;width:50px;height:40px;color:#fff;line-height:40px;text-align:center;border-radius:4px;cursor:help;border:solid 1px #eeeeee;background-color:#ddd;transition:.3s all}.m-p-segment .finish[data-v-56d4409a]{background-color:#0acd76}.m-p-segment .error[data-v-56d4409a]{cursor:pointer;background-color:#dc5350}.m-p-segment .error[data-v-56d4409a]:hover{opacity:.9}.error-btns[data-v-56d4409a]{display:flex;justify-content:flex-end;gap:1rem}.m-p-force[data-v-56d4409a],.m-p-retry[data-v-56d4409a]{margin-top:1rem;right:50px;display:inline-block;padding:6px 12px;font-size:18px;color:#fff;cursor:pointer;border-radius:4px;border:1px solid #eeeeee;background-color:#3d8ac7;opacity:1;transition:.3s all}.m-p-retry[data-v-56d4409a]{right:250px}.m-p-force[data-v-56d4409a]:hover,.m-p-retry[data-v-56d4409a]:hover{opacity:.9}.info[data-v-56d4409a]{display:flex;justify-content:center;align-items:center;gap:1rem;margin-bottom:1rem}.info .options[data-v-56d4409a]{margin-right:1rem;display:flex;flex-direction:column;align-items:flex-end}.info .option[data-v-56d4409a]{cursor:pointer;display:flex;justify-content:center;align-items:center;color:#fff;margin-bottom:.3rem}.info .option label[data-v-56d4409a]{margin:0}.info .option input[data-v-56d4409a]{margin:0;margin-left:1rem;width:1.4rem;height:1.4rem}.progress[data-v-56d4409a]{flex:1;border-radius:.8rem;opacity:1;height:1.5rem;background:#FCFDFF;box-sizing:border-box;border:.1rem solid #FFFFFF;box-shadow:inset 0 -1rem 4rem #fffc,inset 0 1rem 4rem #2d4c7266;display:flex;align-items:center;justify-content:space-between;position:relative}.progress .bar[data-v-56d4409a]{height:100%;width:50%;left:0;top:0;position:absolute;background:linear-gradient(90deg,#9CDCFF 0%,#0ACD76 100%);border-radius:.8rem}.row2[data-v-429419b2]{display:flex;justify-content:flex-end}.big-btn[data-v-429419b2]{position:relative;display:inline-flex;justify-content:center;align-items:center;margin-left:1rem;font-size:1.6rem;min-width:10rem;color:#fff;cursor:pointer;border-radius:4px;border:1px solid #eeeeee;background-color:#3d8ac7;opacity:1;transition:.3s all;padding:.5rem 1rem}.big-btn[data-v-429419b2]:hover{opacity:.9}.big-btn.disable[data-v-429419b2]{cursor:not-allowed;background-color:#ddd;color:#000}.m-p-input-container[data-v-429419b2]{display:flex}.m-p-input-container input[data-v-429419b2]{flex:1;display:block;padding:0 1rem;font-size:1.8rem;border-radius:4px;box-shadow:none;color:#fff;background:rgba(239,239,239,.3);border:1px solid #cccccc}.m-p-tips[data-v-429419b2]{width:100%;color:#999;text-align:left;font-style:italic;word-break:break-all}.m-p-tips p[data-v-429419b2]{width:100px;display:inline-block}.m-p-tips.error-tips[data-v-429419b2]{color:#dc5350}.m-p-segment[data-v-429419b2]{text-align:left}.m-p-segment .item[data-v-429419b2]{display:inline-block;margin:10px 6px;width:50px;height:40px;color:#fff;line-height:40px;text-align:center;border-radius:4px;cursor:help;border:solid 1px #eeeeee;background-color:#ddd;transition:.3s all}.m-p-segment .finish[data-v-429419b2]{background-color:#0acd76}.m-p-segment .error[data-v-429419b2]{cursor:pointer;background-color:#dc5350}.m-p-segment .error[data-v-429419b2]:hover{opacity:.9}.error-btns[data-v-429419b2]{display:flex;justify-content:flex-end;gap:1rem}.m-p-force[data-v-429419b2],.m-p-retry[data-v-429419b2]{margin-top:1rem;right:50px;display:inline-block;padding:6px 12px;font-size:18px;color:#fff;cursor:pointer;border-radius:4px;border:1px solid #eeeeee;background-color:#3d8ac7;opacity:1;transition:.3s all}.m-p-retry[data-v-429419b2]{right:250px}.m-p-force[data-v-429419b2]:hover,.m-p-retry[data-v-429419b2]:hover{opacity:.9}.info[data-v-429419b2]{display:flex;justify-content:center;align-items:center;gap:1rem;margin-bottom:1rem}.info .options[data-v-429419b2]{margin-right:1rem;display:flex;flex-direction:column;align-items:flex-end}.info .option[data-v-429419b2]{cursor:pointer;display:flex;justify-content:center;align-items:center;color:#fff;margin-bottom:.3rem}.info .option label[data-v-429419b2]{margin:0}.info .option input[data-v-429419b2]{margin:0;margin-left:1rem;width:1.4rem;height:1.4rem}.progress[data-v-429419b2]{flex:1;border-radius:.8rem;opacity:1;height:1.5rem;background:#FCFDFF;box-sizing:border-box;border:.1rem solid #FFFFFF;box-shadow:inset 0 -1rem 4rem #fffc,inset 0 1rem 4rem #2d4c7266;display:flex;align-items:center;justify-content:space-between;position:relative}.progress .bar[data-v-429419b2]{height:100%;width:50%;left:0;top:0;position:absolute;background:linear-gradient(90deg,#9CDCFF 0%,#0ACD76 100%);border-radius:.8rem}[data-v-a069d05a]::-webkit-scrollbar{width:10px}[data-v-a069d05a]::-webkit-scrollbar-thumb{background:#4e4e4e;border-radius:25px}.showBtn[data-v-a069d05a]{cursor:pointer;position:fixed;top:1rem;right:1rem;color:#fff;z-index:99999;background:gray;padding:.6rem 1rem;border-radius:.3rem}.content[data-v-a069d05a]{z-index:9999;background:#0f0f0f;position:fixed;left:0;top:0;width:100vw;min-width:100vw;height:100vh;box-sizing:border-box;padding:1rem;display:flex;gap:2rem;text-align:start}.content .close[data-v-a069d05a]{cursor:pointer;position:absolute;top:1rem;right:2rem;width:2rem;height:2rem;line-height:4rem;text-align:center;color:#fff}.content .close[data-v-a069d05a]:before{position:absolute;content:"";width:.2rem;height:2rem;background:white;transform:rotate(45deg);top:calc(50% - .45rem);left:50%}.content .close[data-v-a069d05a]:after{content:"";position:absolute;width:.2rem;height:2rem;background:white;transform:rotate(-45deg);top:calc(50% - .45rem);left:50%}.content .home-icon[data-v-a069d05a]{cursor:pointer;right:2rem;position:absolute}.content .big-title[data-v-a069d05a]{position:relative;display:flex;align-items:center;justify-content:center;height:5rem;font-weight:700;color:#fff;letter-spacing:2px;background:#212121;font-size:2rem}.content .video[data-v-a069d05a]{width:60%;overflow:auto;padding-bottom:2rem}.content .video #dplayer[data-v-a069d05a]{width:100%}.content .video .title[data-v-a069d05a]{margin-top:1rem;font-weight:700;font-size:2rem;color:#fff;margin-bottom:1rem}.content .video .author[data-v-a069d05a]{margin-right:2rem;font-weight:700;font-size:1.4rem;margin-bottom:2rem;display:inline-block}.content .left[data-v-a069d05a]{flex:1;border:1px solid gray;border-radius:.5rem;overflow:hidden;position:relative}.content .left .comments[data-v-a069d05a]{color:#fff;height:calc(100% - 6rem);padding-bottom:4rem;overflow:auto}.content .left .comments .item[data-v-a069d05a]{margin-bottom:5px;padding:10px;border-bottom:1px solid #3f3f3f;text-align:start}.content .left .comments .item .title[data-v-a069d05a]{font-size:1rem}.content .left .comments .item .title span[data-v-a069d05a]{margin-right:10px}.content .left .comments .item .quote[data-v-a069d05a]{margin-top:.5rem;font-size:1rem;padding:.5rem .5rem .5rem 1rem;border:1px dashed gray}.content .left .comments .item .replay[data-v-a069d05a]{margin-top:1rem}.content .right[data-v-a069d05a]{flex:1;border:1px solid gray;border-radius:.5rem;overflow:hidden}.content .right .list[data-v-a069d05a]{color:#fff;height:calc(100% - 6rem);padding:1rem 1rem 4rem;overflow:auto}.ml20[data-v-a069d05a]{margin-left:20px} ');

(function (vue) {
  'use strict';

  var _monkeyWindow = /* @__PURE__ */ (() => window)();
  const _export_sfc = (sfc, props) => {
    const target = sfc.__vccOpts || sfc;
    for (const [key, val] of props) {
      target[key] = val;
    }
    return target;
  };
  function removePadding(buffer) {
    const outputBytes = buffer.byteLength;
    const paddingBytes = outputBytes && new DataView(buffer).getUint8(outputBytes - 1);
    if (paddingBytes) {
      return buffer.slice(0, outputBytes - paddingBytes);
    } else {
      return buffer;
    }
  }
  function AESDecryptor() {
    return {
      constructor() {
        this.rcon = [0, 1, 2, 4, 8, 16, 32, 64, 128, 27, 54];
        this.subMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)];
        this.invSubMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)];
        this.sBox = new Uint32Array(256);
        this.invSBox = new Uint32Array(256);
        this.key = new Uint32Array(0);
        this.initTable();
      },
      // Using view.getUint32() also swaps the byte order.
      uint8ArrayToUint32Array_(arrayBuffer) {
        let view = new DataView(arrayBuffer);
        let newArray = new Uint32Array(4);
        for (let i = 0; i < 4; i++) {
          newArray[i] = view.getUint32(i * 4);
        }
        return newArray;
      },
      initTable() {
        let sBox = this.sBox;
        let invSBox = this.invSBox;
        let subMix = this.subMix;
        let subMix0 = subMix[0];
        let subMix1 = subMix[1];
        let subMix2 = subMix[2];
        let subMix3 = subMix[3];
        let invSubMix = this.invSubMix;
        let invSubMix0 = invSubMix[0];
        let invSubMix1 = invSubMix[1];
        let invSubMix2 = invSubMix[2];
        let invSubMix3 = invSubMix[3];
        let d = new Uint32Array(256);
        let x = 0;
        let xi = 0;
        let i = 0;
        for (i = 0; i < 256; i++) {
          if (i < 128) {
            d[i] = i << 1;
          } else {
            d[i] = i << 1 ^ 283;
          }
        }
        for (i = 0; i < 256; i++) {
          let sx = xi ^ xi << 1 ^ xi << 2 ^ xi << 3 ^ xi << 4;
          sx = sx >>> 8 ^ sx & 255 ^ 99;
          sBox[x] = sx;
          invSBox[sx] = x;
          let x2 = d[x];
          let x4 = d[x2];
          let x8 = d[x4];
          let t = d[sx] * 257 ^ sx * 16843008;
          subMix0[x] = t << 24 | t >>> 8;
          subMix1[x] = t << 16 | t >>> 16;
          subMix2[x] = t << 8 | t >>> 24;
          subMix3[x] = t;
          t = x8 * 16843009 ^ x4 * 65537 ^ x2 * 257 ^ x * 16843008;
          invSubMix0[sx] = t << 24 | t >>> 8;
          invSubMix1[sx] = t << 16 | t >>> 16;
          invSubMix2[sx] = t << 8 | t >>> 24;
          invSubMix3[sx] = t;
          if (!x) {
            x = xi = 1;
          } else {
            x = x2 ^ d[d[d[x8 ^ x2]]];
            xi ^= d[d[xi]];
          }
        }
      },
      expandKey(keyBuffer) {
        let key = this.uint8ArrayToUint32Array_(keyBuffer);
        let sameKey = true;
        let offset = 0;
        while (offset < key.length && sameKey) {
          sameKey = key[offset] === this.key[offset];
          offset++;
        }
        if (sameKey) {
          return;
        }
        this.key = key;
        let keySize = this.keySize = key.length;
        if (keySize !== 4 && keySize !== 6 && keySize !== 8) {
          throw new Error("Invalid aes key size=" + keySize);
        }
        let ksRows = this.ksRows = (keySize + 6 + 1) * 4;
        let ksRow;
        let invKsRow;
        let keySchedule = this.keySchedule = new Uint32Array(ksRows);
        let invKeySchedule = this.invKeySchedule = new Uint32Array(ksRows);
        let sbox = this.sBox;
        let rcon = this.rcon;
        let invSubMix = this.invSubMix;
        let invSubMix0 = invSubMix[0];
        let invSubMix1 = invSubMix[1];
        let invSubMix2 = invSubMix[2];
        let invSubMix3 = invSubMix[3];
        let prev;
        let t;
        for (ksRow = 0; ksRow < ksRows; ksRow++) {
          if (ksRow < keySize) {
            prev = keySchedule[ksRow] = key[ksRow];
            continue;
          }
          t = prev;
          if (ksRow % keySize === 0) {
            t = t << 8 | t >>> 24;
            t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 255] << 16 | sbox[t >>> 8 & 255] << 8 | sbox[t & 255];
            t ^= rcon[ksRow / keySize | 0] << 24;
          } else if (keySize > 6 && ksRow % keySize === 4) {
            t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 255] << 16 | sbox[t >>> 8 & 255] << 8 | sbox[t & 255];
          }
          keySchedule[ksRow] = prev = (keySchedule[ksRow - keySize] ^ t) >>> 0;
        }
        for (invKsRow = 0; invKsRow < ksRows; invKsRow++) {
          ksRow = ksRows - invKsRow;
          if (invKsRow & 3) {
            t = keySchedule[ksRow];
          } else {
            t = keySchedule[ksRow - 4];
          }
          if (invKsRow < 4 || ksRow <= 4) {
            invKeySchedule[invKsRow] = t;
          } else {
            invKeySchedule[invKsRow] = invSubMix0[sbox[t >>> 24]] ^ invSubMix1[sbox[t >>> 16 & 255]] ^ invSubMix2[sbox[t >>> 8 & 255]] ^ invSubMix3[sbox[t & 255]];
          }
          invKeySchedule[invKsRow] = invKeySchedule[invKsRow] >>> 0;
        }
      },
      // Adding this as a method greatly improves performance.
      networkToHostOrderSwap(word) {
        return word << 24 | (word & 65280) << 8 | (word & 16711680) >> 8 | word >>> 24;
      },
      decrypt(inputArrayBuffer, offset, aesIV, removePKCS7Padding) {
        let nRounds = this.keySize + 6;
        let invKeySchedule = this.invKeySchedule;
        let invSBOX = this.invSBox;
        let invSubMix = this.invSubMix;
        let invSubMix0 = invSubMix[0];
        let invSubMix1 = invSubMix[1];
        let invSubMix2 = invSubMix[2];
        let invSubMix3 = invSubMix[3];
        let initVector = this.uint8ArrayToUint32Array_(aesIV);
        let initVector0 = initVector[0];
        let initVector1 = initVector[1];
        let initVector2 = initVector[2];
        let initVector3 = initVector[3];
        let inputInt32 = new Int32Array(inputArrayBuffer);
        let outputInt32 = new Int32Array(inputInt32.length);
        let t0, t1, t2, t3;
        let s0, s1, s2, s3;
        let inputWords0, inputWords1, inputWords2, inputWords3;
        let ksRow, i;
        let swapWord = this.networkToHostOrderSwap;
        while (offset < inputInt32.length) {
          inputWords0 = swapWord(inputInt32[offset]);
          inputWords1 = swapWord(inputInt32[offset + 1]);
          inputWords2 = swapWord(inputInt32[offset + 2]);
          inputWords3 = swapWord(inputInt32[offset + 3]);
          s0 = inputWords0 ^ invKeySchedule[0];
          s1 = inputWords3 ^ invKeySchedule[1];
          s2 = inputWords2 ^ invKeySchedule[2];
          s3 = inputWords1 ^ invKeySchedule[3];
          ksRow = 4;
          for (i = 1; i < nRounds; i++) {
            t0 = invSubMix0[s0 >>> 24] ^ invSubMix1[s1 >> 16 & 255] ^ invSubMix2[s2 >> 8 & 255] ^ invSubMix3[s3 & 255] ^ invKeySchedule[ksRow];
            t1 = invSubMix0[s1 >>> 24] ^ invSubMix1[s2 >> 16 & 255] ^ invSubMix2[s3 >> 8 & 255] ^ invSubMix3[s0 & 255] ^ invKeySchedule[ksRow + 1];
            t2 = invSubMix0[s2 >>> 24] ^ invSubMix1[s3 >> 16 & 255] ^ invSubMix2[s0 >> 8 & 255] ^ invSubMix3[s1 & 255] ^ invKeySchedule[ksRow + 2];
            t3 = invSubMix0[s3 >>> 24] ^ invSubMix1[s0 >> 16 & 255] ^ invSubMix2[s1 >> 8 & 255] ^ invSubMix3[s2 & 255] ^ invKeySchedule[ksRow + 3];
            s0 = t0;
            s1 = t1;
            s2 = t2;
            s3 = t3;
            ksRow = ksRow + 4;
          }
          t0 = invSBOX[s0 >>> 24] << 24 ^ invSBOX[s1 >> 16 & 255] << 16 ^ invSBOX[s2 >> 8 & 255] << 8 ^ invSBOX[s3 & 255] ^ invKeySchedule[ksRow];
          t1 = invSBOX[s1 >>> 24] << 24 ^ invSBOX[s2 >> 16 & 255] << 16 ^ invSBOX[s3 >> 8 & 255] << 8 ^ invSBOX[s0 & 255] ^ invKeySchedule[ksRow + 1];
          t2 = invSBOX[s2 >>> 24] << 24 ^ invSBOX[s3 >> 16 & 255] << 16 ^ invSBOX[s0 >> 8 & 255] << 8 ^ invSBOX[s1 & 255] ^ invKeySchedule[ksRow + 2];
          t3 = invSBOX[s3 >>> 24] << 24 ^ invSBOX[s0 >> 16 & 255] << 16 ^ invSBOX[s1 >> 8 & 255] << 8 ^ invSBOX[s2 & 255] ^ invKeySchedule[ksRow + 3];
          ksRow = ksRow + 3;
          outputInt32[offset] = swapWord(t0 ^ initVector0);
          outputInt32[offset + 1] = swapWord(t3 ^ initVector1);
          outputInt32[offset + 2] = swapWord(t2 ^ initVector2);
          outputInt32[offset + 3] = swapWord(t1 ^ initVector3);
          initVector0 = inputWords0;
          initVector1 = inputWords1;
          initVector2 = inputWords2;
          initVector3 = inputWords3;
          offset = offset + 4;
        }
        return removePKCS7Padding ? removePadding(outputInt32.buffer) : outputInt32.buffer;
      },
      destroy() {
        this.key = void 0;
        this.keySize = void 0;
        this.ksRows = void 0;
        this.sBox = void 0;
        this.invSBox = void 0;
        this.subMix = void 0;
        this.invSubMix = void 0;
        this.keySchedule = void 0;
        this.invKeySchedule = void 0;
        this.rcon = void 0;
      }
    };
  }
  const _sfc_main$2 = {
    name: "downloader",
    props: {
      url: "",
      title: ""
    },
    data() {
      return {
        conf: {
          autoDownload: true,
          autoSave: true,
          autoClose: false
        },
        show: false,
        tips: "m3u8 视频在线提取工具",
        // 顶部提示
        isPause: false,
        // 是否暂停下载
        isGetMP4: false,
        // 是否转码为 MP4 下载
        durationSecond: 0,
        // 视频持续时长
        isShowRefer: false,
        // 是否显示推送
        downloading: false,
        // 是否下载中
        beginTime: "",
        // 开始下载的时间
        errorNum: 0,
        // 错误数
        finishNum: 0,
        // 已下载数
        downloadIndex: 0,
        // 当前下载片段
        finishList: [],
        // 下载完成项目
        tsUrlList: [],
        // ts URL数组
        mediaFileList: [],
        // 下载的媒体数组
        isSupperStreamWrite: _monkeyWindow.streamSaver && !_monkeyWindow.streamSaver.useBlobFallback,
        // 当前浏览器是否支持流式下载
        streamWriter: null,
        // 文件流写入器
        streamDownloadIndex: 0,
        // 文件流写入器,正准备写入第几个视频片段
        rangeDownload: {
          // 特定范围下载
          isShowRange: false,
          // 是否显示范围下载
          startSegment: "",
          // 起始片段
          endSegment: "",
          // 截止片段
          targetSegment: 1
          // 待下载片段
        },
        aesConf: {
          // AES 视频解密配置
          method: "",
          // 加密算法
          uri: "",
          // key 所在文件路径
          iv: "",
          // 偏移值
          key: "",
          // 秘钥
          decryptor: null,
          // 解码器对象
          stringToBuffer: function(str) {
            return new TextEncoder().encode(str);
          }
        }
      };
    },
    computed: {
      progress() {
        return (this.finishNum / this.rangeDownload.targetSegment * 100).toFixed(2);
      }
    },
    watch: {
      conf: {
        handler(n, o) {
          localStorage.setItem("porn-plus", JSON.stringify(n));
        },
        deep: true
      },
      progress(n, o) {
        document.title = Number(n).toFixed(0) + "% " + this.title;
      }
    },
    created() {
      console.log("m3u8-downloader.vue");
      this.getConf();
      if (this.conf.autoDownload) {
        this.getMP4();
      }
      setInterval(this.retryAll.bind(this), 2e3);
    },
    methods: {
      // ajax 请求
      ajax(options) {
        options = options || {};
        let xhr = new XMLHttpRequest();
        if (options.type === "file") {
          xhr.responseType = "arraybuffer";
        }
        xhr.onreadystatechange = function() {
          if (xhr.readyState === 4) {
            let status = xhr.status;
            if (status >= 200 && status < 300) {
              options.success && options.success(xhr.response);
            } else {
              options.fail && options.fail(status);
            }
          }
        };
        xhr.open("GET", options.url, true);
        xhr.send(null);
      },
      // 合成URL
      applyURL(targetURL, baseURL) {
        baseURL = baseURL || location.href;
        if (targetURL.indexOf("http") === 0) {
          if (location.href.indexOf("https") === 0) {
            return targetURL.replace("http://", "https://");
          }
          return targetURL;
        } else if (targetURL[0] === "/") {
          let domain = baseURL.split("/");
          return domain[0] + "//" + domain[2] + targetURL;
        } else {
          let domain = baseURL.split("/");
          domain.pop();
          return domain.join("/") + "/" + targetURL;
        }
      },
      // 使用流式下载,边下载边保存,解决大视频文件内存不足的难题
      streamDownload(isMp4) {
        this.isGetMP4 = isMp4;
        let fileName = this.title || this.formatTime(/* @__PURE__ */ new Date(), "YYYY_MM_DD hh_mm_ss");
        this.streamWriter = _monkeyWindow.streamSaver.createWriteStream(`${fileName}.${isMp4 ? "mp4" : "ts"}`).getWriter();
        this.getM3U8();
      },
      // 解析为 mp4 下载
      getMP4() {
        this.isGetMP4 = true;
        this.getM3U8();
      },
      // 获取在线文件
      getM3U8(onlyGetRange) {
        if (!this.url) {
          alert("请输入链接");
          return;
        }
        if (this.url.toLowerCase().indexOf("m3u8") === -1)
          ;
        if (this.downloading) {
          alert("资源下载中,请稍后");
          return;
        }
        this.tips = "m3u8 文件下载中,请稍后";
        this.beginTime = /* @__PURE__ */ new Date();
        this.ajax({
          url: this.url,
          success: (m3u8Str) => {
            this.tsUrlList = [];
            this.finishList = [];
            m3u8Str.split("\n").forEach((item) => {
              if (/^[^#]/.test(item)) {
                this.tsUrlList.push(this.applyURL(item, this.url));
                this.finishList.push({
                  title: item,
                  status: ""
                });
              }
            });
            if (onlyGetRange) {
              this.rangeDownload.isShowRange = true;
              this.rangeDownload.endSegment = this.tsUrlList.length;
              this.rangeDownload.targetSegment = this.tsUrlList.length;
              return;
            } else {
              let startSegment = Math.max(this.rangeDownload.startSegment || 1, 1);
              let endSegment = Math.max(this.rangeDownload.endSegment || this.tsUrlList.length, 1);
              startSegment = Math.min(startSegment, this.tsUrlList.length);
              endSegment = Math.min(endSegment, this.tsUrlList.length);
              this.rangeDownload.startSegment = Math.min(startSegment, endSegment);
              this.rangeDownload.endSegment = Math.max(startSegment, endSegment);
              this.rangeDownload.targetSegment = this.rangeDownload.endSegment - this.rangeDownload.startSegment + 1;
              this.downloadIndex = this.rangeDownload.startSegment - 1;
              this.downloading = true;
            }
            if (this.isGetMP4) {
              let infoIndex = 0;
              m3u8Str.split("\n").forEach((item) => {
                if (item.toUpperCase().indexOf("#EXTINF:") > -1) {
                  infoIndex++;
                  if (this.rangeDownload.startSegment <= infoIndex && infoIndex <= this.rangeDownload.endSegment) {
                    this.durationSecond += parseFloat(item.split("#EXTINF:")[1]);
                  }
                }
              });
            }
            console.log("this.durationSecond", this.durationSecond);
            if (m3u8Str.indexOf("#EXT-X-KEY") > -1) {
              this.aesConf.method = (m3u8Str.match(/(.*METHOD=([^,\s]+))/) || ["", "", ""])[2];
              this.aesConf.uri = (m3u8Str.match(/(.*URI="([^"]+))"/) || ["", "", ""])[2];
              this.aesConf.iv = (m3u8Str.match(/(.*IV=([^,\s]+))/) || ["", "", ""])[2];
              this.aesConf.iv = this.aesConf.iv ? this.aesConf.stringToBuffer(this.aesConf.iv) : "";
              this.aesConf.uri = this.applyURL(this.aesConf.uri, this.url);
              this.getAES();
            } else if (this.tsUrlList.length > 0) {
              this.downloadTS();
            } else {
              this.alertError("资源为空,请查看链接是否有效");
            }
          },
          fail: () => {
            this.alertError("链接不正确,请查看链接是否有效");
          }
        });
      },
      // 获取AES配置
      getAES() {
        this.ajax({
          type: "file",
          url: this.aesConf.uri,
          success: (key) => {
            this.aesConf.key = key;
            this.aesConf.decryptor = new AESDecryptor();
            this.aesConf.decryptor.constructor();
            this.aesConf.decryptor.expandKey(this.aesConf.key);
            this.downloadTS();
          },
          fail: () => {
            this.alertError("视频已加密,可试用右下角入口的「无差别提取工具」");
          }
        });
      },
      // ts 片段的 AES 解码
      aesDecrypt(data, index) {
        let iv = this.aesConf.iv || new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, index]);
        return this.aesConf.decryptor.decrypt(data, 0, iv.buffer || iv, true);
      },
      // 下载分片
      downloadTS() {
        this.tips = "ts 视频碎片下载中,请稍后";
        let download = () => {
          let isPause = this.isPause;
          let index = this.downloadIndex;
          if (index >= this.rangeDownload.endSegment) {
            return;
          }
          this.downloadIndex++;
          if (this.finishList[index] && this.finishList[index].status === "") {
            this.finishList[index].status = "downloading";
            this.ajax({
              url: this.tsUrlList[index],
              type: "file",
              success: (file) => {
                this.dealTS(file, index, () => this.downloadIndex < this.rangeDownload.endSegment && !isPause && download());
              },
              fail: () => {
                this.errorNum++;
                this.finishList[index].status = "error";
                if (this.downloadIndex < this.rangeDownload.endSegment) {
                  !isPause && download();
                }
              }
            });
          } else if (this.downloadIndex < this.rangeDownload.endSegment) {
            !isPause && download();
          }
        };
        for (let i = 0; i < Math.min(6, this.rangeDownload.targetSegment - this.finishNum); i++) {
          download();
        }
      },
      // 处理 ts 片段,AES 解密、mp4 转码
      dealTS(file, index, callback) {
        const data = this.aesConf.uri ? this.aesDecrypt(file, index) : file;
        this.conversionMp4(data, index, (afterData) => {
          this.mediaFileList[index - this.rangeDownload.startSegment + 1] = afterData;
          this.finishList[index].status = "finish";
          this.finishNum++;
          if (this.streamWriter) {
            for (let index2 = this.streamDownloadIndex; index2 < this.mediaFileList.length; index2++) {
              if (this.mediaFileList[index2]) {
                this.streamWriter.write(new Uint8Array(this.mediaFileList[index2]));
                this.mediaFileList[index2] = null;
                this.streamDownloadIndex = index2 + 1;
              } else {
                break;
              }
            }
            if (this.streamDownloadIndex >= this.rangeDownload.targetSegment) {
              this.streamWriter.close();
            }
          } else if (this.finishNum === this.rangeDownload.targetSegment) {
            let fileName = this.title || this.formatTime(this.beginTime, "YYYY_MM_DD hh_mm_ss");
            this.downloadFile(this.mediaFileList, fileName);
          }
          callback && callback();
        });
      },
      // 转码为 mp4
      conversionMp4(data, index, callback) {
        if (this.isGetMP4) {
          let transmuxer = new _monkeyWindow.muxjs.Transmuxer({
            keepOriginalTimestamps: true,
            duration: parseInt(this.durationSecond)
          });
          transmuxer.on("data", (segment) => {
            if (index === this.rangeDownload.startSegment - 1) {
              let data2 = new Uint8Array(segment.initSegment.byteLength + segment.data.byteLength);
              data2.set(segment.initSegment, 0);
              data2.set(segment.data, segment.initSegment.byteLength);
              callback(data2.buffer);
            } else {
              callback(segment.data);
            }
          });
          transmuxer.push(new Uint8Array(data));
          transmuxer.flush();
        } else {
          callback(data);
        }
      },
      // 暂停与恢复
      togglePause() {
        this.isPause = !this.isPause;
        !this.isPause && this.retryAll(true);
      },
      // 重新下载某个片段
      retry(index) {
        if (this.finishList[index].status === "error") {
          this.finishList[index].status = "";
          this.ajax({
            url: this.tsUrlList[index],
            type: "file",
            success: (file) => {
              this.errorNum--;
              this.dealTS(file, index);
            },
            fail: () => {
              this.finishList[index].status = "error";
            }
          });
        }
      },
      // 重新下载所有错误片段
      retryAll(forceRestart) {
        if (!this.finishList.length || this.isPause) {
          return;
        }
        let firstErrorIndex = this.downloadIndex;
        this.finishList.forEach((item, index) => {
          if (item.status === "error") {
            item.status = "";
            firstErrorIndex = Math.min(firstErrorIndex, index);
          }
        });
        this.errorNum = 0;
        if (this.downloadIndex >= this.rangeDownload.endSegment || forceRestart) {
          this.downloadIndex = firstErrorIndex;
          this.downloadTS();
        } else {
          this.downloadIndex = firstErrorIndex;
        }
      },
      // 下载整合后的TS文件
      downloadFile(fileDataList, fileName, forceSave = false) {
        this.getConf();
        setTimeout(() => {
          document.title = "下载完成 " + this.title;
        }, 1e3);
        if (!this.conf.autoSave && !forceSave) {
          return;
        }
        this.tips = "ts 碎片整合中,请留意浏览器下载";
        let fileBlob = null;
        let a = document.createElement("a");
        if (this.isGetMP4) {
          fileBlob = new Blob(fileDataList, { type: "video/mp4" });
          a.download = fileName + ".mp4";
        } else {
          fileBlob = new Blob(fileDataList, { type: "video/MP2T" });
          a.download = fileName + ".ts";
        }
        a.href = URL.createObjectURL(fileBlob);
        a.style.display = "none";
        document.body.appendChild(a);
        a.click();
        a.remove();
        setTimeout(() => {
          if (this.conf.autoClose) {
            window.opener = null;
            window.open("", "_self", "");
            window.close();
          }
        }, 1e3);
      },
      // 格式化时间
      formatTime(date, formatStr) {
        const formatType = {
          Y: date.getFullYear(),
          M: date.getMonth() + 1,
          D: date.getDate(),
          h: date.getHours(),
          m: date.getMinutes(),
          s: date.getSeconds()
        };
        return formatStr.replace(
          /Y+|M+|D+|h+|m+|s+/g,
          (target) => (new Array(target.length).join("0") + formatType[target[0]]).substr(-target.length)
        );
      },
      // 强制下载现有片段
      forceDownload() {
        if (this.mediaFileList.length) {
          let fileName = this.title || this.formatTime(this.beginTime, "YYYY_MM_DD hh_mm_ss");
          this.downloadFile(this.mediaFileList, fileName, true);
        } else {
          alert("当前无已下载片段");
        }
      },
      // 发生错误,进行提示
      alertError(tips) {
        alert(tips);
        this.downloading = false;
        this.tips = "m3u8 视频在线提取工具";
      },
      getConf() {
        let confStr = localStorage.getItem("porn-plus");
        if (confStr) {
          try {
            this.conf = JSON.parse(confStr);
          } catch (e) {
          }
        }
      }
    }
  };
  const _withScopeId$2 = (n) => (vue.pushScopeId("data-v-56d4409a"), n = n(), vue.popScopeId(), n);
  const _hoisted_1$2 = { id: "app" };
  const _hoisted_2$2 = { class: "info" };
  const _hoisted_3$2 = { class: "options" };
  const _hoisted_4$2 = { class: "option" };
  const _hoisted_5$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ vue.createElementVNode("label", { for: "a1" }, "自动下载", -1));
  const _hoisted_6$2 = { class: "option" };
  const _hoisted_7$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ vue.createElementVNode("label", { for: "a2" }, "下载完成后自动保存", -1));
  const _hoisted_8$2 = { class: "option" };
  const _hoisted_9$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ vue.createElementVNode("label", { for: "a3" }, "保存后自动关闭", -1));
  const _hoisted_10$2 = {
    key: 0,
    class: "row2"
  };
  const _hoisted_11$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ vue.createElementVNode("span", null, "保存", -1));
  const _hoisted_12$2 = [
    _hoisted_11$2
  ];
  const _hoisted_13$2 = { class: "wrapper" };
  const _hoisted_14$2 = { class: "m-p-input-container" };
  const _hoisted_15$2 = ["value"];
  const _hoisted_16$2 = {
    key: 1,
    class: "disable big-btn"
  };
  const _hoisted_17$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ vue.createElementVNode("span", null, "保存", -1));
  const _hoisted_18$2 = [
    _hoisted_17$2
  ];
  const _hoisted_19$1 = { class: "error-btns" };
  const _hoisted_20 = { class: "m-p-tips" };
  const _hoisted_21 = { class: "m-p-segment" };
  const _hoisted_22 = ["title", "onClick"];
  function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) {
    return vue.openBlock(), vue.createElementBlock("section", _hoisted_1$2, [
      vue.createElementVNode("div", _hoisted_2$2, [
        vue.createElementVNode("div", {
          class: "progress",
          onClick: _cache[0] || (_cache[0] = ($event) => $data.show = !$data.show)
        }, [
          vue.createElementVNode("div", {
            class: "bar",
            style: vue.normalizeStyle({ width: $options.progress + "%" })
          }, null, 4)
        ]),
        vue.createElementVNode("div", _hoisted_3$2, [
          vue.createElementVNode("div", _hoisted_4$2, [
            _hoisted_5$2,
            vue.withDirectives(vue.createElementVNode("input", {
              id: "a1",
              type: "checkbox",
              "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => $data.conf.autoDownload = $event)
            }, null, 512), [
              [vue.vModelCheckbox, $data.conf.autoDownload]
            ])
          ]),
          vue.createElementVNode("div", _hoisted_6$2, [
            _hoisted_7$2,
            vue.withDirectives(vue.createElementVNode("input", {
              id: "a2",
              type: "checkbox",
              "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => $data.conf.autoSave = $event)
            }, null, 512), [
              [vue.vModelCheckbox, $data.conf.autoSave]
            ])
          ]),
          vue.createElementVNode("div", _hoisted_8$2, [
            _hoisted_9$2,
            vue.withDirectives(vue.createElementVNode("input", {
              id: "a3",
              type: "checkbox",
              "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => $data.conf.autoClose = $event)
            }, null, 512), [
              [vue.vModelCheckbox, $data.conf.autoClose]
            ])
          ])
        ])
      ]),
      !$data.show ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_10$2, [
        $data.finishNum === $data.rangeDownload.targetSegment && $data.rangeDownload.targetSegment > 0 ? (vue.openBlock(), vue.createElementBlock("div", {
          key: 0,
          class: "big-btn",
          onClick: _cache[4] || (_cache[4] = (...args) => $options.forceDownload && $options.forceDownload(...args))
        }, _hoisted_12$2)) : vue.createCommentVNode("", true),
        vue.createElementVNode("div", {
          class: "big-btn",
          onClick: _cache[5] || (_cache[5] = ($event) => $data.show = !$data.show)
        }, [
          vue.createElementVNode("span", null, vue.toDisplayString(!$data.show ? "展开" : "收起") + "下载详情", 1)
        ])
      ])) : vue.createCommentVNode("", true),
      vue.withDirectives(vue.createElementVNode("div", _hoisted_13$2, [
        vue.createElementVNode("section", _hoisted_14$2, [
          vue.createElementVNode("input", {
            type: "text",
            value: $props.url,
            disabled: true,
            placeholder: "请输入 m3u8 链接"
          }, null, 8, _hoisted_15$2),
          !$data.downloading ? (vue.openBlock(), vue.createElementBlock("div", {
            key: 0,
            class: "big-btn",
            onClick: _cache[6] || (_cache[6] = (...args) => $options.getMP4 && $options.getMP4(...args))
          }, "下载")) : $data.finishNum === $data.rangeDownload.targetSegment && $data.rangeDownload.targetSegment > 0 ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_16$2, " 下载完成 ")) : (vue.openBlock(), vue.createElementBlock("div", {
            key: 2,
            class: "big-btn",
            onClick: _cache[7] || (_cache[7] = (...args) => $options.togglePause && $options.togglePause(...args))
          }, vue.toDisplayString($data.isPause ? "恢复下载" : "暂停下载"), 1)),
          $data.finishNum === $data.rangeDownload.targetSegment && $data.rangeDownload.targetSegment > 0 && !$data.conf.autoSave ? (vue.openBlock(), vue.createElementBlock("div", {
            key: 3,
            class: "big-btn",
            onClick: _cache[8] || (_cache[8] = (...args) => $options.forceDownload && $options.forceDownload(...args))
          }, _hoisted_18$2)) : vue.createCommentVNode("", true),
          vue.createElementVNode("div", {
            class: "big-btn detail",
            onClick: _cache[9] || (_cache[9] = ($event) => $data.show = !$data.show)
          }, [
            vue.createElementVNode("span", null, vue.toDisplayString(!$data.show ? "展开" : "收起") + "下载详情", 1)
          ])
        ]),
        $data.finishList.length > 0 ? (vue.openBlock(), vue.createElementBlock(vue.Fragment, { key: 0 }, [
          vue.createElementVNode("div", _hoisted_19$1, [
            $data.errorNum && $data.downloadIndex >= $data.rangeDownload.targetSegment ? (vue.openBlock(), vue.createElementBlock("div", {
              key: 0,
              class: "m-p-retry",
              onClick: _cache[10] || (_cache[10] = (...args) => $options.retryAll && $options.retryAll(...args))
            }, " 重新下载错误片段 ")) : vue.createCommentVNode("", true),
            $data.mediaFileList.length && !$data.streamWriter ? (vue.openBlock(), vue.createElementBlock("div", {
              key: 1,
              class: "m-p-force",
              onClick: _cache[11] || (_cache[11] = (...args) => $options.forceDownload && $options.forceDownload(...args))
            }, "强制下载现有片段 ")) : vue.createCommentVNode("", true)
          ]),
          vue.createElementVNode("div", _hoisted_20, "待下载碎片总量:" + vue.toDisplayString($data.rangeDownload.targetSegment) + ",已下载:" + vue.toDisplayString($data.finishNum) + ",错误:" + vue.toDisplayString($data.errorNum) + ",进度:" + vue.toDisplayString($options.progress) + "% ", 1),
          vue.createElementVNode("div", {
            class: vue.normalizeClass(["m-p-tips", [$data.errorNum ? "error-tips" : ""]])
          }, " 若某视频碎片下载发生错误,将标记为红色,可点击相应图标进行重试 ", 2),
          vue.createElementVNode("section", _hoisted_21, [
            (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList($data.finishList, (item, index) => {
              return vue.openBlock(), vue.createElementBlock("div", {
                class: vue.normalizeClass(["item", [item.status]]),
                title: item.title,
                onClick: ($event) => $options.retry(index)
              }, vue.toDisplayString(index + 1), 11, _hoisted_22);
            }), 256))
          ])
        ], 64)) : vue.createCommentVNode("", true)
      ], 512), [
        [vue.vShow, $data.show]
      ])
    ]);
  }
  const Downloader = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render$1], ["__scopeId", "data-v-56d4409a"]]);
  let videoBlob;
  const _sfc_main$1 = {
    name: "mp4-downloader",
    props: {
      url: "",
      name: ""
    },
    data() {
      return {
        conf: {
          autoDownload: true,
          autoSave: true,
          autoClose: false
        },
        progress: 0,
        show: false,
        tips: "m3u8 视频在线提取工具",
        // 顶部提示
        isPause: false,
        // 是否暂停下载
        isGetMP4: false
        // 是否转码为 MP4 下载
      };
    },
    watch: {
      conf: {
        handler(n, o) {
          localStorage.setItem("porn-plus", JSON.stringify(n));
        },
        deep: true
      },
      progress(n, o) {
        document.title = Number(n).toFixed(0) + "% " + this.name;
      }
    },
    created() {
      console.log("mp4-downloader.vue");
      this.getConf();
      if (this.conf.autoDownload) {
        this.download();
      }
    },
    methods: {
      getConf() {
        let confStr = localStorage.getItem("porn-plus");
        if (confStr) {
          try {
            this.conf = JSON.parse(confStr);
          } catch (e) {
          }
        }
      },
      download() {
        let xhr = new XMLHttpRequest();
        xhr.open("GET", this.url, true);
        xhr.responseType = "blob";
        xhr.onprogress = (event) => {
          this.progress = Number(Number(event.loaded / event.total).toFixed(2)) * 100;
          console.log("进度", this.progress);
        };
        xhr.onload = () => {
          if (xhr.readyState === 4 && xhr.status === 200) {
            videoBlob = xhr.response;
            this.progress = 100;
            this.getConf();
            if (this.conf.autoSave) {
              this.save();
            }
          }
        };
        xhr.send();
      },
      save() {
        if (!videoBlob)
          return;
        this.getConf();
        let a = document.createElement("a");
        a.style.display = "none";
        document.body.appendChild(a);
        let url = window.URL.createObjectURL(videoBlob);
        a.href = url;
        a.download = this.name + ".mp4";
        a.click();
        window.URL.revokeObjectURL(url);
        a.remove();
        setTimeout(() => {
          if (this.conf.autoClose) {
            window.opener = null;
            window.open("", "_self", "");
            window.close();
          }
        }, 1e3);
      }
    }
  };
  const _withScopeId$1 = (n) => (vue.pushScopeId("data-v-429419b2"), n = n(), vue.popScopeId(), n);
  const _hoisted_1$1 = { class: "mp4-downloader" };
  const _hoisted_2$1 = { class: "info" };
  const _hoisted_3$1 = { class: "options" };
  const _hoisted_4$1 = { class: "option" };
  const _hoisted_5$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ vue.createElementVNode("label", { for: "a1" }, "自动下载", -1));
  const _hoisted_6$1 = { class: "option" };
  const _hoisted_7$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ vue.createElementVNode("label", { for: "a2" }, "下载完成后自动保存", -1));
  const _hoisted_8$1 = { class: "option" };
  const _hoisted_9$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ vue.createElementVNode("label", { for: "a3" }, "保存后自动关闭", -1));
  const _hoisted_10$1 = {
    key: 0,
    class: "row2"
  };
  const _hoisted_11$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ vue.createElementVNode("span", null, "保存", -1));
  const _hoisted_12$1 = [
    _hoisted_11$1
  ];
  const _hoisted_13$1 = { class: "wrapper" };
  const _hoisted_14$1 = { class: "m-p-input-container" };
  const _hoisted_15$1 = ["value"];
  const _hoisted_16$1 = {
    key: 1,
    class: "disable big-btn"
  };
  const _hoisted_17$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ vue.createElementVNode("span", null, "保存", -1));
  const _hoisted_18$1 = [
    _hoisted_17$1
  ];
  const _hoisted_19 = { class: "m-p-tips" };
  function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
    return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$1, [
      vue.createElementVNode("div", _hoisted_2$1, [
        vue.createElementVNode("div", {
          class: "progress",
          onClick: _cache[0] || (_cache[0] = ($event) => $data.show = !$data.show)
        }, [
          vue.createElementVNode("div", {
            class: "bar",
            style: vue.normalizeStyle({ width: $data.progress + "%" })
          }, null, 4)
        ]),
        vue.createElementVNode("div", _hoisted_3$1, [
          vue.createElementVNode("div", _hoisted_4$1, [
            _hoisted_5$1,
            vue.withDirectives(vue.createElementVNode("input", {
              id: "a1",
              type: "checkbox",
              "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => $data.conf.autoDownload = $event)
            }, null, 512), [
              [vue.vModelCheckbox, $data.conf.autoDownload]
            ])
          ]),
          vue.createElementVNode("div", _hoisted_6$1, [
            _hoisted_7$1,
            vue.withDirectives(vue.createElementVNode("input", {
              id: "a2",
              type: "checkbox",
              "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => $data.conf.autoSave = $event)
            }, null, 512), [
              [vue.vModelCheckbox, $data.conf.autoSave]
            ])
          ]),
          vue.createElementVNode("div", _hoisted_8$1, [
            _hoisted_9$1,
            vue.withDirectives(vue.createElementVNode("input", {
              id: "a3",
              type: "checkbox",
              "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => $data.conf.autoClose = $event)
            }, null, 512), [
              [vue.vModelCheckbox, $data.conf.autoClose]
            ])
          ])
        ])
      ]),
      !$data.show ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_10$1, [
        vue.createElementVNode("div", {
          class: "big-btn",
          onClick: _cache[4] || (_cache[4] = (...args) => $options.save && $options.save(...args))
        }, _hoisted_12$1),
        vue.createElementVNode("div", {
          class: "big-btn",
          onClick: _cache[5] || (_cache[5] = ($event) => $data.show = !$data.show)
        }, [
          vue.createElementVNode("span", null, vue.toDisplayString(!$data.show ? "展开" : "收起") + "下载详情", 1)
        ])
      ])) : vue.createCommentVNode("", true),
      vue.withDirectives(vue.createElementVNode("div", _hoisted_13$1, [
        vue.createElementVNode("section", _hoisted_14$1, [
          vue.createElementVNode("input", {
            type: "text",
            value: $props.url,
            disabled: true,
            placeholder: "请输入 链接"
          }, null, 8, _hoisted_15$1),
          $data.progress === 0 ? (vue.openBlock(), vue.createElementBlock("div", {
            key: 0,
            class: "big-btn",
            onClick: _cache[6] || (_cache[6] = (...args) => $options.download && $options.download(...args))
          }, "下载")) : $data.progress === 100 ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_16$1, " 下载完成 ")) : vue.createCommentVNode("", true),
          $data.progress === 100 ? (vue.openBlock(), vue.createElementBlock("div", {
            key: 2,
            class: "big-btn",
            onClick: _cache[7] || (_cache[7] = (...args) => $options.save && $options.save(...args))
          }, _hoisted_18$1)) : vue.createCommentVNode("", true),
          vue.createElementVNode("div", {
            class: "big-btn detail",
            onClick: _cache[8] || (_cache[8] = ($event) => $data.show = !$data.show)
          }, [
            vue.createElementVNode("span", null, vue.toDisplayString(!$data.show ? "展开" : "收起") + "下载详情", 1)
          ])
        ]),
        vue.createElementVNode("div", _hoisted_19, "进度:" + vue.toDisplayString($data.progress) + "%", 1)
      ], 512), [
        [vue.vShow, $data.show]
      ])
    ]);
  }
  const Mp4Downloader = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render], ["__scopeId", "data-v-429419b2"]]);
  const _withScopeId = (n) => (vue.pushScopeId("data-v-a069d05a"), n = n(), vue.popScopeId(), n);
  const _hoisted_1 = { class: "content" };
  const _hoisted_2 = { class: "left" };
  const _hoisted_3 = { class: "big-title" };
  const _hoisted_4 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("path", {
    d: "M9 18V42H39V18L24 6L9 18Z",
    fill: "none",
    stroke: "#ffffff",
    "stroke-width": "4",
    "stroke-linecap": "round",
    "stroke-linejoin": "round"
  }, null, -1));
  const _hoisted_5 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("path", {
    d: "M19 29V42H29V29H19Z",
    fill: "none",
    stroke: "#ffffff",
    "stroke-width": "4",
    "stroke-linejoin": "round"
  }, null, -1));
  const _hoisted_6 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("path", {
    d: "M9 42H39",
    stroke: "#ffffff",
    "stroke-width": "4",
    "stroke-linecap": "round"
  }, null, -1));
  const _hoisted_7 = [
    _hoisted_4,
    _hoisted_5,
    _hoisted_6
  ];
  const _hoisted_8 = { class: "comments" };
  const _hoisted_9 = { class: "item" };
  const _hoisted_10 = { class: "title" };
  const _hoisted_11 = {
    key: 0,
    class: "quote"
  };
  const _hoisted_12 = { class: "replay" };
  const _hoisted_13 = { class: "video" };
  const _hoisted_14 = { class: "title" };
  const _hoisted_15 = {
    class: "author",
    target: "_blank"
  };
  const _hoisted_16 = ["href"];
  const _hoisted_17 = { class: "right" };
  const _hoisted_18 = { class: "big-title" };
  const _sfc_main = {
    __name: "App",
    setup(__props) {
      const dpRef = vue.ref(null);
      const dp = vue.ref(null);
      const recommendRef = vue.ref(null);
      const data = vue.reactive({
        comments: [],
        info: window.info,
        show: true
      });
      vue.watch(() => data.show, (n, o) => {
        var _a;
        if (n) {
          pauseOriginVideo();
          document.body.style.overflow = "hidden";
        } else {
          (_a = dp.value) == null ? void 0 : _a.pause();
          document.body.style.overflow = "unset";
        }
      }, { immediate: true });
      vue.onMounted(() => {
        recommendRef.value.append($("h3").next().next()[0]);
        $("br").remove();
        initComments();
        initVideo();
        checkOriginVideoIsPlay();
      });
      function pauseOriginVideo() {
        if ($(".vjs-playing").length) {
          $("#player_one_html5_api").click();
        }
      }
      function checkOriginVideoIsPlay() {
        setInterval(() => {
          if (data.show) {
            pauseOriginVideo();
          }
        }, 3e3);
      }
      function initComments() {
        $.ajax({
          url: `${location.origin}/show_comments2.php?VID=${data.info.video.vid}&start=1&comment_per_page=30`,
          success(r) {
            let h = $(r);
            h.each(function() {
              if (this.tagName === "TABLE") {
                let name = $(this).find("a:first").remove().text().trim();
                let time = $(this).find(".comment-info").contents()[4];
                let body = $(this).find(".comment-body");
                let quote = body.find(".comment_quote").text().replaceAll(" ", "").replaceAll("\n", "");
                body.find(".comment_quote").remove();
                let replay = body.find("a").remove().end().text().trim();
                if (replay.includes("请不要发广告或与视频无关的评论") || quote.includes("请不要发广告或与视频无关的评论"))
                  ;
                else {
                  data.comments.push({
                    name,
                    time: $(time).text(),
                    quote,
                    replay
                  });
                }
              }
            });
          }
        });
      }
      function initVideo() {
        if (!dpRef.value)
          return;
        let config = {
          url: data.info.video.url,
          type: data.info.video.type
        };
        if (config.type === "customHls") {
          config.customType = {
            customHls: function(video, player) {
              const hls = new _monkeyWindow.Hls();
              hls.loadSource(video.src);
              hls.attachMedia(video);
            }
          };
        }
        dp.value = new _monkeyWindow.DPlayer({
          container: dpRef.value,
          autoplay: true,
          video: config
        });
        dp.value.seek(10);
      }
      function goHome() {
        location.href = "https://91porn.com/index.php";
      }
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [
          vue.withDirectives(vue.createElementVNode("div", _hoisted_1, [
            vue.createElementVNode("div", _hoisted_2, [
              vue.createElementVNode("div", _hoisted_3, [
                vue.createTextVNode(" 评论 "),
                (vue.openBlock(), vue.createElementBlock("svg", {
                  onClick: goHome,
                  class: "home-icon",
                  width: "24",
                  height: "24",
                  viewBox: "0 0 48 48",
                  fill: "none",
                  xmlns: "http://www.w3.org/2000/svg"
                }, _hoisted_7))
              ]),
              vue.createElementVNode("div", _hoisted_8, [
                (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(data.comments, (item) => {
                  return vue.openBlock(), vue.createElementBlock("div", _hoisted_9, [
                    vue.createElementVNode("div", _hoisted_10, [
                      vue.createElementVNode("span", null, vue.toDisplayString(item.name), 1),
                      vue.createElementVNode("span", null, vue.toDisplayString(item.time), 1)
                    ]),
                    item.quote ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_11, vue.toDisplayString(item.quote), 1)) : vue.createCommentVNode("", true),
                    vue.createElementVNode("div", _hoisted_12, vue.toDisplayString(item.replay), 1)
                  ]);
                }), 256))
              ])
            ]),
            vue.createElementVNode("div", _hoisted_13, [
              vue.createElementVNode("div", {
                id: "dplayer",
                ref_key: "dpRef",
                ref: dpRef,
                style: { "height": "70vh" }
              }, null, 512),
              vue.createElementVNode("div", _hoisted_14, vue.toDisplayString(data.info.video.name), 1),
              vue.createElementVNode("div", _hoisted_15, " 添加时间: " + vue.toDisplayString(data.info.video.date), 1),
              data.info.author.name ? (vue.openBlock(), vue.createElementBlock("a", {
                key: 0,
                href: data.info.author.url,
                class: "author",
                target: "_blank"
              }, " 作者: " + vue.toDisplayString(data.info.author.name), 9, _hoisted_16)) : vue.createCommentVNode("", true),
              data.info.video.type === "auto" ? (vue.openBlock(), vue.createBlock(Mp4Downloader, {
                key: 1,
                name: data.info.video.name,
                url: data.info.video.url
              }, null, 8, ["name", "url"])) : (vue.openBlock(), vue.createBlock(Downloader, {
                key: 2,
                title: data.info.video.name,
                url: data.info.video.url
              }, null, 8, ["title", "url"]))
            ]),
            vue.createElementVNode("div", _hoisted_17, [
              vue.createElementVNode("div", _hoisted_18, [
                vue.createTextVNode(" 本月热播 "),
                vue.createElementVNode("div", {
                  class: "close",
                  onClick: _cache[0] || (_cache[0] = ($event) => data.show = !data.show)
                })
              ]),
              vue.createElementVNode("div", {
                class: "list",
                ref_key: "recommendRef",
                ref: recommendRef
              }, null, 512)
            ])
          ], 512), [
            [vue.vShow, data.show]
          ]),
          vue.withDirectives(vue.createElementVNode("div", {
            class: "showBtn",
            onClick: _cache[1] || (_cache[1] = ($event) => data.show = !data.show)
          }, "展开脚本", 512), [
            [vue.vShow, !data.show]
          ])
        ], 64);
      };
    }
  };
  const App = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-a069d05a"]]);
  window.info = {
    author: {
      name: "",
      url: "",
      vid: ""
    },
    video: {
      name: "",
      url: "",
      vid: ""
    }
    // videoUrl:'https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8',
  };
  function init() {
    if (window.info.videoUrl)
      return;
    const videoWrapper = $("#player_one_html5_api");
    const userWrapper = $(".title-yakov");
    if ((userWrapper == null ? void 0 : userWrapper.length) === 2) {
      let href = userWrapper[1].firstElementChild.href;
      let start = href.indexOf("UID");
      let uid = href.substring(start + 4);
      window.info.author = {
        name: userWrapper.find(".title").text(),
        url: `https://91porn.com/uvideos.php?UID=${uid}&type=public`,
        uid
      };
    }
    if (!videoWrapper[0]) {
      return;
    } else {
      window.info.video = {
        name: document.title.replace("Chinese homemade video", ""),
        url: videoWrapper.find("source")[0].src,
        vid: $("#favorite #VID").text(),
        date: $(".title-yakov")[0].innerText
      };
      if (window.info.video.url.toLowerCase().indexOf("m3u8") === -1) {
        window.info.video.type = "auto";
      } else {
        window.info.video.type = "customHls";
      }
      vue.createApp(App).mount(
        (() => {
          const app = document.createElement("div");
          document.body.append(app);
          return app;
        })()
      );
    }
    console.log(window.info);
  }
  try {
    let style2 = `
  .ad_img{display:none;}
  `;
    let addStyle2 = document.createElement("style");
    addStyle2.rel = "stylesheet";
    addStyle2.type = "text/css";
    addStyle2.innerHTML = style2;
    window.document.head.append(addStyle2);
    $("iframe").hide();
    let l = window.location;
    if (l.pathname === "/index.php") {
    } else if (l.pathname === "/view_video.php") {
      init();
    }
  } catch (e) {
    console.log("init报错了", e);
  }

})(Vue);