☰

91-plus

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         91-plus
// @namespace    jiuyi
// @version      19
// @author       uh58fg
// @description  多线程下载91porn视频,跳过广告,清除页面广告。
// @license      WTFPL
// @icon         https://img-blog.csdnimg.cn/20181221195058594.gif
// @match        *.com/view_video.php?*
// @match        *.com/uvideos.php?*
// @match        *.com/index.php
// @require      https://cdnjs.cloudflare.com/ajax/libs/hls.js/8.0.0-beta.3/hls.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/dplayer/1.26.0/DPlayer.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.runtime.global.prod.js
// @connect      *
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_openInTab
// @grant        GM_setValue
// @grant        GM_xmlhttpRequest
// @grant        unsafeWindow
// @grant        window.close
// ==/UserScript==

(e=>{if(typeof GM_addStyle=="function"){GM_addStyle(e);return}const t=document.createElement("style");t.textContent=e,document.head.append(t)})(' .row2[data-v-1eda6002]{display:flex;justify-content:flex-end}.big-btn[data-v-1eda6002]{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-1eda6002]:hover{opacity:.9}.big-btn.disable[data-v-1eda6002]{cursor:default;background-color:#ddd;color:#000}.m-p-input-container[data-v-1eda6002]{display:flex;justify-content:flex-end}.m-p-tips[data-v-1eda6002]{width:100%;color:#999;text-align:left;font-style:italic;word-break:break-all}.m-p-tips.error-tips[data-v-1eda6002]{color:#dc5350}.info[data-v-1eda6002]{display:flex;justify-content:center;align-items:center;gap:1rem;margin-bottom:1rem}.info .options[data-v-1eda6002]{margin-right:1rem;display:flex;flex-direction:column;align-items:flex-end}.info .option[data-v-1eda6002]{cursor:pointer;display:flex;justify-content:center;align-items:center;color:#fff;margin-bottom:.3rem}.info .option label[data-v-1eda6002]{margin:0}.info .option input[data-v-1eda6002]{margin:0 0 0 1rem;width:1.4rem;height:1.4rem}.progress[data-v-1eda6002]{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;position:relative}.progress .bar[data-v-1eda6002]{height:100%;left:0;top:0;position:absolute;background:linear-gradient(90deg,#9CDCFF 0%,#0ACD76 100%);border-radius:.8rem}[data-v-fb31b46a]::-webkit-scrollbar{width:10px}[data-v-fb31b46a]::-webkit-scrollbar-thumb{background:#4e4e4e;border-radius:25px}.showBtn[data-v-fb31b46a]{cursor:pointer;position:fixed;top:1rem;right:1rem;color:#fff;z-index:99999;background:gray;padding:.6rem 1rem;border-radius:.3rem}.content[data-v-fb31b46a]{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-fb31b46a]{cursor:pointer;position:absolute;top:1rem;right:2rem;width:2rem;height:2rem;line-height:4rem;text-align:center;color:#fff}.content .close[data-v-fb31b46a]:before{position:absolute;content:"";width:.2rem;height:2rem;background:white;transform:rotate(45deg);top:calc(50% - .45rem);left:50%}.content .close[data-v-fb31b46a]:after{content:"";position:absolute;width:.2rem;height:2rem;background:white;transform:rotate(-45deg);top:calc(50% - .45rem);left:50%}.content .home-icon[data-v-fb31b46a]{cursor:pointer;right:2rem;position:absolute}.content .big-title[data-v-fb31b46a]{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-wrapper[data-v-fb31b46a]{width:60vw;overflow:auto;padding-bottom:2rem}.content .video-wrapper .my-video[data-v-fb31b46a],.content .video-wrapper #dplayer[data-v-fb31b46a]{width:100%;max-height:70vh}.content .video-wrapper .title[data-v-fb31b46a]{margin-top:1rem;font-weight:700;font-size:2rem;color:#fff;margin-bottom:1rem}.content .video-wrapper .author[data-v-fb31b46a]{margin-right:2rem;font-weight:700;font-size:1.4rem;margin-bottom:2rem;display:inline-block}.content .left[data-v-fb31b46a]{flex:1;border:1px solid gray;border-radius:.5rem;overflow:hidden;position:relative;font-size:1.8rem}.content .left .comments[data-v-fb31b46a]{color:#fff;height:calc(100% - 6rem);padding-bottom:4rem;overflow:auto}.content .left .comments .item[data-v-fb31b46a]{margin-bottom:5px;padding:10px;border-bottom:1px solid #3f3f3f;text-align:start}.content .left .comments .item .title[data-v-fb31b46a]{font-size:1.4rem}.content .left .comments .item .title span[data-v-fb31b46a]{margin-right:10px}.content .left .comments .item .quote[data-v-fb31b46a]{margin-top:.5rem;font-size:1.8rem;padding:.5rem .5rem .5rem 1rem;border:1px dashed gray}.content .right[data-v-fb31b46a]{flex:1;border:1px solid gray;border-radius:.5rem;overflow:hidden}.content .right .list[data-v-fb31b46a]{color:#fff;height:calc(100% - 6rem);padding:1rem 1rem 4rem;overflow:auto}.ml20[data-v-fb31b46a]{margin-left:20px} ');

(function (vue) {
  'use strict';

  var _GM_getValue = /* @__PURE__ */ (() => typeof GM_getValue != "undefined" ? GM_getValue : void 0)();
  var _GM_openInTab = /* @__PURE__ */ (() => typeof GM_openInTab != "undefined" ? GM_openInTab : void 0)();
  var _GM_setValue = /* @__PURE__ */ (() => typeof GM_setValue != "undefined" ? GM_setValue : void 0)();
  var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)();
  var _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)();
  var _monkeyWindow = /* @__PURE__ */ (() => window)();
  const page = _unsafeWindow || window;
  const PageMediaSource = page.MediaSource;
  const PageURL = page.URL;
  const mediaSourceMap = /* @__PURE__ */ new Map();
  const objectURLMap = /* @__PURE__ */ new Map();
  const endListeners = /* @__PURE__ */ new Set();
  let originCreateObjectURL = null;
  let originAddSourceBuffer = null;
  let originEndOfStream = null;
  let inited = false;
  function copyBuffer(data) {
    try {
      if (data instanceof ArrayBuffer)
        return data.slice(0);
      if (ArrayBuffer.isView(data)) {
        return new Uint8Array(data.buffer, data.byteOffset, data.byteLength).slice().buffer;
      }
    } catch (e) {
    }
    return null;
  }
  function parseMime(mimeCodecs) {
    const raw = String(mimeCodecs || "");
    const [essence = "video/mp4", codecsPart = ""] = raw.split(";");
    const [type = "video", format = "mp4"] = essence.trim().split("/");
    return {
      type: type || "video",
      format: format || "mp4",
      codecs: codecsPart.trim().replace(/codecs=/i, "").replace(/["']/g, "")
    };
  }
  function rememberObjectUrl(object, objectURL) {
    if (!(PageMediaSource && object instanceof PageMediaSource) || objectURLMap.has(object))
      return;
    object.__objURL__ = objectURL;
    objectURLMap.set(object, objectURL);
  }
  function ensureInfo(mediaSource) {
    let info = mediaSourceMap.get(mediaSource);
    if (info)
      return info;
    info = {
      mediaSource,
      sourceBuffer: [],
      endOfStream: false,
      mediaUrl: ""
    };
    mediaSourceMap.set(mediaSource, info);
    return info;
  }
  function proxyMediaSource() {
    if (!originAddSourceBuffer || !originEndOfStream || !originCreateObjectURL)
      return;
    PageURL.createObjectURL = new Proxy(originCreateObjectURL, {
      apply(target, ctx, args) {
        const objectURL = Reflect.apply(target, ctx, args);
        rememberObjectUrl(args[0], objectURL);
        return objectURL;
      }
    });
    PageMediaSource.prototype.addSourceBuffer = new Proxy(originAddSourceBuffer, {
      apply(target, ctx, args) {
        const info = ensureInfo(ctx);
        const mimeCodecs = args[0] || "";
        const sourceBuffer = Reflect.apply(target, ctx, args);
        const sourceBufferItem = {
          mimeCodecs,
          bufferData: [],
          mediaInfo: parseMime(mimeCodecs)
        };
        info.sourceBuffer.push(sourceBufferItem);
        const originAppendBuffer = sourceBuffer.appendBuffer;
        sourceBuffer.appendBuffer = new Proxy(originAppendBuffer, {
          apply(bufTarget, bufCtx, bufArgs) {
            const buffer = bufArgs[0];
            if (!info.endOfStream) {
              const copied = copyBuffer(buffer);
              if (copied)
                sourceBufferItem.bufferData.push(copied);
            }
            const objectURL = objectURLMap.get(ctx);
            if (objectURL)
              info.mediaUrl = objectURL;
            if (!mediaSourceMap.has(ctx))
              mediaSourceMap.set(ctx, info);
            return Reflect.apply(bufTarget, bufCtx, bufArgs);
          }
        });
        return sourceBuffer;
      }
    });
    PageMediaSource.prototype.endOfStream = new Proxy(originEndOfStream, {
      apply(target, ctx, args) {
        const info = mediaSourceMap.get(ctx);
        if (info && !info.endOfStream) {
          info.endOfStream = true;
          const objectURL = objectURLMap.get(ctx);
          if (objectURL)
            info.mediaUrl = objectURL;
          endListeners.forEach((listener) => {
            try {
              listener(info);
            } catch (e) {
            }
          });
        }
        return Reflect.apply(target, ctx, args);
      }
    });
  }
  function initMediaSource() {
    if (inited)
      return true;
    if (!PageMediaSource || !(PageURL == null ? void 0 : PageURL.createObjectURL)) {
      return false;
    }
    originCreateObjectURL = PageURL.createObjectURL;
    originAddSourceBuffer = PageMediaSource.prototype.addSourceBuffer;
    originEndOfStream = PageMediaSource.prototype.endOfStream;
    proxyMediaSource();
    inited = true;
    return true;
  }
  function onMediaEnd(listener) {
    endListeners.add(listener);
    return () => endListeners.delete(listener);
  }
  function findMediaSource(mediaEl) {
    if (!mediaEl)
      return null;
    const curSrc = mediaEl.currentSrc || mediaEl.src;
    if (!curSrc)
      return null;
    for (const info of mediaSourceMap.values()) {
      const objectURL = info.mediaSource.__objURL__ || info.mediaUrl;
      if (objectURL && objectURL === curSrc)
        return info;
    }
    return null;
  }
  function safeFilename(value) {
    const normalized = Array.from(String(value || "video"), (character) => {
      const codePoint = character.codePointAt(0) ?? 0;
      return codePoint < 32 || '<>:"/\\|?*'.includes(character) ? " " : character;
    }).join("").replace(/\s+/g, " ").trim();
    return (normalized || "video").slice(0, 180);
  }
  function collectMediaFiles(mediaEl, title) {
    const info = findMediaSource(mediaEl);
    if (!info)
      return [];
    const readyBuffers = info.sourceBuffer.filter((item) => item.bufferData.length > 0);
    const files = [];
    readyBuffers.forEach((item) => {
      const kind = item.mediaInfo.type || "video";
      const ext = item.mediaInfo.format || "mp4";
      const base = safeFilename(title || document.title || "video");
      const filename = readyBuffers.length > 1 ? `${base}_${kind}.${ext}` : `${base}.${ext}`;
      const blob = new Blob(item.bufferData, { type: kind === "audio" ? "audio/mp4" : "video/mp4" });
      files.push({
        filename,
        blob,
        url: PageURL.createObjectURL(blob)
      });
      item.bufferData = [];
    });
    return files;
  }
  const _export_sfc = (sfc, props) => {
    const target = sfc.__vccOpts || sfc;
    for (const [key, val] of props) {
      target[key] = val;
    }
    return target;
  };
  const _hoisted_1$1 = { class: "video-downloader" };
  const _hoisted_2$1 = { class: "info" };
  const _hoisted_3$1 = { class: "options" };
  const _hoisted_4$1 = { class: "option" };
  const _hoisted_5$1 = { class: "option" };
  const _hoisted_6$1 = { class: "option" };
  const _hoisted_7$1 = { class: "wrapper" };
  const _hoisted_8$1 = { class: "m-p-input-container" };
  const _hoisted_9$1 = {
    key: 1,
    class: "big-btn disable"
  };
  const _hoisted_10$1 = {
    key: 2,
    class: "big-btn disable"
  };
  const CONF_KEY = "porn-plus";
  const _sfc_main$1 = {
    __name: "video-downloader",
    props: {
      title: { type: String, default: "" },
      url: { type: String, default: "" },
      mediaType: { type: String, default: "" },
      getMedia: { type: Function, required: true }
    },
    setup(__props) {
      const props = __props;
      const pageURL = (_unsafeWindow == null ? void 0 : _unsafeWindow.URL) || URL;
      const conf = vue.reactive({
        autoDownload: true,
        autoSave: true,
        autoClose: false
      });
      const progress = vue.ref(0);
      const show = vue.ref(false);
      const tips = vue.ref("等待开始下载");
      const phase = vue.ref("idle");
      let armed = false;
      let directStarted = false;
      let collected = false;
      let saving = false;
      let pollTimer = 0;
      let files = [];
      let stopEndListen = () => {
      };
      vue.watch(conf, (value) => {
        localStorage.setItem(CONF_KEY, JSON.stringify(value));
      }, { deep: true });
      vue.watch(progress, (value) => {
        document.title = `${Number(value).toFixed(0)}% ${props.title}`;
      });
      vue.watch(() => conf.autoDownload, (enabled) => {
        if (enabled && phase.value === "idle")
          start();
      });
      function loadConf() {
        const raw = localStorage.getItem(CONF_KEY);
        if (!raw)
          return;
        try {
          Object.assign(conf, JSON.parse(raw));
        } catch (e) {
        }
      }
      function safeFilename2(value) {
        const normalized = Array.from(String(value || "video"), (character) => {
          const codePoint = character.codePointAt(0) ?? 0;
          return codePoint < 32 || '<>:"/\\|?*'.includes(character) ? " " : character;
        }).join("").replace(/\s+/g, " ").trim();
        return (normalized || "video").slice(0, 180);
      }
      function filenameFromTitle(ext = "mp4") {
        var _a, _b;
        const base = safeFilename2(((_b = (_a = window.info) == null ? void 0 : _a.video) == null ? void 0 : _b.name) || "video").replace(new RegExp(`\\.${ext}$`, "i"), "");
        return `${base || "video"}.${ext}`;
      }
      function isHls() {
        if (props.mediaType === "customHls")
          return true;
        return String(props.url || "").toLowerCase().includes("m3u8");
      }
      function clickDownload(url, filename) {
        const doc = (_unsafeWindow == null ? void 0 : _unsafeWindow.document) || document;
        const anchor = doc.createElement("a");
        anchor.href = url;
        anchor.download = filename;
        doc.body.appendChild(anchor);
        anchor.click();
        anchor.remove();
      }
      function closePage() {
        window.opener = null;
        window.open("", "_self", "");
        window.close();
      }
      function releaseFilesLater() {
        const pending = files.slice();
        setTimeout(() => {
          pending.forEach((file) => {
            if (String(file.url).startsWith("blob:"))
              pageURL.revokeObjectURL(file.url);
          });
        }, 6e4);
      }
      function downloadName(filename) {
        const ext = (String(filename || "").match(/\.([a-z0-9]{2,5})$/i) || [])[1] || "mp4";
        return filenameFromTitle(ext);
      }
      function requestBytes(url) {
        let fetchUrl = url;
        if (url.startsWith("http://") && location.href.startsWith("https://")) {
          fetchUrl = url.replace("http://", "https://");
        }
        return new Promise((resolve, reject) => {
          _GM_xmlhttpRequest({
            method: "GET",
            url: fetchUrl,
            responseType: "arraybuffer",
            onprogress(event) {
              if (event.lengthComputable && event.total) {
                progress.value = Math.min(99, event.loaded / event.total * 100);
              }
            },
            onload(response) {
              if (response.status >= 200 && response.status < 300 && response.response) {
                resolve(response.response);
                return;
              }
              reject(new Error(String(response.status || "request failed")));
            },
            onerror(error) {
              reject(error || new Error("request failed"));
            },
            ontimeout() {
              reject(new Error("timeout"));
            }
          });
        });
      }
      function namedObjectUrl(bytes, filename) {
        const page2 = _unsafeWindow || window;
        const source = new Uint8Array(bytes);
        const copy = new page2.Uint8Array(source.byteLength);
        const chunkSize = 2 * 1024 * 1024;
        for (let offset = 0; offset < source.byteLength; offset += chunkSize) {
          copy.set(source.subarray(offset, Math.min(source.byteLength, offset + chunkSize)), offset);
        }
        const type = filename.toLowerCase().endsWith(".m4a") || filename.toLowerCase().endsWith(".mp3") ? "audio/mp4" : "video/mp4";
        const file = new page2.File([copy], filename, { type });
        return page2.URL.createObjectURL(file);
      }
      async function saveNamed(file, filename) {
        const url = String(file.url || "");
        if (url.startsWith("blob:")) {
          clickDownload(url, filename);
          return;
        }
        const bytes = await requestBytes(url);
        clickDownload(namedObjectUrl(bytes, filename), filename);
      }
      async function save() {
        if (!files.length || phase.value === "saved" || saving)
          return;
        saving = true;
        tips.value = "正在保存";
        try {
          for (const file of files) {
            const filename = downloadName(file.filename);
            tips.value = `正在保存 ${filename}`;
            try {
              await saveNamed(file, filename);
            } catch (error) {
              console.error(error);
              if (!file.url || String(file.url).startsWith("blob:"))
                throw error;
              clickDownload(file.url, filename);
            }
          }
          phase.value = "saved";
          progress.value = 100;
          tips.value = "已开始保存";
          document.title = `下载完成 ${props.title}`;
          releaseFilesLater();
          if (conf.autoClose)
            setTimeout(closePage, 1500);
        } catch (error) {
          console.error(error);
          phase.value = "error";
          tips.value = "保存失败";
        } finally {
          saving = false;
        }
      }
      function finishWithFiles(nextFiles) {
        if (collected) {
          if (conf.autoSave)
            save();
          return;
        }
        if (!nextFiles.length) {
          phase.value = "error";
          tips.value = "没有可保存的媒体数据";
          stopPolling();
          return;
        }
        collected = true;
        files = nextFiles;
        progress.value = 100;
        phase.value = "ready";
        tips.value = conf.autoSave ? "媒体已就绪,正在保存" : "媒体已就绪,点击保存";
        document.title = `下载完成 ${props.title}`;
        stopPolling();
        if (conf.autoSave)
          save();
      }
      function updateBufferedProgress(video) {
        if (!Number.isFinite(video.duration) || video.duration <= 0 || !video.buffered.length)
          return;
        const end = video.buffered.end(video.buffered.length - 1);
        progress.value = Math.min(99, Math.max(progress.value, end / video.duration * 100));
      }
      function tryCollect(video) {
        var _a, _b;
        if (collected || directStarted)
          return;
        const info = findMediaSource(video);
        if (!info) {
          tips.value = "等待播放器建立媒体流";
          return;
        }
        if (!info.endOfStream) {
          tips.value = "媒体缓冲中,就绪后自动保存";
          updateBufferedProgress(video);
          return;
        }
        finishWithFiles(collectMediaFiles(video, ((_b = (_a = window.info) == null ? void 0 : _a.video) == null ? void 0 : _b.name) || ""));
      }
      function startDirect(src) {
        if (directStarted)
          return;
        directStarted = true;
        stopPolling();
        collected = true;
        files = [{ url: src, filename: filenameFromTitle("mp4") }];
        phase.value = "ready";
        tips.value = conf.autoSave ? "开始保存" : "点击保存";
        if (conf.autoSave)
          save();
      }
      function tick() {
        if (!armed || collected)
          return;
        const video = props.getMedia();
        if (!video)
          return;
        const src = video.currentSrc || video.src;
        if (!src)
          return;
        if (src.startsWith("blob:") || src.toLowerCase().includes("m3u8")) {
          if (src.startsWith("blob:"))
            tryCollect(video);
          else
            tips.value = "等待播放器建立媒体流";
          return;
        }
        startDirect(src);
      }
      function stopPolling() {
        if (pollTimer) {
          clearInterval(pollTimer);
          pollTimer = 0;
        }
      }
      function start() {
        var _a;
        if (phase.value === "saved")
          return;
        if (phase.value === "ready") {
          if (conf.autoSave)
            save();
          return;
        }
        if (armed && phase.value === "waiting")
          return;
        if (phase.value === "error") {
          armed = false;
          directStarted = false;
          collected = false;
        }
        armed = true;
        phase.value = "waiting";
        tips.value = "正在准备下载";
        if (!isHls() && props.url) {
          startDirect(props.url);
          return;
        }
        const video = props.getMedia();
        if (video && ((_a = findMediaSource(video)) == null ? void 0 : _a.endOfStream)) {
          tryCollect(video);
          return;
        }
        if (!pollTimer)
          pollTimer = setInterval(tick, 300);
        tick();
      }
      vue.onMounted(() => {
        initMediaSource();
        loadConf();
        stopEndListen = onMediaEnd((info) => {
          if (!armed || collected || directStarted)
            return;
          const video = props.getMedia();
          if (!video)
            return;
          const src = video.currentSrc || video.src;
          const objectURL = info.mediaSource.__objURL__ || info.mediaUrl;
          if (src && objectURL === src)
            tryCollect(video);
        });
        if (conf.autoDownload)
          start();
      });
      vue.onBeforeUnmount(() => {
        stopPolling();
        stopEndListen();
        if (phase.value !== "saved") {
          files.forEach((file) => {
            if (String(file.url).startsWith("blob:"))
              pageURL.revokeObjectURL(file.url);
          });
        }
      });
      return (_ctx, _cache) => {
        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) => show.value = !show.value)
            }, [
              vue.createElementVNode("div", {
                class: "bar",
                style: vue.normalizeStyle({ width: progress.value + "%" })
              }, null, 4)
            ]),
            vue.createElementVNode("div", _hoisted_3$1, [
              vue.createElementVNode("div", _hoisted_4$1, [
                _cache[4] || (_cache[4] = vue.createElementVNode("label", { for: "a1" }, "自动下载", -1)),
                vue.withDirectives(vue.createElementVNode("input", {
                  id: "a1",
                  type: "checkbox",
                  "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => conf.autoDownload = $event)
                }, null, 512), [
                  [vue.vModelCheckbox, conf.autoDownload]
                ])
              ]),
              vue.createElementVNode("div", _hoisted_5$1, [
                _cache[5] || (_cache[5] = vue.createElementVNode("label", { for: "a2" }, "下载完成后自动保存", -1)),
                vue.withDirectives(vue.createElementVNode("input", {
                  id: "a2",
                  type: "checkbox",
                  "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => conf.autoSave = $event)
                }, null, 512), [
                  [vue.vModelCheckbox, conf.autoSave]
                ])
              ]),
              vue.createElementVNode("div", _hoisted_6$1, [
                _cache[6] || (_cache[6] = vue.createElementVNode("label", { for: "a3" }, "保存后自动关闭", -1)),
                vue.withDirectives(vue.createElementVNode("input", {
                  id: "a3",
                  type: "checkbox",
                  "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => conf.autoClose = $event)
                }, null, 512), [
                  [vue.vModelCheckbox, conf.autoClose]
                ])
              ])
            ])
          ]),
          vue.createElementVNode("div", _hoisted_7$1, [
            vue.createElementVNode("section", _hoisted_8$1, [
              phase.value === "idle" || phase.value === "error" ? (vue.openBlock(), vue.createElementBlock("div", {
                key: 0,
                class: "big-btn",
                onClick: start
              }, "下载")) : phase.value === "waiting" ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_9$1, "准备中")) : phase.value === "saved" ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_10$1, "下载完成")) : vue.createCommentVNode("", true),
              phase.value === "ready" ? (vue.openBlock(), vue.createElementBlock("div", {
                key: 3,
                class: "big-btn",
                onClick: save
              }, [..._cache[7] || (_cache[7] = [
                vue.createElementVNode("span", null, "保存", -1)
              ])])) : vue.createCommentVNode("", true)
            ]),
            vue.createElementVNode("div", {
              class: vue.normalizeClass(["m-p-tips", { "error-tips": phase.value === "error" }])
            }, " 进度:" + vue.toDisplayString(Number(progress.value).toFixed(0)) + "%," + vue.toDisplayString(tips.value), 3)
          ])
        ]);
      };
    }
  };
  const VideoDownloader = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-1eda6002"]]);
  const _hoisted_1 = { class: "content" };
  const _hoisted_2 = { class: "left" };
  const _hoisted_3 = { class: "big-title" };
  const _hoisted_4 = { class: "comments" };
  const _hoisted_5 = { class: "item" };
  const _hoisted_6 = { class: "title" };
  const _hoisted_7 = ["href"];
  const _hoisted_8 = {
    key: 0,
    class: "quote"
  };
  const _hoisted_9 = { class: "replay" };
  const _hoisted_10 = { class: "video-wrapper" };
  const _hoisted_11 = ["src"];
  const _hoisted_12 = { class: "title" };
  const _hoisted_13 = {
    class: "author",
    target: "_blank"
  };
  const _hoisted_14 = ["href"];
  const _hoisted_15 = { class: "right" };
  const _hoisted_16 = { class: "big-title" };
  const _sfc_main = {
    __name: "App",
    setup(__props) {
      const dpRef = vue.ref(null);
      const dp = vue.ref(null);
      const h5Video = 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 a = $(this).find("a:first");
                let name = a.text().trim();
                let url = a[0].href + "&type=public";
                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,
                    url
                  });
                }
              }
            });
          }
        });
      }
      function initVideo() {
        let config = {
          url: data.info.video.url,
          type: data.info.video.type
        };
        if (config.type === "customHls") {
          if (!dpRef.value)
            return;
          config.customType = {
            customHls: function(video, player) {
              const hls = new _monkeyWindow.Hls({
                autoStartLoad: true,
                startPosition: 0,
                // 后台标签页不会自动播放,默认只向前缓冲约 30 秒,整片永远等不到结束
                maxBufferLength: 60 * 60 * 24,
                maxMaxBufferLength: 60 * 60 * 24,
                maxBufferSize: 1024 * 1024 * 1024,
                startFragPrefetch: true
              });
              hls.loadSource(video.src);
              hls.attachMedia(video);
              hls.startLoad(0);
            }
          };
          dp.value = new _monkeyWindow.DPlayer({
            container: dpRef.value,
            autoplay: true,
            video: config
          });
          if (!document.hidden) {
            dp.value.seek(10);
          }
        } else {
          if (h5Video.value) {
            h5Video.value.currentTime = 10;
          }
        }
      }
      function goHome() {
        location.href = "https://91porn.com/index.php";
      }
      function getPlayingVideo() {
        var _a, _b;
        if (data.info.video.type === "customHls") {
          return ((_a = dp.value) == null ? void 0 : _a.video) || ((_b = dpRef.value) == null ? void 0 : _b.querySelector("video")) || null;
        }
        return h5Video.value;
      }
      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, [
                _cache[3] || (_cache[3] = vue.createTextVNode(" 评论 ", -1)),
                (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"
                }, [..._cache[2] || (_cache[2] = [
                  vue.createElementVNode("path", {
                    d: "M9 18V42H39V18L24 6L9 18Z",
                    fill: "none",
                    stroke: "#ffffff",
                    "stroke-width": "4",
                    "stroke-linecap": "round",
                    "stroke-linejoin": "round"
                  }, null, -1),
                  vue.createElementVNode("path", {
                    d: "M19 29V42H29V29H19Z",
                    fill: "none",
                    stroke: "#ffffff",
                    "stroke-width": "4",
                    "stroke-linejoin": "round"
                  }, null, -1),
                  vue.createElementVNode("path", {
                    d: "M9 42H39",
                    stroke: "#ffffff",
                    "stroke-width": "4",
                    "stroke-linecap": "round"
                  }, null, -1)
                ])]))
              ]),
              vue.createElementVNode("div", _hoisted_4, [
                (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(data.comments, (item) => {
                  return vue.openBlock(), vue.createElementBlock("div", _hoisted_5, [
                    vue.createElementVNode("div", _hoisted_6, [
                      vue.createElementVNode("a", {
                        href: item.url,
                        target: "_blank"
                      }, vue.toDisplayString(item.name), 9, _hoisted_7),
                      vue.createElementVNode("span", null, vue.toDisplayString(item.time), 1)
                    ]),
                    item.quote ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_8, vue.toDisplayString(item.quote), 1)) : vue.createCommentVNode("", true),
                    vue.createElementVNode("div", _hoisted_9, vue.toDisplayString(item.replay), 1)
                  ]);
                }), 256))
              ])
            ]),
            vue.createElementVNode("div", _hoisted_10, [
              vue.createElementVNode("div", {
                id: "dplayer",
                ref_key: "dpRef",
                ref: dpRef
              }, null, 512),
              vue.createElementVNode("video", {
                ref_key: "h5Video",
                ref: h5Video,
                src: data.info.video.url,
                controls: "",
                preload: "",
                autoplay: "",
                class: "my-video"
              }, null, 8, _hoisted_11),
              vue.createElementVNode("div", _hoisted_12, vue.toDisplayString(data.info.video.name), 1),
              vue.createElementVNode("div", _hoisted_13, " 添加时间: " + 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_14)) : vue.createCommentVNode("", true),
              vue.createElementVNode("div", null, vue.toDisplayString(data.info.author.desc), 1),
              vue.createVNode(VideoDownloader, {
                title: data.info.video.name,
                url: data.info.video.url,
                "media-type": data.info.video.type,
                "get-media": getPlayingVideo
              }, null, 8, ["title", "url", "media-type"])
            ]),
            vue.createElementVNode("div", _hoisted_15, [
              vue.createElementVNode("div", _hoisted_16, [
                _cache[4] || (_cache[4] = vue.createTextVNode(" 本月热播 ", -1)),
                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-fb31b46a"]]);
  initMediaSource();
  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(),
        desc: $("#v_desc").text().replace("91版主提醒您:不要下载任何app,它们可能是诈骗app。如果被骗了,请联系我们,全面打击诈骗", ""),
        url: `https://91porn.com/uvideos.php?UID=${uid}&type=public`,
        uid
      };
      openAuthorHomeOnceToday();
    }
    if (!videoWrapper[0]) {
      document.title = "作为游客,你每天只可观看15个视频";
    } else {
      window.info.video = {
        name: document.title.replace(/Chinese homemade video/gi, "").trim(),
        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);
  }
  const AUTHOR_HOME_KEY = "author-home-opened";
  function todayText() {
    const date = /* @__PURE__ */ new Date();
    const month = String(date.getMonth() + 1).padStart(2, "0");
    const day = String(date.getDate()).padStart(2, "0");
    return `${date.getFullYear()}-${month}-${day}`;
  }
  function openedAuthorsToday() {
    const today = todayText();
    const stored = _GM_getValue(AUTHOR_HOME_KEY, {});
    const opened = {};
    if (stored && typeof stored === "object") {
      Object.entries(stored).forEach(([uid, date]) => {
        if (date === today)
          opened[uid] = date;
      });
    }
    return opened;
  }
  function markAuthorOpened(uid) {
    if (!uid)
      return;
    const opened = openedAuthorsToday();
    opened[uid] = todayText();
    _GM_setValue(AUTHOR_HOME_KEY, opened);
  }
  function authorUid() {
    try {
      return new URL(window.info.author.url, location.href).searchParams.get("UID") || "";
    } catch (e) {
      return String(window.info.author.uid || "").split("&")[0];
    }
  }
  function openAuthorHomeOnceToday() {
    const uid = authorUid();
    if (!uid || openedAuthorsToday()[uid])
      return;
    markAuthorOpened(uid);
    _GM_openInTab(`${location.origin}/uvideos.php?UID=${encodeURIComponent(uid)}&type=public`, {
      active: false,
      insert: true,
      setParent: true
    });
  }
  function listVideoUrls() {
    const seen = /* @__PURE__ */ new Set();
    const urls = [];
    document.querySelectorAll('a[href*="view_video.php"]').forEach((anchor) => {
      let url;
      try {
        url = new URL(anchor.href, location.href);
      } catch (e) {
        return;
      }
      if (!url.pathname.endsWith("/view_video.php"))
        return;
      const viewkey = url.searchParams.get("viewkey");
      if (!viewkey || seen.has(viewkey))
        return;
      seen.add(viewkey);
      urls.push(url.href);
    });
    return urls;
  }
  function pagingUrl(offset) {
    var _a, _b;
    const nav = document.querySelector("div.pagingnav");
    if (!nav)
      return "";
    const current = Number((_b = (_a = nav.querySelector("span.pagingnav")) == null ? void 0 : _a.textContent) == null ? void 0 : _b.trim());
    if (!Number.isInteger(current))
      return "";
    const target = current + offset;
    if (target < 1)
      return "";
    const link = [...nav.querySelectorAll("a")].find((anchor) => {
      try {
        return Number(new URL(anchor.href, location.href).searchParams.get("page")) === target;
      } catch (e) {
        return false;
      }
    });
    return link ? new URL(link.href, location.href).href : "";
  }
  function nextPageUrl() {
    return pagingUrl(1);
  }
  function bindListPagingKeys() {
    window.addEventListener("keydown", (event) => {
      if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey)
        return;
      if (event.key !== "ArrowLeft" && event.key !== "ArrowRight")
        return;
      const target = event.target;
      if (target && (target.isContentEditable || /^(input|textarea|select)$/i.test(target.tagName)))
        return;
      const url = pagingUrl(event.key === "ArrowLeft" ? -1 : 1);
      if (!url)
        return;
      event.preventDefault();
      event.stopPropagation();
      location.href = url;
    }, true);
  }
  function previewMp4Url(overlay) {
    var _a, _b, _c, _d;
    const id = ((_a = (overlay.id || "").match(/playvthumb_(\d+)/)) == null ? void 0 : _a[1]) || ((_d = (_c = (_b = overlay.querySelector("img")) == null ? void 0 : _b.src) == null ? void 0 : _c.match(/\/(\d+)\.jpe?g/i)) == null ? void 0 : _d[1]);
    if (!id)
      return "";
    return `https://vthumb.killcovid2021.com/thumb/${id}.mp4`;
  }
  function mountListPreview(overlay) {
    if (overlay.querySelector("video.plus-preview"))
      return;
    const img = overlay.querySelector("img");
    const src = previewMp4Url(overlay);
    if (!src)
      return;
    const video = document.createElement("video");
    video.className = "plus-preview img-responsive";
    video.muted = true;
    video.defaultMuted = true;
    video.loop = true;
    video.autoplay = true;
    video.playsInline = true;
    video.preload = "auto";
    video.src = src;
    const place = () => {
      if (!img)
        return;
      video.style.left = `${img.offsetLeft}px`;
      video.style.top = `${img.offsetTop}px`;
      video.style.width = `${img.offsetWidth}px`;
      video.style.height = `${img.offsetHeight}px`;
    };
    video.addEventListener("loadedmetadata", place);
    video.addEventListener("playing", () => {
      place();
      if (img)
        img.style.opacity = "0";
    });
    video.addEventListener("error", () => {
      video.remove();
      if (img)
        img.style.opacity = "";
    });
    overlay.insertBefore(video, overlay.firstChild);
    place();
    if (img && !img.complete)
      img.addEventListener("load", place, { once: true });
    video.play().catch(() => {
    });
  }
  function autoPlayListPreviews() {
    document.querySelectorAll(".thumb-overlay").forEach((overlay) => {
      mountListPreview(overlay);
      overlay.addEventListener("mouseleave", () => {
        setTimeout(() => mountListPreview(overlay), 50);
      });
    });
  }
  function durationSeconds(text) {
    const parts = String(text || "").trim().split(":").map((part) => Number(part));
    if (!parts.length || parts.some((part) => !Number.isFinite(part)))
      return 0;
    return parts.reduce((total, part) => total * 60 + part, 0);
  }
  function sortListByDuration() {
    const cards = [];
    document.querySelectorAll(".well .duration").forEach((el) => {
      var _a;
      const card = (_a = el.closest(".well")) == null ? void 0 : _a.parentElement;
      if (card && !cards.includes(card))
        cards.push(card);
    });
    if (cards.length < 2)
      return;
    const parent = cards[0].parentElement;
    if (!parent)
      return;
    cards.sort((a, b) => {
      var _a, _b;
      return durationSeconds((_a = b.querySelector(".duration")) == null ? void 0 : _a.textContent) - durationSeconds((_b = a.querySelector(".duration")) == null ? void 0 : _b.textContent);
    });
    cards.forEach((card) => parent.append(card));
  }
  function initListDownload() {
    markAuthorOpened(new URL(location.href).searchParams.get("UID") || "");
    sortListByDuration();
    bindListPagingKeys();
    autoPlayListPreviews();
    const button = document.createElement("button");
    button.type = "button";
    button.textContent = "下载本页全部视频";
    button.style.cssText = [
      "position:fixed",
      "top:1rem",
      "right:1rem",
      "z-index:99999",
      "cursor:pointer",
      "color:#fff",
      "background:#3D8AC7",
      "border:0",
      "border-radius:.3rem",
      "padding:.6rem 1rem",
      "font-size:1.6rem"
    ].join(";");
    button.addEventListener("click", () => {
      const urls = listVideoUrls();
      if (!urls.length) {
        button.textContent = "没有找到视频";
        return;
      }
      urls.forEach((url) => {
        _GM_openInTab(url, { active: false, insert: true, setParent: true });
      });
      const nextUrl = nextPageUrl();
      if (!nextUrl) {
        button.textContent = `已打开 ${urls.length} 个视频,已经是最后一页`;
        return;
      }
      button.textContent = `已打开 ${urls.length} 个视频,正在前往下一页`;
      location.href = nextUrl;
    });
    document.body.append(button);
  }
  try {
    let style2 = `
  .ad_img{display:none;}
  .thumb-overlay{position:relative;}
  .thumb-overlay video.plus-preview{position:absolute;object-fit:contain;z-index:1;pointer-events:none;margin:0;}
  .thumb-overlay .hd-text-icon,.thumb-overlay .duration{z-index:2;}
  `;
    let addStyle2 = document.createElement("style");
    addStyle2.rel = "stylesheet";
    addStyle2.type = "text/css";
    addStyle2.innerHTML = style2;
    document.head.append(addStyle2);
    $("iframe").hide();
    let l = window.location;
    if (l.pathname === "/index.php") {
    } else if (l.pathname === "/uvideos.php") {
      initListDownload();
    } else if (l.pathname === "/view_video.php") {
      init();
    }
  } catch (e) {
    console.log("init报错了", e);
  }

})(Vue);