Greasy Fork

Douban Info Class

Parse Douban Info

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

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

// ==UserScript==
// @name               Douban Info Class
// @description        parse douban info
// @version            0.0.8
// @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) {
    this.id = (async () => id)();
    this.pathname = (async () => DoubanInfo.subjectPathName + id + "/")();
  }

  get subjectDoc() {
    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", {
        value: (async () => doc)(),
        writable: false,
      });
      return this.subjectDoc;
    })();
  }

  get linkingData() {
    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", {
        value: (async () => ldJSON)(),
        writable: false,
      });
      return this.linkingData;
    })();
  }

  get title() {
    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;
      Object.defineProperty(this, "title", {
        value: (async () => title)(),
        writable: false,
      });
      return this.title;
    })();
  }

  get type() {
    return (async () => {
      const ld = await this.linkingData;
      const type = ld?.["@type"]?.toLowerCase() || null;
      Object.defineProperty(this, "type", {
        value: (async () => type)(),
        writable: false,
      });
      return this.type;
    })();
  }

  get poster() {
    return (async () => {
      const doc = await this.subjectDoc;
      const poster =
        doc
          ?.querySelector("head>meta[property='og:image']")
          ?.content.replace("s_ratio_poster", "l_ratio_poster") || null;
      Object.defineProperty(this, "poster", {
        value: (async () => poster)(),
        writable: false,
      });
      return this.poster;
    })();
  }

  get duration() {
    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 || durationFromLD;
      Object.defineProperty(this, "duration", {
        value: (async () => duration)(),
        writable: false,
      });
      return this.duration;
    })();
  }
}