Greasy Fork

Hua VPlayer

A video player for V.qq.com

目前为 2024-12-24 提交的版本。查看 最新版本

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