Greasy Fork

Greasy Fork is available in English.

Hua VPlayer

A video player for V.qq.com

当前为 2024-12-24 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Hua VPlayer
// @namespace    npm/vite-plugin-monkey
// @version      0.0.2.92
// @author       monkey
// @description  A video player for V.qq.com
// @license      MIT
// @icon         https://vitejs.dev/logo.svg
// @match        *://v.qq.com/*
// @match        *://*.v.qq.com/*
// @require      https://cdn.jsdelivr.net/npm/[email protected]/umd/react.production.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/umd/react-dom.production.min.js
// @grant        none
// ==/UserScript==

(function(e){const o=document.createElement("style");o.id="monkey-hua-vplayer-popup-style-css",o.textContent=e,document.head.appendChild(o)})(" #root{max-width:1280px;margin:0 auto;padding:2rem;text-align:center}.App{--block-c-value: 0 0 0;--block-color: rgba(0, 0, 0, .8);--white-color: rgba(255, 255, 255, 1);--green-color: rgba(67, 231, 0, 1);--yellow-color: rgba(215, 127, 47, 1);--blue-color: rgba(0, 154, 255, 1);--card-top: 27px;--card-left: 105px}.App ul{list-style:none;padding:0;margin:0}.App button{border:none;border-radius:4px}.App .card{position:fixed;top:var(--card-top);left:var(--card-left);z-index:20241222}.App .card button{padding:5px 8px;font-size:16px;color:var(--white-color);background-color:var(--green-color);cursor:pointer}.App dialog{width:var(--dialog-width, 0px);height:var(--dialog-height, 0px);background-color:var(--white-color);padding:0;border:none;border-radius:8px;grid-template-columns:2fr;transition:all .3s ease-in-out}.App dialog .header{--font-size: 20px;--close-size: 28px;display:flex;justify-content:space-between;align-items:center;margin-bottom:16px}.App dialog .header h2{margin:0;padding:0;color:var(--block-color);font-size:var(--font-size);font-weight:400}.App dialog .header .close{color:var(--white-color);background-color:var(--green-color);width:var(--close-size);height:var(--close-size);border-radius:3px;cursor:pointer}.App dialog .body ul{--cols: 6;--li-py: 8px;--li-font-size: 14px;display:grid;grid-template-columns:repeat(var(--cols),1fr);gap:10px}.App dialog .body ul li{display:grid;justify-content:center;align-items:center;padding:var(--li-py) 0;color:var(--white-color);background-color:var(--blue-color);border-radius:4px;font-size:var(--li-font-size);transition:all .3s ease-in-out;cursor:pointer}.App dialog .body ul li:hover{background-color:var(--green-color)}.App dialog[open]{display:grid;--dialog-width: 520px;--dialog-height: 266px;padding:16px;border:2px solid var(--green-color)}.App dialog::backdrop{background-color:rgba(var(--block-c-value),.2);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border-radius:4px}@media screen and (max-width: 768px){.App{--card-top: 12px;--card-left: calc(100vw - 90px) }.App dialog[open]{--dialog-width: 85vw;--dialog-height: 285px}.App dialog .header{--font-size: 20px;--close-size: 32px}.App dialog .body ul{--cols: 5;--li-py: 4px;--li-font-size: 10px}}:root{font-family:Inter,Avenir,Helvetica,Arial,sans-serif;font-size:16px;line-height:24px;font-weight:400;color-scheme:light dark;color:#ffffffde;background-color:#242424;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-text-size-adjust:100%} ");

(function (require$$0, require$$0$1) {
  'use strict';

  function getDefaultExportFromCjs(x) {
    return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
  }
  var jsxRuntime = { exports: {} };
  var reactJsxRuntime_production_min = {};
  /**
   * @license React
   * react-jsx-runtime.production.min.js
   *
   * Copyright (c) Facebook, Inc. and its affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   */
  var hasRequiredReactJsxRuntime_production_min;
  function requireReactJsxRuntime_production_min() {
    if (hasRequiredReactJsxRuntime_production_min) return reactJsxRuntime_production_min;
    hasRequiredReactJsxRuntime_production_min = 1;
    var f = require$$0, k = Symbol.for("react.element"), l = Symbol.for("react.fragment"), m = Object.prototype.hasOwnProperty, n = f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner, p = { key: true, ref: true, __self: true, __source: true };
    function q(c, a, g) {
      var b, d = {}, e = null, h = null;
      void 0 !== g && (e = "" + g);
      void 0 !== a.key && (e = "" + a.key);
      void 0 !== a.ref && (h = a.ref);
      for (b in a) m.call(a, b) && !p.hasOwnProperty(b) && (d[b] = a[b]);
      if (c && c.defaultProps) for (b in a = c.defaultProps, a) void 0 === d[b] && (d[b] = a[b]);
      return { $$typeof: k, type: c, key: e, ref: h, props: d, _owner: n.current };
    }
    reactJsxRuntime_production_min.Fragment = l;
    reactJsxRuntime_production_min.jsx = q;
    reactJsxRuntime_production_min.jsxs = q;
    return reactJsxRuntime_production_min;
  }
  var hasRequiredJsxRuntime;
  function requireJsxRuntime() {
    if (hasRequiredJsxRuntime) return jsxRuntime.exports;
    hasRequiredJsxRuntime = 1;
    {
      jsxRuntime.exports = requireReactJsxRuntime_production_min();
    }
    return jsxRuntime.exports;
  }
  var jsxRuntimeExports = requireJsxRuntime();
  var client = {};
  var hasRequiredClient;
  function requireClient() {
    if (hasRequiredClient) return client;
    hasRequiredClient = 1;
    var m = require$$0$1;
    {
      client.createRoot = m.createRoot;
      client.hydrateRoot = m.hydrateRoot;
    }
    return client;
  }
  var clientExports = requireClient();
  const ReactDOM = /* @__PURE__ */ getDefaultExportFromCjs(clientExports);
  const styleToString = (object) => {
    const result = [];
    for (let k in object) {
      result.push(`${k}:${object[k]}`);
    }
    return result.join(";");
  };
  const cookie = {
    write: function(t, e, day = 30) {
      var a = [], r = "/", o = location.hostname, i = location.protocol.startsWith("https");
      a.push(t + "=" + encodeURIComponent(e)), a.push("expires=" + new Date(Date.now() + day * 864e5).toUTCString()), a.push("path=" + r), a.push("domain=" + o), true === i && a.push("secure"), document.cookie = a.join("; ");
    },
    read: function(t, defaultValue) {
      var e = document.cookie.match(new RegExp("(^|;\\s*)(" + t + ")=([^;]*)"));
      if (e && e.length) {
        return decodeURIComponent(e[3]);
      }
      this.write(t, defaultValue);
      return defaultValue;
    },
    remove: function(t) {
      this.write(t, "", Date.now() - 864e5);
    }
  };
  const LINES = [
    { name: "YT", url: "https://jx.yangtu.top/?url=", mobile: 0 },
    { name: "冰豆", url: "https://bd.jx.cn/?url=", mobile: 0 },
    { name: "CK", url: "https://www.ckplayer.vip/jiexi/?url=", mobile: 0 },
    { name: "IK9", url: "https://yparse.ik9.cc/index.php?url=", mobile: 0 },
    { name: "JX", url: "https://jiexi.site/?url=", mobile: 0 },
    { name: "JY", url: "https://jx.playerjy.com/?url=", mobile: 0 },
    { name: "解析la", url: "https://api.jiexi.la/?url=", mobile: 0 },
    { name: "M3U8 TV", url: "https://jx.m3u8.tv/jiexi/?url=", mobile: 0 },
    { name: "m3u8", url: "https://www.playm3u8.cn/jiexi.php?url=", mobile: 0 },
    {
      name: "盘古",
      url: "https://www.pangujiexi.cc/jiexi.php?url=",
      mobile: 0
    },
    { name: "盘古2", url: "https://www.pangujiexi.com/jiexi/?url=", mobile: 0 },
    { name: "剖云", url: "https://www.pouyun.com/?url=", mobile: 0 },
    { name: "七哥", url: "https://jx.nnxv.cn/tv.php?url=", mobile: 0 },
    { name: "神哥", url: "https://json.ovvo.pro/jx.php?url=", mobile: 0 },
    { name: "听乐", url: "https://jx.dj6u.com/?url=", mobile: 1 },
    { name: "虾米", url: "https://jx.xmflv.com/?url=", mobile: 0 },
    { name: "虾米2", url: "https://jx.xmflv.cc/?url=", mobile: 0 },
    { name: "夜幕", url: "https://www.yemu.xyz/?url=", mobile: 0 },
    { name: "云析", url: "https://jx.yparse.com/index.php?url=", mobile: 0 },
    { name: "17云", url: "https://www.1717yun.com/jx/ty.php?url=", mobile: 0 },
    { name: "180", url: "https://jx.000180.top/jx/?url=", mobile: 0 },
    { name: "8090", url: "https://www.8090g.cn/?url=", mobile: 0 }
  ];
  const platfrom = {
    mobile: {
      container: "#player",
      episode: ".pl-episode",
      getParseUrl(cid, vid) {
        return `https://m.v.qq.com/x/m/play?cid=${cid}&vid=${vid}`;
      },
      getEpisodeData(pinia2) {
        var _a;
        const list = (_a = pinia2.playlist) == null ? void 0 : _a.playList.list.flat();
        const result = list.filter((e) => /第(\d+)[集|话]/.test(e.playTitle));
        return result;
      },
      getData() {
        const tvUrl = getQuery("schema");
        return getQuery(tvUrl);
      }
    },
    pc: {
      container: "#player",
      episode: ".page-content__bottom",
      pattern: "/x/cover((/\\w+)+)",
      getParseUrl(cid, vid) {
        return `https://v.qq.com/x/cover/${cid}/${vid}.html`;
      },
      getEpisodeData(pinia2) {
        var _a;
        const list = (_a = pinia2.episodeMain) == null ? void 0 : _a.listData[0].list.flat();
        const data = list.filter((e) => !e.isTrailer);
        return data;
      },
      getData() {
        const matches = location.href.match(this.pattern);
        if (matches && matches[1]) {
          const data = matches[1].split("/").filter(Boolean);
          const [cid, vid] = data.slice(0, 2);
          const result = { cid, vid };
          return result;
        }
        return null;
      }
    }
  };
  const app = platfrom[isMobile() ? "mobile" : "pc"];
  let pinia = null;
  let iframe = null;
  function App() {
    const dialog = require$$0.useRef(null);
    const onShow = () => {
      var _a;
      (_a = dialog.current) == null ? void 0 : _a.showModal();
      pinia = window.__PINIA__;
      console.log("%c Line:77 🍉 pinia", "color:#b03734", pinia);
      renderEpisode(pinia);
    };
    const onClose = () => {
      var _a;
      return (_a = dialog.current) == null ? void 0 : _a.close();
    };
    function onPlayer(event) {
      const player = document.querySelector(app.container);
      const data = app.getData();
      const parseUrl = app.getParseUrl(data.cid, data.vid);
      const lineUrl = event.currentTarget.dataset.line;
      const hybridUrl = `${lineUrl}${parseUrl}`;
      cookie.write("__lineUrl", lineUrl);
      if (iframe) {
        iframe.src = hybridUrl;
        onClose();
        return;
      }
      iframe = createIframe(hybridUrl);
      clearChildNodes(player);
      clearChildNodes(player == null ? void 0 : player.parentNode, (e) => e !== player);
      player == null ? void 0 : player.appendChild(iframe);
      onClose();
    }
    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "App", children: [
      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "card", children: /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "", onClick: onShow, children: "VIP助手" }) }),
      /* @__PURE__ */ jsxRuntimeExports.jsxs("dialog", { ref: dialog, children: [
        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "header", children: [
          /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { children: "解析线路" }),
          /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "close", onClick: onClose, children: "×" })
        ] }),
        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "body", children: /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { children: LINES.map((line) => /* @__PURE__ */ jsxRuntimeExports.jsx("li", { "data-line": line.url, onClick: onPlayer, children: line.name }, line.name)) }) })
      ] })
    ] });
  }
  function setTitle(e, title) {
    e.textContent = title;
  }
  function waitTime(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }
  function isMobile() {
    return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
  }
  function getQuery(name, url) {
    if (!url && (name == null ? void 0 : name.includes("?"))) {
      url = name;
      name = void 0;
    }
    const handle = new URL(url || window.location.href);
    const s = new URLSearchParams(handle.search);
    const o = Object.fromEntries(s.entries());
    return !name ? o : o.hasOwnProperty(name) ? o[name] : void 0;
  }
  function createIframe(url) {
    const iframe2 = document.createElement("iframe");
    iframe2.setAttribute("allowfullscreen", "true");
    iframe2.setAttribute("allowtransparency", "true");
    iframe2.setAttribute("allow", "autoplay; encrypted-media; picture-in-picture");
    iframe2.src = url;
    const style = {
      position: "absolute",
      "z-index": Date.now().toString(),
      width: "100%",
      height: "100%",
      border: "none"
    };
    iframe2.setAttribute("style", styleToString(style));
    return iframe2;
  }
  function clearChildNodes(node, filter = () => true) {
    while (node.firstChild && filter(node.firstChild)) {
      node.removeChild(node.firstChild);
    }
  }
  function createEpisodeStyle() {
    const style = `
    .panel-tip-pay.panel-tip-pay-video { display: none; }
    img.gaussian-blur-lcp-img { display: none; }
    .pl-episode-list {overflow: hidden;}
    .pl-episode-list ul { display: flex; flex-direction: row; flex-wrap: nowrap; align-items: center; gap: 8px; margin-bottom: 10px; overflow-x: scroll; padding-bottom: 10px; }
    .pl-episode-list li { position: relative; width: 51px; height: 51px; color: rgba(0,0,0,1); background-color: rgba(0, 0, 0, 0.1); text-align: center; font-size: 16px; font-weight: 600; border-radius: 4px; flex-shrink: 0; display: grid; align-items: center; justify-content: center; cursor: pointer; transition: all 0.3s ease-in-out; }
    .pl-episode-list li .vip { position: absolute; top: 0; right: 0; background-color: #ffdf89; color: #663d00; font-size: 10px; font-weight: 600; padding: 0 4px; border-bottom-left-radius: 4px; box-shadow: -2px 2px 5px 0px #d2a52e; }
    .pl-episode-list[pc] h2 { font-weight: 500; font-size: 20px; line-height: 28px; padding: 16px 0 10px 0;}
    .pl-episode-list[pc] li { color: rgba(255,255,255,.6); background-color: rgba(255,255,255, 0.2);}
    .pl-episode-list li.active { background-color: rgba(0, 154, 255, 1); }
  `;
    const styleEl = document.createElement("style");
    styleEl.innerHTML = style;
    return styleEl;
  }
  function createItemNode(item) {
    console.log("%c Line:247 🍆 item", "color:#42b983", item);
    const url = app.getParseUrl(item.cid, item.vid);
    let t = item.playSubtitle ? `${item.playSubtitle}` : item.playTitle;
    let v = item.imgTag ? `<div class="vip">VIP</div>` : "";
    return `<li data-title="${t}" data-url="${url}">${item.title}${v}</li>`;
  }
  function createEpisodeNode(data) {
    const cls = "pl-episode-list";
    const clsNode = document.querySelector(`.${cls}`);
    if (clsNode) clsNode.remove();
    const el = document.createElement("div");
    el.setAttribute(isMobile() ? "mobile" : "pc", "");
    el.className = cls;
    el.appendChild(createEpisodeStyle());
    if (!isMobile()) {
      const h2 = document.createElement("h2");
      h2.textContent = "电视剧";
      el.appendChild(h2);
    }
    const ul = document.createElement("ul");
    ul.innerHTML = data.reverse().map(createItemNode).join("");
    el.appendChild(ul);
    ul.addEventListener("click", (e) => {
      const target2 = e.target;
      if (target2.tagName === "LI") {
        const childs = target2.parentElement.children;
        Array.from(childs).forEach((e2) => e2.classList.remove("active"));
        target2.classList.toggle("active");
        const parseUrl = target2.dataset.url;
        const lineUrl = cookie.read("__lineUrl", "https://jx.xmflv.com/?url=");
        const player = document.querySelector(app.container);
        const hybridUrl = `${lineUrl}${parseUrl}`;
        iframe = createIframe(hybridUrl);
        clearChildNodes(player);
        clearChildNodes(player == null ? void 0 : player.parentNode, (child) => child !== player);
        player == null ? void 0 : player.appendChild(iframe);
        let r, t;
        if (isMobile()) {
          r = document.querySelector(".video-desc-long__title");
          t = "" + target2.dataset.title;
        } else {
          r = el.querySelector("h2");
          t = `电视剧 - ${target2.dataset.title}`;
        }
        setTitle(r, t);
      }
    });
    return el;
  }
  function renderEpisode(pinia2) {
    const data = app.getEpisodeData(pinia2);
    if (!data) return;
    const node = document.querySelector(app.episode);
    if (!node) return;
    const el = createEpisodeNode(data);
    if (isMobile()) {
      node.parentNode.insertBefore(el, node);
    } else {
      node.insertBefore(el, node.firstChild);
    }
  }
  waitTime(4e3).then(() => {
    pinia = window.__PINIA__;
    console.log("%c Line:301 🍐 pinia", "color:#42b983", pinia);
    renderEpisode(pinia);
  });
  const shadowApp = (() => {
    const app2 = document.createElement("div");
    app2.id = "monkey-hua-vplayer-popup";
    app2.attachShadow({ mode: "open" });
    document.body.parentElement.appendChild(app2);
    return app2;
  })();
  const shadowRoot = shadowApp.shadowRoot;
  const target = document.querySelector("style[id=monkey-hua-vplayer-popup-style-css]");
  if (target) {
    const style = document.createElement("style");
    style.textContent = target.textContent;
    shadowRoot.appendChild(style);
  }
  ReactDOM.createRoot(shadowRoot).render(/* @__PURE__ */ jsxRuntimeExports.jsx(App, {}));

})(React, ReactDOM);