Sleazy Fork is available in English.

JavDB 添加跳转在线观看

在影片详情页添加跳转到在线观看网站的按钮,并检查对应是否存在资源,如果对应网站上存在该资源则为绿色,否则显示红色,顺便检测有无中文字幕。

Ajankohdalta 18.9.2022. Katso uusin versio.

// ==UserScript==
// @name         JavDB 添加跳转在线观看
// @namespace    https://greasyfork.org/users/58790
// @version      0.30.1
// @author       mission522
// @description  在影片详情页添加跳转到在线观看网站的按钮,并检查对应是否存在资源,如果对应网站上存在该资源则为绿色,否则显示红色,顺便检测有无中文字幕。
// @license      MIT
// @icon         https://javdb.com/favicon-32x32.png
// @match        *://*.javdb.com/*
// @connect      jable.tv
// @connect      missav.com
// @connect      javhhh.com
// @connect      netflav.com
// @connect      avgle.com
// @connect      bestjavporn.com
// @connect      jav.guru
// @connect      javmost.cx
// @connect      hpjav.tv
// @connect      av01.tv
// @connect      javbus.com
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// ==/UserScript==

// use vite-plugin-monkey@2.4.4 at 2022-09-18T10:42:32.030Z

(function() {
  var _a, _b, _c, _d;
  "use strict";
  const jdbStyle = `
  .button-g {
    font-size: .75rem;
    margin:0.35rem 0.75rem 0.35rem 0;
  }
  .has-subtitle::before {
    position: absolute;
    content: "\u5B57\u5E55";
    padding: 1px;
    top: -8px;
    left: -3px;
    line-height: 1;
    color: white;
    background: green;
  }
  .has-leakage::after {
    position: absolute;
    content: "\u65E0\u7801";
    padding: 1px;
    top: -8px;
    left: 30px;
    line-height: 1;
    color: white;
    background: green;
  }
`;
  const temp = () => {
  };
  const siteList = [
    {
      id: 0,
      name: "Jable",
      hostname: "jable.tv",
      url: "https://jable.tv/videos/{{code}}/",
      fetcher: "get",
      domQuery: { subQuery: ".header-right>h6" },
      method: temp
    },
    {
      id: 1,
      name: "MISSAV",
      hostname: "missav.com",
      url: "https://missav.com/{{code}}/",
      fetcher: "get",
      domQuery: {
        subQuery: '.space-y-2 a.text-nord13[href="https://missav.com/chinese-subtitle"]',
        leakQuery: ".order-first div.rounded-md a[href]:last-child"
      },
      method: temp
    },
    {
      id: 2,
      name: "NETFLAV",
      hostname: "netflav.com",
      url: "https://netflav.com/search?type=title&keyword={{code}}",
      fetcher: "parser",
      domQuery: { linkQuery: ".grid_cell>a", titleQuery: ".grid_cell>a>.grid_title" },
      method: temp
    },
    {
      id: 3,
      name: "Avgle",
      hostname: "avgle.com",
      url: "https://avgle.com/search/videos?search_query={{code}}&search_type=videos",
      fetcher: "parser",
      domQuery: {
        linkQuery: ".container>.row .row .well>a[href]",
        titleQuery: ".container>.row .row .well .video-title"
      },
      method: temp
    },
    {
      id: 4,
      name: "JAVHHH",
      hostname: "javhhh.com",
      url: "https://javhhh.com/v/?wd={{code}}",
      fetcher: "parser",
      domQuery: {
        linkQuery: ".typelist>.i-container>a[href]",
        titleQuery: ".typelist>.i-container>a[href]"
      },
      method: temp
    },
    {
      id: 5,
      name: "BestJavPorn",
      hostname: "BestJavPorn.com",
      url: "https://www2.bestjavporn.com/search/{{code}}/",
      fetcher: "parser",
      domQuery: { linkQuery: "article.thumb-block>a", titleQuery: "article.thumb-block>a" },
      method: temp
    },
    {
      id: 6,
      name: "Jav.Guru",
      hostname: "jav.guru",
      url: "https://jav.guru/?s={{code}}",
      fetcher: "parser",
      domQuery: { linkQuery: ".imgg>a[href]", titleQuery: ".inside-article>.grid1 a[title]" },
      method: temp
    },
    {
      id: 7,
      name: "JAVMOST",
      hostname: "javmost.cx",
      url: "https://javmost.cx/search/{{code}}/",
      fetcher: "parser",
      domQuery: {
        linkQuery: "#content .card>a[href]",
        titleQuery: "#content .card-block .card-title"
      },
      method: temp
    },
    {
      id: 8,
      name: "HPJAV",
      hostname: "hpjav.tv",
      url: "https://hpjav.tv/tw?s={{code}}",
      fetcher: "parser",
      domQuery: { linkQuery: ".entry-title a[href]", titleQuery: ".entry-title a[href]" },
      method: temp
    },
    {
      id: 9,
      name: "AV01",
      hostname: "av01.tv",
      url: "https://www.av01.tv/search/videos?search_query={{code}}",
      fetcher: "parser",
      domQuery: { linkQuery: "div[id].well-sm>a", titleQuery: ".video-views>.pull-left" },
      method: temp
    }
  ];
  var r = (_a = Reflect.get(document, "__monkeyWindow")) != null ? _a : window;
  r.GM;
  r.unsafeWindow = (_b = r.unsafeWindow) != null ? _b : window;
  r.unsafeWindow;
  r.GM_info;
  r.GM_cookie;
  var l = (...e) => r.GM_addStyle(...e), b = (...e) => r.GM_xmlhttpRequest(...e);
  window.location.hostname.match(/^.*?\.?(.*)\.com$/)[1] === "javdb" ? "jdb" : "jbus";
  const jdbCode = (_c = document.querySelector(`[data-clipboard-text]`)) == null ? void 0 : _c.dataset.clipboardText;
  (_d = document.querySelector(`span[style="color:#CC0000;"]`)) == null ? void 0 : _d.innerText.replace("\u590D\u5236", "");
  const envCode = jdbCode;
  function videoPageParser(responseText, { subQuery, leakQuery }) {
    const doc = new DOMParser().parseFromString(responseText, "text/html");
    const subNode = subQuery ? doc.querySelector(subQuery) : "";
    const subNodeText = subNode ? subNode.innerHTML : "";
    const leakDode = leakQuery ? doc.querySelector(leakQuery) : null;
    return {
      hasSubtitle: subNodeText.includes("\u5B57\u5E55") || subNodeText.includes("subtitle"),
      hasLeakage: !!leakDode
    };
  }
  function serachPageParser(responseText, { linkQuery, titleQuery }, siteHostName) {
    const doc = new DOMParser().parseFromString(responseText, "text/html");
    const linkDode = linkQuery ? doc.querySelectorAll(linkQuery)[0] : null;
    const titleDode = titleQuery ? doc.querySelectorAll(titleQuery)[0] : null;
    function query() {
      if (linkDode && titleDode && titleDode.outerHTML.includes(envCode)) {
        return { targetLink: linkDode.href.replace(linkDode.hostname, siteHostName), success: true };
      } else {
        return { targetLink: "", success: false };
      }
    }
    return query();
  }
  async function xhr(siteItem, siteUrl) {
    const xhrPromise = new Promise((resolve) => {
      b({
        method: "GET",
        url: siteUrl,
        onload: (response) => {
          if (siteItem.fetcher === "get") {
            if (response.status === 404) {
              resolve({
                isSuccess: false,
                targetLink: siteUrl,
                name: siteItem.name,
                msg: "\u5E94\u8BE5\u662F\u6CA1\u6709\u8D44\u6E90"
              });
            } else {
              const successResult = videoPageParser(response.responseText, siteItem.domQuery);
              resolve({
                isSuccess: true,
                targetLink: siteUrl,
                name: siteItem.name,
                ...successResult,
                msg: "[get]\uFF0C\u5B58\u5728\u8D44\u6E90"
              });
            }
          } else if (siteItem.fetcher === "parser") {
            const successResult = serachPageParser(
              response.responseText,
              siteItem.domQuery,
              siteItem.hostname
            );
            resolve({
              name: siteItem.name,
              targetLink: successResult.targetLink,
              isSuccess: successResult.success,
              hasSubtitle: false,
              hasLeakage: false,
              msg: "[parser]\u5B58\u5728\u8D44\u6E90"
            });
          }
        },
        onerror: (error) => {
          resolve({
            isSuccess: false,
            targetLink: siteUrl,
            name: siteItem.name,
            msg: error.error
          });
        }
      });
    });
    return xhrPromise;
  }
  function addStyle() {
    const style = jdbStyle;
    l(style);
  }
  function createPanelNode() {
    const parentNodeQueryString = `.panel.movie-panel-info`;
    const parentNode = document.querySelector(parentNodeQueryString);
    const panelNode = document.createElement("div");
    parentNode && parentNode.appendChild(panelNode);
    panelNode.classList.add("panel-block", "column");
    return panelNode;
  }
  function createButtonNode(panelNode, siteItem) {
    const buttonNode = document.createElement("a");
    buttonNode.setAttribute("target", "_blank");
    panelNode.appendChild(buttonNode);
    const buttonClassList = ["button", "is-info", "is-outlined"];
    buttonClassList.forEach((item) => {
      buttonNode.classList.add(item, "button-g");
    });
    buttonNode.innerHTML = siteItem.name;
    return {
      buttonNode,
      setButtonStatus: (targetLink, color, hasLeakage, hasSubtitle) => {
        buttonNode.href = targetLink;
        buttonNode.style.color = color;
        buttonNode.style.borderColor = color;
        buttonNode.style.backgroundColor = buttonNode.style.backgroundColor;
        hasLeakage && buttonNode.classList.add("has-leakage");
        hasSubtitle && buttonNode.classList.add("has-subtitle");
      }
    };
  }
  (function main() {
    addStyle();
    const panelNode = createPanelNode();
    siteList.forEach(async (item) => {
      const siteUrl = item.url.replace("{{code}}", envCode);
      const { setButtonStatus } = createButtonNode(panelNode, item);
      const { isSuccess, hasLeakage, hasSubtitle, targetLink } = await xhr(item, siteUrl);
      setButtonStatus(targetLink, isSuccess ? "green" : "red", hasLeakage, hasSubtitle);
    });
  })();
})();