Greasy Fork

Douban Info Class

Parse Douban Info

目前为 2022-01-05 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.greasyfork.icu/scripts/438042/1005307/Douban%20Info%20Class.js

// ==UserScript==
// @name               Douban Info Class
// @description        parse douban info
// @version            0.0.11
// @author             Secant(TYT@NexusHD)
// @icon               https://movie.douban.com/favicon.ico
// @contributionURL    https://i.loli.net/2020/02/28/JPGgHc3UMwXedhv.jpg
// @contributionAmount 10
// @namespace          https://greasyfork.org/users/152136
// @grant              GM_xmlhttpRequest
// @connect            movie.douban.com

class DoubanInfo {
  static origin = "https://movie.douban.com";
  static subjectPathName = "/subject/";
  static timeout = 6000;

  constructor(id) {
    Object.defineProperties(this, {
      id: {
        configurable: true,
        enumerable: true,
        get: function () {
          return (async () => {
            Object.defineProperty(this, "id", {
              writable: false,
              configurable: false,
              enumerable: true,
              value: (async () => id)(),
            });
            return this.id;
          })();
        },
      },
      pathname: {
        configurable: true,
        enumerable: false,
        get: function () {
          return (async () => {
            const pathname = DoubanInfo.subjectPathName + (await this.id) + "/";
            Object.defineProperty(this, "pathname", {
              writable: false,
              configurable: false,
              enumerable: false,
              value: (async () => pathname)(),
            });
            return this.pathname;
          })();
        },
      },
      subjectDoc: {
        configurable: true,
        enumerable: false,
        get: function () {
          return (async () => {
            const currentURL = new URL(window.location.href);
            let doc;
            if (
              currentURL.origin === DoubanInfo.origin &&
              currentURL.pathname === (await this.pathname)
            ) {
              doc = document;
            } else {
              doc = new Promise(async (resolve) => {
                GM_xmlhttpRequest({
                  method: "GET",
                  url: new URL(
                    await this.pathname,
                    DoubanInfo.origin
                  ).toString(),
                  headers: {
                    referrer: DoubanInfo.origin,
                  },
                  timout: DoubanInfo.timeout,
                  onload: (resp) => {
                    try {
                      resolve(
                        new DOMParser().parseFromString(
                          resp.responseText,
                          "text/html"
                        )
                      );
                    } catch (err) {
                      console.warn(err);
                      resolve(null);
                    }
                  },
                  ontimeout: (e) => {
                    console.warn(e);
                    resolve(null);
                  },
                  onerror: (e) => {
                    console.warn(e);
                    resolve(null);
                  },
                });
              });
            }
            Object.defineProperty(this, "subjectDoc", {
              writable: false,
              configurable: false,
              enumerable: false,
              value: (async () => doc)(),
            });
            return this.subjectDoc;
          })();
        },
      },
      linkingData: {
        configurable: true,
        enumerable: false,
        get: function () {
          return (async () => {
            const doc = await this.subjectDoc;
            const ldJSON =
              dJSON.parse(
                heDecode(
                  doc?.querySelector("head>script[type='application/ld+json']")
                    ?.textContent
                )
              ) || null;
            Object.defineProperty(this, "linkingData", {
              writable: false,
              configurable: false,
              enumerable: false,
              value: (async () => ldJSON)(),
            });
            return this.linkingData;
          })();
        },
      },
      type: {
        configurable: true,
        enumerable: true,
        get: function () {
          return (async () => {
            const ld = await this.linkingData;
            const type = ld?.["@type"]?.toLowerCase() || null;
            Object.defineProperty(this, "type", {
              writable: false,
              configurable: false,
              enumerable: true,
              value: (async () => type)(),
            });
            return this.type;
          })();
        },
      },
      poster: {
        configurable: true,
        enumerable: true,
        get: function () {
          return (async () => {
            const doc = await this.subjectDoc;
            const ld = await this.linkingData;
            const posterFromMeta =
              doc?.querySelector("head>meta[property='og:image']")?.content ||
              null;
            const posterFromLD = ld?.image || null;
            const posterFromPage =
              doc?.querySelector("body #mainpic img")?.src || null;
            const poster =
              (posterFromMeta || posterFromLD || posterFromPage)
                ?.replace("s_ratio_poster", "l_ratio_poster")
                .replace(/img\d+\.doubanio\.com/, "img9.doubanio.com")
                .replace(/\.webp$/i, ".jpg") || null;
            Object.defineProperty(this, "poster", {
              writable: false,
              configurable: false,
              enumerable: true,
              value: (async () => poster)(),
            });
            return this.poster;
          })();
        },
      },
      title: {
        configurable: true,
        enumerable: false,
        get: function () {
          return (async () => {
            const doc = await this.subjectDoc;
            const ld = await this.linkingData;
            const titleFromPage =
              doc?.querySelector("body #content h1>span[property]")
                ?.textContent || null;
            const titleFromMeta =
              doc?.querySelector("head>meta[property='og:title']")?.content ||
              null;
            const titleFromLD = ld?.name || null;
            const title = titleFromPage || titleFromMeta || titleFromLD || null;
            Object.defineProperty(this, "title", {
              writable: false,
              configurable: false,
              enumerable: false,
              value: (async () => title)(),
            });
            return this.title;
          })();
        },
      },
      chineseTitle: {
        configurable: true,
        enumerable: true,
        get: function () {
          return (async () => {
            const doc = await this.subjectDoc;
            const chineseTitle = doc?.title?.slice(0, -5);
            Object.defineProperty(this, "chineseTitle", {
              writable: false,
              configurable: false,
              enumerable: true,
              value: (async () => chineseTitle)(),
            });
            return this.chineseTitle;
          })();
        },
      },
      originalTitle: {
        configurable: true,
        enumerable: true,
        get: function () {
          return (async () => {
            let originalTitle;
            if (await this.isChinese) {
              originalTitle = await this.chineseTitle;
            } else {
              originalTitle = (await this.title)
                ?.replace(await this.chineseTitle, "")
                .trim();
            }
            Object.defineProperty(this, "originalTitle", {
              writable: false,
              configurable: false,
              enumerable: true,
              value: (async () => originalTitle)(),
            });
            return this.originalTitle;
          })();
        },
      },
      isChinese: {
        configurable: true,
        enumerable: false,
        get: function () {
          return (async () => {
            let isChinese = false;
            if ((await this.title) === (await this.chineseTitle)) {
              isChinese = true;
            }
            Object.defineProperty(this, "isChinese", {
              writable: false,
              configurable: false,
              enumerable: false,
              value: (async () => isChinese)(),
            });
            return this.isChinese;
          })();
        },
      },
      duration: {
        configurable: true,
        enumerable: true,
        get: function () {
          return (async () => {
            const doc = await this.subjectDoc;
            const ld = await this.linkingData;
            const durationFromMeta =
              parseInt(
                doc?.querySelector("head>meta[property='video:duration']")
                  ?.content,
                10
              ) || null;
            const durationFromLD =
              ld?.duration?.replace(
                /^PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?$/,
                (_, p1, p2, p3) => {
                  return (
                    parseInt(p1, 10) * 3600 +
                    parseInt(p2, 10) * 60 +
                    parseInt(p3, 10)
                  ).toString();
                }
              ) || null;
            /*const durationFromPage =*/
            const duration = durationFromMeta
              ? [{ duration: durationFromMeta, type: undefined }]
              : durationFromLD
              ? [{ duration: durationFromLD, type: undefined }]
              : [];
            Object.defineProperty(this, "duration", {
              writable: false,
              configurable: false,
              enumerable: true,
              value: (async () => duration)(),
            });
            return this.duration;
          })();
        },
      },
    });
  }
}