☰

91-plus

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

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

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

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==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);