// ==UserScript==
// @name 豆瓣评分增强大师
// @namespace doveboy_js
// @version 10.87
// @description 一站式聚合全球数十家主流影视数据库与评分平台,涵盖IMDb、烂番茄新鲜度&爆米花指数、Metacritic影评人&用户双评分、TMDB、Letterboxd、Allocine、AniList、Bangumi、MyAnimeList、MyDramaList、NeoDB及Common Sense Media等,全面覆盖电影、剧集、综艺与动画四大类型。独家打造“客观评分”智能加权算法,依据作品主场自动调权,融合票数置信度输出一个真正去偏的综合分数,并创新引入Steam式好评率分析,将豆瓣影评拆解为总评、热门、最新三维好评度,让口碑一目了然。自动拉取出品发行、技术参数、幕后花絮及获奖荣誉,并以悬浮气泡优雅展示;同步联动电影票房,实时汇率换算呈现预算、全球票房、北美首周、大陆首周&总票房及港台日韩等细分市场收支全景卡片。特有“大中华区评分差异对比”横评两岸三地观众口味分歧,辅以剧集播出日历,更提供一键复制影片资料(含完整演职员表及获奖列表)与历史评分趋势曲线,让每一部作品的轮廓在豆瓣页面上从未如此立体、专业而丰沛。
// @author 白鸽男孩
// @contributor Rhilip
// @license Zlib/Libpng License
// @icon data:image/png;base64,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
// @match https://movie.douban.com/subject/*
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @grant GM_setClipboard
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_openInTab
// @connect bimzcy.github.io
// @connect www.imdb.com
// @connect query.wikidata.org
// @connect www.rottentomatoes.com
// @connect api.themoviedb.org
// @connect www.themoviedb.org
// @connect www.metacritic.com
// @connect backend.metacritic.com
// @connect metacritic.com
// @connect letterboxd.com
// @connect www.allocine.fr
// @connect graphql.anilist.co
// @connect www.omdbapi.com
// @connect bgm.tv
// @connect myanimelist.net
// @connect neodb.social
// @connect zduo.me
// @connect wikidata.org
// @connect api.frankfurter.dev
// @connect api.graphql.imdb.com
// @connect m.maoyan.com
// @connect www.maoyan.com
// @connect boxofficemojo.com
// @connect api.ratetend.com
// @connect www.ratetend.com
// @connect api.tvmaze.com
// @connect www.tvmaze.com
// @connect mydramalist.com
// @connect www.mydramalist.com
// @connect www.commonsensemedia.org
// @require https://lib.baomitu.com/jquery/3.6.0/jquery.min.js
// @require https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js
// ==/UserScript==
(function() {
'use strict';
// Base64 图标
const AL_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAD5UExURRkhLRkhLRkhLRkhLUxpcRkhLRkhLRkhLRkhLRkhLRkhLRkhLRkhLRkhLf///v///xgfLACq/wCs/xQdKgN4sfv7+wCo/A1Pc6irrwR1rvf3+GZrdKCjp6WorRkfKv39/QVzqmRpcUpQWuHi4zpBSygvO5WZnjE3Qtzd3xAYJfHy8uXm5yW4/+r3/hFAXX6DiQR9uQZupHd7grCzt25zelNYYsvN0Pb8/2XM/8zv/wmx/xkfKTy//3zU/yIqNQR5s4qNkxUwQ8DCxrq8vyxAUF1iat7z/mGUsAdikqTU7luPrtLT1U7G/4zY/tHT1avj/hONyr/p/rXm/lS/nxsAAAANdFJOU/ua3pYAfRAOd9nd4J6x3JfpAAABaUlEQVQ4y4WTh1LCQBCGDw0B1OQud4IhCakUKaJIb/be9f0fxivRgeESvskwyew3+yfLHlBy+QLQpIBCXlVAbjehzJWMCvIpdWpkQUFLZQ+AdGGljgQuvdxV479erTuOU7978v1eF0kEcgopxccSxtiXCRc3UKc8lwwDH0oiSNjiwkMnSbjidd2byAVUGwlB/8RSgQxpgmdR4aQj71CHOmxVmDGRCaj2SoWP8Jjm/GCJQO4tKjiaCXmGJMKhFSto2kWasdwUUIP2hu2wEbap+D3bEIjN3s6Kooh/x2IzwuRDoP8FH8WyPJ+7DBQLqBrFUxJMv44E10gIZMDezSsyPHo3XpQ4Ri/uAPq0AewHtm0Ht6zXu8HBZ0JA1TYb47BJCGkGrNl0tiaIVRnVUPzBEI7fDMy4jCPOzUrFHMRbxB9eygy/i/4GxVjdXU38uOtrnbD2Ww7Ozvajp2bSDu+BChQ1u5Ok7GdV5Reeuka5jCikYgAAAFd6VFh0UmF3IHByb2ZpbGUgdHlwZSBpcHRjAAB4nOPyDAhxVigoyk/LzEnlUgADIwsuYwsTIxNLkxQDEyBEgDTDZAMjs1Qgy9jUyMTMxBzEB8uASKBKLgDqFxF08kI1lQAAAABJRU5ErkJggg=="
const LBX_ICON = "data:image/vnd.microsoft.icon;base64,AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAAAAAgAAAAAQAgAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABrWUcrZ1ZGj2dVRNVnVUTzZ1VE82dVRNVnVkaPa1lHKwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBVVQZnVkWUZ1ZF/WZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2dWRf1nVkWUgFVVBgAAAAAAAAAAAAAAAIBVVQZnVUW9ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2dVRb2AVVUGAAAAAAAAAABnVkWUZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/Z1ZFlAAAAABrWUcrZ1ZF/WZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2dWRf1rWUcrZ1ZGj2ZVRP9GYn3/K26v/ztnkv9jV0j/ZGg6/12eIP9dniD/ZGg6/2lYRP+igEL/t5BC/5J1Qv9mVUT/Z1ZGj2dVRNU2aZr/AID//wCA//8AgP//QpK3/1XZA/9U4AD/VOAA/1XZA//Es1P/9LxA//S8QP/0vED/p4RC/2dVRNVnVUTzCnzs/wCA//8AgP//AID//7bk0P9w5Sr/VOAA/1TgAP9w5Sr/3u6z//S8QP/0vED/9LxA/+eyQP9nVUTzZ1VE8wp87P8AgP//AID//wCA//+25ND/cOUq/1TgAP9U4AD/cOUq/97us//0vED/9LxA//S8QP/nskD/Z1VE82dVRNU2aZr/AID//wCA//8AgP//QpK3/1XZA/9U4AD/VOAA/1XZA//Es1P/9LxA//S8QP/0vED/p4RC/2dVRNVnVkaPZlVE/0Ziff8rbq//O2eS/2NXSP9kaDr/XZ4g/12eIP9kaDr/aVhE/6KAQv+3kEL/knVC/2ZVRP9nVkaPa1lHK2dWRf1mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9nVkX9a1lHKwAAAABnVkWUZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/Z1ZFlAAAAAAAAAAAgFVVBmdVRb1mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/Z1VFvYBVVQYAAAAAAAAAAAAAAACAVVUGZ1ZFlGdWRf1mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9nVkX9Z1ZFlIBVVQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABrWUcrZ1ZGj2dVRNVnVUTzZ1VE82dVRNVnVkaPa1lHKwAAAAAAAAAAAAAAAAAAAADwDwAAwAMAAIABAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAAgAEAAMADAADwDwAAKAAAACAAAABAAAAAAQAgAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAVVUGaFZER2dWRJVnVUS/ZlZE2WdVRPNnVUTzZlZE2WdVRL9nVkSVaFZER4BVVQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABqVUoYZlVElmdWRfFmVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/Z1ZF8WZVRJZqVUoYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABtbUkHZlZFgmZWRfRmVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZWRfRmVkWCbW1JBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAalVKGGdWRcdmVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9nVkXHalVKGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGlZSC5mVUXjZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUXjZlVELQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABqVUoYZlVF42ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUXjalVKGAAAAAAAAAAAAAAAAAAAAAAAAAAAbW1JB2dWRcdmVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9nVkXHbW1JBwAAAAAAAAAAAAAAAAAAAABmVkWCZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVkWCAAAAAAAAAAAAAAAAalVKGGZWRfRmVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZWRfRqVUoYAAAAAAAAAABmVUSWZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRJYAAAAAgFVVBmdWRfFmVUT/ZlVE/2ZVRP9mVUT/X1hP/1hbXP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZWI9/2ViPf9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/eGNE/29bRP9mVUT/ZlVE/2ZVRP9mVUT/Z1ZF8YBVVQZoVkRHZlVE/2ZVRP9mVUT/RGOC/w954/8AgP7/AID//wZ98/8pbrL/YFdN/2ZVRP9lV0P/Xpck/1bSBv9U4AD/VOAA/1bSBv9elyT/ZVdD/2ZVRP9tWkT/upJB/+u2QP/0vED/9LxA/9+sQf+Wd0P/ZlVE/2ZVRP9mVUT/aFZER2dWRJVmVUT/ZlVE/y1tq/8AgP//AID//wCA//8AgP//AID//wCA//8Meuf/XFpS/1m9Ef9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9ZvRH/cV9D/+KvQP/0vED/9LxA//S8QP/0vED/9LxA//S8QP+0jkL/ZlVE/2ZVRP9nVkSVZ1VEv2ZVRP9KYXf/AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//9ap8n/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP/SxWv/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP+NcUP/ZlVE/2dVRL9mVkTZZlVE/x5zxv8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AoD//+32//9l3Dj/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/eeAq//768f/0vEH/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA/8mdQf9mVUT/ZlZE2WdVRPNmVUT/Cnzs/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8gj////////3rXhv9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP+r42H///////XEV//0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/57JA/2ZVRP9mVUTyZ1VE82ZVRP8KfOz/AID//wCA//8AgP//AID//wCA//8AgP//AID//yGQ////////eteG/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/6vjYf//////9cRX//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/nskD/ZlVE/2dVRPNmVkTZZlVE/x5zxv8AgP//AID//wCA//8AgP//AID//wCA//8AgP//A4H//+32//9l3Dj/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/eeEq//768f/0vEH/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA/8mdQf9mVUT/ZlZE2WdVRL9mVUT/SmF3/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//W6fK/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1Tg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RP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2dVReloVkU7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoVkZiZlVF+WZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVF+WhWRmIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKpVVQNmVkSOZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlZEjqpVVQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKpVVQNmVUWxZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUWxqlVVAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmVkSOZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZWRI4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoVkZiZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/aFZGYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoVkU7ZlVF+WZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRfloVkU7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmVUQeZ1VF6WZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/Z1VF6WZVRB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8BZ1ZFxGZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9nVkXE////AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ1dEYWZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2dXRGEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcGBQEGZVRepmVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUXqcGBQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhWRoBmVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2hWRoAAAAAAAAAAAAAAAAAAAAAAAAAAAHZiTg1nVkXuZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9nVkXudmJODQAAAAAAAAAAAAAAAAAAAABmVkVzZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZWRXMAAAAAAAAAAAAAAAAAAAAAZ1VE1WZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZV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QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/5rFA/2xZRP9mVUT/ZlVE/2ZVRP9mVUSlZ1VFy2ZVRP9mVUT/ZlVE/0Jlhv8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//7jb///9/v//X9dD/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/gN4q//7+/P/77Mr/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP+ZekP/ZlVE/2ZVRP9mVUT/Z1VFy2dWRd9mVUT/ZlVE/2ZVRP8icb//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wuF/////////////4rVsv9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/8jkgf////////////S+SP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/xJlB/2ZVRP9mVUT/ZlVE/2dWRd9nVUTsZlVE/2ZVRP9mVUT/D3nj/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8vl/////////////+h2Nr/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP/j6Kb////////////2yGP/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA/9+sQf9mVUT/ZlVE/2ZVRP9nVUTsZlVF+WZVRP9mVUT/ZlVE/wR+9v8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//VKn/////////////v+Pt/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/8e/G////////////99J+//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/tuED/ZlVE/2ZVRP9mVUT/ZlVF+WZVRflmVUT/ZlVE/2ZVRP8Efvb/AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//1Sp/////////////7/j7f9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA//Hvxv////////////fSfv/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/7bhA/2ZVRP9mVUT/ZlVE/2ZVRflnVUTsZlVE/2ZVRP9mVUT/D3nj/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8wl/////////////+h2Nr/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP/j6Kj////////////2yGP/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA/9+sQf9mVUT/ZlVE/2ZVRP9nVUTsZ1ZF32ZVRP9mVUT/ZlVE/yJxv/8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//DYb/////////////itWy/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/yeSC////////////9L5I//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/EmUH/ZlVE/2ZVRP9mVUT/Z1ZF32dVRctmVUT/ZlVE/2ZVRP9CZYb/AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//+42////f7//1/XQ/9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/3/eKf/+/v3/++zK//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/mXpD/2ZVRP9mVUT/ZlVE/2dVRctmVUSlZlVE/2ZVRP9mVUT/YldL/wp76/8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//SKP//7Pd6f9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/7uy9//bOc//0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/5rFA/2xZRP9mVUT/ZlVE/2ZVRP9mVUSlZ1VFfmZVRP9mVUT/ZlVE/2ZVRP8+Zoz/AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wF//f9fwFj/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/4rKOP/zu0D/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA/519Qv9mVUT/ZlVE/2ZVRP9mVUT/Z1VFfmhWR1NmVUT/ZlVE/2ZVRP9mVUT/ZlVE/yNxvv8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//83aZn/ZGc6/1XWBf9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1XWBf9kZzr/p4VC//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA/8OZQf9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2hWR1NtVUkVZlVE/mZVRP9mVUT/ZlVE/2ZVRP9iV0r/HHTK/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wCA//8tbav/ZlVE/2ZVRP9idDT/VdYF/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1XWBf9idDT/ZlVE/2ZVRP+0jkL/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA/8yfQf9rWUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP5tVUkVAAAAAGZVRNJmVUT/ZlVE/2ZVRP9mVUT/ZlVE/2RWRv8wbKf/An/7/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AID//wZ+9P8/ZYv/ZlVE/2ZVRP9mVUT/ZlVE/2RnOv9Yww7/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1TgAP9U4AD/VOAA/1jDDv9kZzr/ZlVE/2ZVRP9mVUT/ZlVE/5x8Q//stkH/9LxA//S8QP/0vED/9LxA//S8QP/0vED/9LxA//S8QP/0vED/8rpA/7KMQv9nV0T/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUTSAAAAAAAAAABnVUWNZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/1dcXv8nb7f/B33x/wCA//8AgP//AID//wCA//8AgP//DHvo/zBsp/9eWVH/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVD/2CELf9ZvhD/Vd8A/1TgAP9U4AD/VOAA/1TgAP9V3wD/Wb4Q/2CELf9mVUP/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/cF1E/7KMQv/jsEH/9LxA//S8QP/0vED/9LxA//S8QP/ptEH/vpRC/3pkRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/Z1VFjQAAAAAAAAAAaVVGM2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2RWRv9SXmj/RmJ9/0Blif9IYXn/VVxi/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2VfPv9heDP/YIYs/2CGLP9heDP/ZV8+/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/31mRP+OckP/m3xC/5J1Qv+CaUP/Z1dE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2lVRjMAAAAAAAAAAAAAAABnVUTVZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2dVRNUAAAAAAAAAAAAAAAAAAAAAZlZFc2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVkVzAAAAAAAAAAAAAAAAAAAAAHZiTg1nVkXuZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9nVkXudmJODQAAAAAAAAAAAAAAAAAAAAAAAAAAaFZGgGZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/aFZGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBgUBBmVUXqZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVF6nBgUBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ1dEYWZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2dXRGEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wFnVkXEZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2dWRcT///8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZlVEHmdVRelmVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2dVRelmVUQeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoVkU7ZlVF+WZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRfloVkU7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhWRmJmVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9oVkZiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZlZEjmZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVkSOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKpVVQNmVUWxZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUWxqlVVAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqlVVA2ZWRI5mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVkSOqlVVAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaFZGYmZVRflmVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRfloVkZiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoVkU7Z1VF6WZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2dVReloVkU7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGZVRB5nVkXEZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2dWRcRmVUQeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AWdXRGFmVUXqZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZVRP9mVUT/ZlVE/2ZV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"
const ALC_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHYAAAB2BAMAAADviXoiAAAAGFBMVEUAAAAmJib/////ywBcUzPIoQmUehKRkZFw3PDCAAAAAXRSTlMAQObYZgAAA2BJREFUWMPdmU1P20AQQKv+AzeOe3WjSD1HG+fuptBz5JS7lY87qBJ/vxCyed6ZHS94b8wBiOTHzL7xLvbw5VPF18Vb1B8mF8OYQhIfqFZHPRUFTqNTYdA0nEaBk+hkOIoQ70Wf2+I1ymcBpyv+VxB/gqpTaX8WYTwlE8uk0dSJih8LHd/HqxaoBY+lXRbx+DWSGE1hlIc+FGanVWjnVtcfzcTGYu+dcxuxZIOVFZ/cSzSizUbJraz3Eq3/HC866njr3qIXru20xOzKrgojMaxq7fzKrmlyhCWtYpFFYp0WybDIQjWJYektrpBFj2Ep+VaYv/Tg2R2/j6KH7A9/K3XejfOxhv0Gq0u+e7mW5SIrUjTsdddQ48HdoiAES3OrDjl3jujlPob1JVfOx/n36fL94dxRyKBoUTIrJI7X/GtVNOz1xigFuvf2NvLArXWHOkegt0MWXQqW69USO+6vVi8YtmXXqa7OAlklLKqELARtDVmokrKo02lZsNzMsZIrPiBLsELWipIpAjbQzHXiPrTuLDQrWTevVKFEozm4ozeDDglZmkUWie57r6AxWY65Q7Dczh19Ja1o0rC9yOLaSm9hGuzZpT6T5S26Etu/lu0l1YYF6PPOYIvhpWJnJNmOEktIZJkssnbUj6xRFj+9Or52abaC3QbsOsGyyFaxzTiLLFgiwdJUfkLWOIsslCMrzVYWu06z5Y0tTwG7gY3tBWTxdEbE9gJ7EFksnmjVIavYGdeFt9YOVp85XB9l16PnFbJ6WCVLn7EE9YVsEz9jH6WsFayQxdlOg4kt9altaP89Amhi7EqyusEVsugvsmgvf7sJGgKLrAVsRPTBJ4ZFFpqlaM67po1vYf2sgiwM/T2fXhd5+YponpFMWTyf8fkoVBmy0BMsAlXmgjuqvN3iD/KJAXYZyvJxM78Xz8Bm0TO5dea9eDKzX1MqsWXtZ37dpZI7WAQdghVFd6gixDuOVfQB1ijZfqebGTU/wZrvklXMFe+S9jssspo+Nqio7XdnZG3aaNrafGdH1t6Yj1izAmQ1RyCRNjGjmDeiXmYU6dmIPQ6yZzJ2MJNJz4KM4VnODCpj9pUzc8ub9eXPGPNnm/kzVWA78mfI+bPr/Jl5/qwe2EKnwnXe/0Sm0Rn/A/pM8R8w2HBWwA4YRgAAAABJRU5ErkJggg=="
// 样式注入
GM_addStyle(`
#content h1 { margin-bottom: 0px !important; padding-bottom: 0px !important; line-height: 1.1 !important; }
#dme-header-ratings {
display: inline-flex !important;
flex-wrap: wrap;
width: 100%;
align-items: center;
margin: 4px 0 10px 0 !important;
padding: 0 !important;
gap: 8px;
user-select: none;
clear: both;
}
.drdm-capsule {
display: inline-flex;
align-items: center;
border-radius: 4px;
font-size: 13px;
height: 24px;
overflow: hidden;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
font-family: Arial, sans-serif;
}
.drdm-capsule a { text-decoration: none !important; display: flex; align-items: center; height: 100%; }
/* 烂西红柿胶囊 */
.drdm-capsule.rt-cap { background: #fff; border: 1px solid #ddd; }
.drdm-rt-chip { display: flex; align-items: center; padding: 0 6px; height: 100%; font-weight: bold; color: #fff; }
.drdm-rt-chip.critics { background: #fa320a; }
.drdm-rt-chip.audience { background: #ffb300; color: #2c2c2c; }
/* Metacritic 双拼胶囊样式 */
.mc-cap {
background: #fff;
border: 1px solid #ddd;
display: inline-flex !important;
align-items: stretch;
height: 24px;
margin-right: 8px;
border-radius: 4px;
overflow: hidden;
}
.drdm-mc-chip {
padding: 0 7px;
display: flex;
align-items: center;
font-weight: bold;
font-size: 13px;
height: 100%;
}
.drdm-mc-chip.users {
background: #fccb30;
color: #000;
border-left: 1px solid #ddd;
}
/* 统一黄色系胶囊 (Meta, Letterboxd, Allocine) */
.drdm-capsule.yellow-cap { background: #ffcc33; border: 1px solid #d9aa00; color: #111; padding: 0 7px; font-weight: bold; }
.drdm-capsule img { height: 14px; margin-right: 5px; }
.view-original-btn {
display: block !important;
margin: 12px auto 0 !important;
padding: 4px 0 !important;
border: 1px solid #556783 !important;
color: #556783 !important;
border-radius: 2px !important;
font-size: 13px !important;
text-decoration: none !important;
text-align: center !important;
width: 120px !important;
background: #f0f3f5 !important;
}
`);
const movieId = window.location.pathname.split('/')[2];
const orangeColor = "#f0ad4e";
const tmdbAuth = "Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIyMzc1ZGIzOTYwYWVhMWI1OTA1NWMwZmM3ZDcwYjYwZiIsInN1YiI6IjYwYmNhZTk0NGE0YmY2MDA1OWJhNWE1ZSIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.DU51juQWlAIIfZ2lK99b3zi-c5vgc4jAwVz5h2WjOP8";
// 核心:强制排序插入逻辑
const CAPSULE_ORDER = ['header-rt-score', 'header-alc-score', 'header-mtc-score', 'header-anilist-score', 'header-lbx-score'];
function renderHeaderCapsule(id, html) {
if (document.getElementById(id)) return;
const container = getHeaderContainer();
if (!container) return;
const tempWrap = document.createElement('div');
tempWrap.innerHTML = html;
const newEl = tempWrap.firstElementChild;
newEl.id = id;
// 查找位置:遍历顺序表,寻找当前 ID 之后的已存在元素
const myIndex = CAPSULE_ORDER.indexOf(id);
let inserted = false;
for (let i = myIndex + 1; i < CAPSULE_ORDER.length; i++) {
const afterEl = document.getElementById(CAPSULE_ORDER[i]);
if (afterEl) {
container.insertBefore(newEl, afterEl);
inserted = true;
break;
}
}
if (!inserted) container.appendChild(newEl);
}
function getHeaderContainer() {
let container = document.getElementById('drdm-header-scores');
if (container) return container;
container = document.createElement('div');
container.id = 'drdm-header-scores';
// --- 核心样式调整 ---
// gap: 10px -> 统一所有胶囊之间的水平间距
// margin-top: 12px -> 调大胶囊与影视标题之间的距离
// margin-left: 0px -> 确保与标题左端对齐(如果还觉得不齐,可尝试 -1px)
container.setAttribute('style', `
display: flex !important;
flex-wrap: wrap;
gap: 10px;
margin-top: 12px;
margin-bottom: 10px;
align-items: center;
clear: both;
margin-left: 0px;
`);
// 插入在标题(h1)之后
const titleHeader = document.querySelector('#content h1');
if (titleHeader) {
titleHeader.after(container);
return container;
}
return null;
}
// --- 1. 排行榜终极修复:适配动画页面 rank-label 并统一合并 ---
function initRankings() {
const titleH1 = document.querySelector('#content h1');
if (!titleH1) { setTimeout(initRankings, 100); return; }
// 创建唯一的 Flex 容器
let container = document.getElementById('unified-rank-wrapper');
if (!container) {
container = document.createElement('div');
container.id = 'unified-rank-wrapper';
// 关键:margin-top 设为 10px 确保和标题有间距,底部 12px
container.style.cssText = "display: flex !important; flex-wrap: wrap !important; gap: 8px !important; margin: 10px 0 12px 0 !important; padding: 0 !important; align-items: center !important; clear: both !important; width: 100% !important;";
// 确保容器在标题上方
titleH1.parentNode.insertBefore(container, titleH1);
}
const renderBadge = (no, name, url, isNative = false) => {
const badgeId = isNative ? 'native-sync-badge' : `rank-${name}`;
if (document.getElementById(badgeId)) return;
const html = `
${no}
${name}
`;
container.insertAdjacentHTML('beforeend', html);
};
const syncNativeRank = () => {
// 增加对动画页面 .rank-label 的识别
const nativeSelectors = '.top250, .top-list-rank, .rank-banner, .rank-label, .billboard';
document.querySelectorAll(nativeSelectors).forEach(el => {
if (el.id === 'unified-rank-wrapper' || el.closest('#unified-rank-wrapper')) return;
// 尝试多种方式提取排名 (No.XX)
let noText = "No.";
const noEl = el.querySelector('.rank-label-no, .top250-no, .rank, [class*="no"]');
if (noEl) {
noText = noEl.innerText.trim();
} else {
const match = el.innerText.match(/No\.?\s?\d+/i);
if (match) noText = match[0].replace(/\s/g, '');
}
// 提取榜单名称
let titleText = "豆瓣高分榜";
const linkEl = el.querySelector('.rank-label-link a, .top250-link, a');
if (linkEl) {
titleText = linkEl.innerText.trim();
} else {
titleText = el.innerText.replace(noText, '').replace(/[\s\(\)()]/g, '').trim();
}
if (titleText.length > 15) titleText = "豆瓣高分榜";
const url = el.querySelector('a')?.href || window.location.href;
renderBadge(noText, titleText, url, true);
el.style.display = 'none !important'; // 隐藏原标签
el.remove(); // 彻底移除防止占位
});
};
// 立即执行 + 监听异步加载
syncNativeRank();
const obs = new MutationObserver(syncNativeRank);
obs.observe(document.body, { childList: true, subtree: true });
// 获取 BGM / MAL 等第三方榜单
const movieId = (window.location.pathname.match(/subject\/(\d+)/) || [])[1];
GM_xmlhttpRequest({
method: 'GET', url: 'https://bimzcy.github.io/rank4douban/data.json',
onload: function(res) {
if (res.status === 200) {
try {
const data = JSON.parse(res.responseText);
for (const k in data) {
if (k === 'top250' || data[k].short_title.includes("Top250")) continue;
if (data[k].list[movieId]) {
renderBadge((data[k].prefix || 'No.') + data[k].list[movieId], data[k].short_title, data[k].href);
}
}
} catch(e){}
}
}
});
}
// 判定是否为动画/动漫页面
function isAnime() {
var genres = document.querySelectorAll('span[property="v:genre"]');
for (var i = 0; i < genres.length; i++) {
if (genres[i].textContent.includes('动画')) return true;
}
// 兜底逻辑:如果在标签(Tags)里发现“动画”或“动漫”
var tags = document.querySelector('#db-tags-section');
if (tags && (tags.textContent.includes('动画') || tags.textContent.includes('动漫'))) return true;
return false;
}
// --- 影视名称(基于 Box Office Mojo 页面高效解析,避免额外请求 IMDb) ---
function addMediaTitle() {
// 防止重复添加
if ($('#info').find('span.pl:contains("影视名称")').length) return;
var year = '';
try { year = $('span.year').text().match(/\d+/)[0]; } catch (e) {}
var ch_name = $('h1').find('span:first').text().split(' ')[0];
// 清洗规则
function formatName(str) {
return str.replace(/\s+/g, ' ')
.replace(/ /g, '.')
.replace(/:\./, '.')
.replace('-.', '-')
.replace('..', '.')
.replace('.-', '-')
.replace(/\.+$/, ''); // ★ 确保末尾没有 .
}
function appendName(en_name) {
var raw = en_name ? `${ch_name} ${en_name} ${year}` : `${ch_name} ${year}`;
var name = formatName(raw);
var html = `影视名称: ${name}
`;
$('#info').append(html);
}
// 兜底逻辑:无 IMDb 时从“又名”中提取英文名
function fallbackAkaName() {
var en_name = null;
try {
var akaNode = $('#info span.pl:contains("又名")')[0];
if (akaNode && akaNode.nextSibling) {
akaNode.nextSibling.textContent.trim().split('/').forEach(function (e) {
e = e.trim();
if (e && e.match(/^[a-zA-Z0-9 '-:]*$/)) en_name = e;
});
}
} catch (err) {}
appendName(en_name);
}
var imdbMatch = $('#info').html().match(/tt\d+/i);
if (!imdbMatch) { fallbackAkaName(); return; }
// 改为请求 Box Office Mojo 页面来获取影片英文标题
GM_xmlhttpRequest({
method: 'GET',
url: 'https://www.boxofficemojo.com/title/' + imdbMatch[0] + '/',
timeout: 15000,
headers: {
'Accept-Language': 'en-US,en;q=0.9',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
},
onload: function (res) {
try {
var doc = new DOMParser().parseFromString(res.responseText, 'text/html');
// Box Office Mojo 页面标题通常形如: "Movie Title - Box Office Mojo"
var pageTitle = doc.querySelector('title')?.textContent || '';
var en_name = pageTitle.split('-')[0].trim();
if (!en_name || en_name.toLowerCase().includes('box office mojo')) {
// 备用选择:尝试抓取 Mojo 页面上的大标题
en_name = doc.querySelector('h1.a-size-extra-large, h1[class*="a-size-extra-large"]')?.textContent?.trim() || '';
}
// 剧集:拼接季号 Sxx
if ($('span.pl:contains("季数")').length) {
var number = $('#season option:selected').text().trim();
if (!number) {
var sn = $('span.pl:contains("季数")')[0];
if (sn && sn.nextSibling) number = sn.nextSibling.textContent.trim();
}
if (number) {
if (number.length < 2) number = '0' + number;
en_name = en_name + ' S' + number;
}
}
appendName(en_name || null);
} catch (err) {
console.warn('影视名称生成失败:', err);
fallbackAkaName();
}
},
onerror: fallbackAkaName,
ontimeout: fallbackAkaName
});
}
// --- 2. 核心控制器 ---
async function initMain() {
const movieId = location.href.match(/subject\/(\d+)/)?.[1];
const infoDiv = document.querySelector('#info');
const ratingWrap = document.querySelector('.rating_wrap.clearbox') || document.querySelector('.rating_logo')?.parentNode;
if (!infoDiv || !ratingWrap) { setTimeout(initMain, 100); return; }
// 影视名称
addMediaTitle();
let sideArea = document.getElementById("custom-side-area") || (()=>{
const div = document.createElement('div');
div.id = "custom-side-area";
ratingWrap.appendChild(div);
return div;
})();
// 侧边栏
sideArea.innerHTML = `
`;
const imdbMatch = infoDiv.innerText.match(/IMDb:\s*(tt\d+)/) || infoDiv.innerText.match(/tt\d+/);
const imdbId = imdbMatch ? (imdbMatch[1] || imdbMatch[0]) : null;
// --- 修复后的 IMDb 扩展评分加载逻辑 ---
if (imdbId) {
// 使用统一的 Wikidata 桥梁函数
getExtraIdsFromWikidata(imdbId, function(ids) {
// 这里的 data.Title 来自 OMDb 请求的结果
var movieTitle = (typeof data !== 'undefined' && data.Title) ? data.Title : document.title.split('(豆瓣)')[0].trim();
// 1. 加载 Allocine
if (ids.allocineId) {
fetchAllocineDualScores(ids.allocineId);
}
// 2. 加载 Metacritic
// 注意:即便 Wikidata 没查到 mcId,函数内部也有标题兜底逻辑
fetchMetacriticDualScores(ids.mcId, movieTitle);
// 3. 渲染大中华差异对比(新功能)
if (ids.claims) {
renderKanjiZoneComparison(ids.labels);
renderBoxOfficeModule(ids.claims, imdbId).catch(error => {
console.warn('电影收支模块渲染失败:', error);
});
renderRatingModule(ids.claims);
}
});
}
if (imdbId) {
processOMDb(imdbId);
processRottenTomatoes(imdbId);
processTMDB(imdbId);
processNeoDB(movieId);
processMyDramaList();
processCommonSenseMedia();
processLetterboxd(imdbId);
// === 动漫专属逻辑开始 ===
if (isAnime()) {
// AniList 通常也是动漫专用,建议也放在这里
processAniList(imdbId);
// 获取当前豆瓣页面的电影/动画标题,用于搜索
const titleHeader = document.querySelector('h1 [property="v:itemregulated"]') || document.querySelector('h1 span');
const title = titleHeader ? titleHeader.innerText.replace(window.location.pathname.split('/')[2], '').trim() : '';
if (title) {
processBangumi(title);
processMAL(title);
}
}
// === 动漫专属逻辑结束 ===
}
}
// --- 3. 各平台逻辑 ---
function processRottenTomatoes(imdbId) {
const sparql = `SELECT * WHERE {?s wdt:P345 "${imdbId}". OPTIONAL { ?s wdt:P1258 ?RTID. }}`;
GM_xmlhttpRequest({
method: "GET", url: `https://query.wikidata.org/sparql?format=json&query=${encodeURIComponent(sparql)}`,
onload: res => {
try {
const rtId = JSON.parse(res.responseText).results.bindings[0]?.RTID?.value;
if (!rtId) return;
const rtUrl = `https://www.rottentomatoes.com/${rtId}`;
GM_xmlhttpRequest({
method: "GET", url: rtUrl,
onload: res => {
const doc = new DOMParser().parseFromString(res.responseText, "text/html");
const jsonEl = doc.getElementById("media-scorecard-json");
if (jsonEl) {
const json = JSON.parse(jsonEl.textContent);
let tScore = json.criticsScore?.score;
let aScore = json.audienceScore?.score;
if (tScore || aScore) {
let html = ``;
renderHeaderCapsule('header-rt-score', html);
}
}
}
});
} catch(e) {}
}
});
}
/* --- Metacritic 修复版核心逻辑 --- */
// 2. 抓取与解析
function fetchMetacriticDualScores(mcId, title) {
let path = mcId || ("movie/" + title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, ''));
const url = "https://www.metacritic.com/" + path;
GM_xmlhttpRequest({
method: "GET",
url: url,
headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0" },
onload: function(resp) {
if (resp.status !== 200) return;
const parser = new DOMParser();
const doc = parser.parseFromString(resp.responseText, "text/html");
// 使用最新的选择器
const criticEl = doc.querySelector('div[data-testid="global-score-wrapper"]:has(a[href*="critic-reviews"]) span[data-testid="global-score-value"]');
const userEl = doc.querySelector('div[data-testid="global-score-wrapper"]:has(a[href*="user-reviews"]) span[data-testid="global-score-value"]');
const criticScore = criticEl ? criticEl.textContent.trim() : "N/A";
const userScore = userEl ? userEl.textContent.trim() : "N/A";
if (criticScore !== "N/A" || userScore !== "N/A") {
renderMetacriticDoubleCapsule(path, criticScore, userScore);
}
}
});
}
// 3. 渲染
function renderMetacriticDoubleCapsule(path, critic, user) {
// 1. 定义 Metacritic M 图标的 Base64
const mcIconBase64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACABAMAAAAxEHz4AAAAElBMVEUAAAAzMzP/zDT+/v5oX0elmG641eaPAAAAAXRSTlMAQObYZgAAA3ZJREFUaN69mVFym0AQBVO6ARL8WxQ5gGulAyzGBwDL979K4tjmie2dmVKo8nxZpKbV83YFBH65dej/lv3PYbPqf9tVTw/29yggwq9nPfL1uwi9Xfv6GUTcz4r7owrzD2tHP2KIB3j/qMVQCPtvU/NZ7bSAEA9wy81dCaEhfIHcFDVCwRP43bBeQ0LRj+qCIQ7oB8FXYD8JnsKB+bFGRwH9PuHJFBjQNE3T+ndrKpgBtG8ppeuqdSwUIDCX/emz1uOLC6BASgWhq89gJfiS1spFjk81gQEDqC7KEYBYQENIgQAuYbqvq61wMARO6qYCAVyCtwSF+4XABGdjAihoBn+CLqW6wkiAImQEVGiLGbSLsYiGwlIFNMzQUjhqBq4BM6RCWwOcLcC1oqAQBMgGIG9muW7WgRHwl/SsNO4UjgQMBiDrTykoBC+C9k75VCoQkBvU/Vlgq6AQBCguhB/yawRQ2ISAXaDQBIBCVwIG7GEBoLBNkRl2CQAqlIC5NoBCpMLyDdAiUEDLSIWxAJg/wlxX0DIIwGuBZqBCJ4BWEechKOhguwUMmKCqoGMCYBuk5Cno0BZwRgSGgo4QwEWkwnpAG0H7iBlSQZ8dgKugj+MGAEtLQZ8CwPX1zVLIDuA0fwEu64aAwqVxACl9AbSgUMh1wLff5aSzQAcFFQDrIv0Tn7GrcwRQft8TQCEGpAIAhQBwAgAKNmCWgABQ8AEnABwF/Rb0a3wBAAo+oEuqOVLg+QDXo1ABpzTeBEhBWBVOqhuFeaOgg6oWp3VPQcd0GBcWR4ECvDJlV4ECBIQKMzYiLu+uwtXYiNoIngIEeIMxNJ4CBLSKulF1FSDQCaAUHYVSoDkCMLsKOB8tAAyNo6BihrrTtBVYHQF94yk4ESiERxTGCmB4RKEXADNAIYhAgOwpcAICzoECJzD+8x4rdABoHajgTsAHGFSIJ5BCU1F4NnYRANb9asNa+Binp4IEGKENmKUQCvTmkzApxAJSYAq2gABU6LMU/CXow+eRbVWg7SWAGbAdKwKjBKDAXwT7OwhQYTBOxXikCgUSuAICsHrGwH5MAAUQECAFqEAC+32F/sb85M8BqND/LpZgwjsSS0ESGV8vAVdBFrfpo3l6f+g1T1z7X1TtJOx+WRdV1B9X1B+XG+A+wk+9NiaB+nGh/Wde3hMSN/8BSXUWJsXAq8kAAAAASUVORK5CYII="
// 2. 核心颜色逻辑
let bgColor = "#ffcc33"; // 默认黄色 (40-60)
let textColor = "#000"; // 默认黑色
const score = parseInt(critic);
if (!isNaN(score)) {
if (score <= 39) {
bgColor = "#ff0000"; // 红色
textColor = "#fff"; // 白色
} else if (score >= 61) {
bgColor = "#66cc33"; // 绿色
textColor = "#fff"; // 白色
}
}
// 3. 构造 HTML
const html = '';
// 4. 渲染
if (typeof renderHeaderCapsule === 'function') {
renderHeaderCapsule('header-mtc-score', html);
}
}
/* --- Metacritic 联动逻辑结束 --- */
let myDramaListContentRating = null;
let latestRatingModuleClaims = null;
let commonSenseMediaRating = null;
let latestCommonSenseRequestKey = null;
function getCommonSenseMediaSearchInfo() {
const titleNodes = [
document.querySelector('h1 [property="v:itemreviewed"]'),
document.querySelector('h1 [property="v:itemregulated"]'),
document.querySelector('h1 span'),
document.querySelector('h1')
].filter(Boolean);
const titles = [];
titleNodes.forEach(node => {
const title = node.textContent
.replace(/\(\d{4}\)\s*$/, '')
.trim();
if (title && !titles.includes(title)) {
titles.push(title);
}
});
const infoText = document.querySelector('#info')?.innerText || '';
const originalTitle =
infoText.match(/(?:原名|又名):\s*([^\n/]+)/)?.[1]?.trim();
if (originalTitle && !titles.includes(originalTitle)) {
titles.push(originalTitle);
}
const aliasesLine = infoText.match(/又名:\s*([^\n]+)/)?.[1];
if (aliasesLine) {
aliasesLine.split('/').forEach(item => {
const alias = item.trim();
if (alias && !titles.includes(alias)) {
titles.push(alias);
}
});
}
const year =
document.querySelector('.year')?.textContent.match(/\d{4}/)?.[0] ||
'';
return { titles, year };
}
function slugifyCommonSenseMediaTitle(title) {
return String(title || '')
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.toLowerCase()
.replace(/&/g, ' and ')
.replace(/[^a-z0-9\s]/g, '')
.trim()
.replace(/\s+/g, '-');
}
function parseCommonSenseMediaRating(doc, rawHtml = '') {
const selectors = [
'.rating__age',
'[class*="rating__age"]',
'[data-testid*="age"]'
];
let text = '';
for (const selector of selectors) {
const node = doc.querySelector(selector);
if (node?.textContent?.trim()) {
text = node.textContent;
break;
}
}
// 页面结构变更或服务端 HTML 与 DOM 结构不一致时的兜底。
if (!text && rawHtml) {
const patterns = [
/class=["'][^"']*rating__age[^"']*["'][^>]*>\s*([^<]+)/i,
/["']recommendedAge["']\s*:\s*["']?(\d{1,2}\+?)["']?/i,
/["']age["']\s*:\s*["']?(\d{1,2}\+?)["']?/i
];
for (const pattern of patterns) {
const match = rawHtml.match(pattern);
if (match?.[1]) {
text = match[1];
break;
}
}
}
const ageMatch = String(text).match(/\b(\d{1,2})\s*\+?\b/);
if (!ageMatch) return null;
return `${ageMatch[1]}+`;
}
function requestCommonSenseMedia(url) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url,
timeout: 15000,
headers: {
Accept: 'text/html,application/xhtml+xml',
'Accept-Language': 'en-US,en;q=0.9'
},
onload(response) {
if (response.status >= 200 && response.status < 400) {
resolve(response.responseText);
return;
}
reject(
new Error(
`Common Sense Media 请求失败,状态码:${response.status}`
)
);
},
ontimeout() {
reject(new Error('Common Sense Media 请求超时'));
},
onerror() {
reject(new Error('Common Sense Media 网络请求失败'));
}
});
});
}
async function fetchCommonSenseMediaRating() {
const { titles } = getCommonSenseMediaSearchInfo();
for (const title of titles) {
const slug = slugifyCommonSenseMediaTitle(title);
if (!slug) continue;
const url =
`https://www.commonsensemedia.org/movie-reviews/${slug}`;
try {
const html = await requestCommonSenseMedia(url);
const doc = new DOMParser().parseFromString(html, 'text/html');
const rating = parseCommonSenseMediaRating(doc, html);
if (rating) {
return rating;
}
} catch (error) {
// 标题/别名有多个,单个 URL 不存在时继续尝试。
console.debug(`Common Sense Media 查询失败:${title}`, error);
}
}
return null;
}
function processCommonSenseMedia() {
const { titles, year } = getCommonSenseMediaSearchInfo();
if (!titles.length) return;
// 防止页面上的其他异步逻辑或重复初始化重复请求。
const requestKey = `${titles.join('|')}|${year}`;
if (latestCommonSenseRequestKey === requestKey) return;
latestCommonSenseRequestKey = requestKey;
fetchCommonSenseMediaRating()
.then(rating => {
commonSenseMediaRating = rating || null;
if (latestRatingModuleClaims) {
renderRatingModule(latestRatingModuleClaims);
}
})
.catch(error => {
commonSenseMediaRating = null;
console.warn('Common Sense Media 信息加载失败:', error);
if (latestRatingModuleClaims) {
renderRatingModule(latestRatingModuleClaims);
}
});
}
function parseMyDramaListContentRating(doc, rawHtml = '') {
const ratingNode = doc.querySelector('li.content-rating');
let text = ratingNode?.textContent || '';
if (!text && rawHtml) {
const match = rawHtml.match(
/Content\s*Rating:\s*<\/[^>]+>\s*([^<\n]+?)(?:\s*<\/li>|<)/i
);
text = match?.[1] || '';
}
text = text
.replace(/Content\s*Rating\s*:/i, '')
.trim();
if (
!text ||
/^(?:not\s*yet\s*rated|unrated|unknown|n\/a|none|null|-)+$/i.test(text)
) {
return null;
}
// 必须从字符串开头匹配。
const match = text.match(
/^(?:\d{1,2}\s*\+|G|PG(?:\s*-\s*\d{1,2})?|NC\s*-\s*17|R\s*-\s*\d{1,2}|R15\+|R18\+|PG12|I{1,3}[AB]?|AL|\d{1,2})(?=\s|$|-)/
);
if (!match) return null;
return match[0]
.replace(/\s+/g, '')
.toUpperCase();
}
async function fetchMyDramaListContentRating(result) {
if (!result?.url) return null;
const html = await requestMyDramaList(result.url);
const doc = new DOMParser().parseFromString(
html,
'text/html'
);
return parseMyDramaListContentRating(doc, html);
}
function processMyDramaList() {
const block = document.getElementById('block-mydramalist');
if (!block) return;
const titleInfo = getMyDramaListSearchInfo();
if (!titleInfo.titles.length) return;
searchMyDramaListTitle(titleInfo)
.then(async result => {
if (!result) return;
// 同一详情页只请求一次:
// 评分和 Content Rating 一起解析。
const html = await requestMyDramaList(result.url);
const doc = new DOMParser().parseFromString(
html,
'text/html'
);
const ratingData = parseMyDramaListRating(doc, html);
const contentRating = parseMyDramaListContentRating(doc, html);
return {
...result,
...ratingData,
contentRating
};
})
.then(data => {
if (!data) return;
// 原本的 MyDL 评分侧栏。
if (data.score) {
renderMyDramaListRating(data);
}
// MyDL 分级成功时,保存并刷新“影视分级”模块。
// `null` 也要写入缓存,确保 Not Yet Rated 时旧 MyDL 卡片被移除。
myDramaListContentRating = data.contentRating || null;
if (latestRatingModuleClaims) {
renderRatingModule(latestRatingModuleClaims);
}
})
.catch(error => {
console.warn('MyDramaList 信息加载失败:', error);
});
}
function getMyDramaListSearchInfo() {
const titleNodes = [
document.querySelector('h1 [property="v:itemreviewed"]'),
document.querySelector('h1 [property="v:itemregulated"]'),
document.querySelector('h1 span'),
document.querySelector('h1')
].filter(Boolean);
const titles = [];
titleNodes.forEach(node => {
const title = node.textContent
.replace(/\(\d{4}\)\s*$/, '')
.trim();
if (title && !titles.includes(title)) {
titles.push(title);
}
});
const infoText = document.querySelector('#info')?.innerText || '';
const originalTitle =
infoText.match(/(?:原名|又名):\s*([^\n/]+)/)?.[1]?.trim();
if (originalTitle && !titles.includes(originalTitle)) {
titles.push(originalTitle);
}
const aliasesLine =
infoText.match(/又名:\s*([^\n]+)/)?.[1];
if (aliasesLine) {
aliasesLine.split('/').forEach(item => {
const alias = item.trim();
if (alias && !titles.includes(alias)) {
titles.push(alias);
}
});
}
const year =
document.querySelector('.year')?.textContent.match(/\d{4}/)?.[0] ||
'';
return {
titles,
year
};
}
async function searchMyDramaListTitle(titleInfo) {
const resultMap = new Map();
// 搜索所有标题,再统一选择最可信的候选。
// 不能在单个关键词找到一个弱匹配后立即返回。
for (const title of titleInfo.titles) {
if (!title) continue;
try {
const html = await requestMyDramaList(
`https://mydramalist.com/search?q=${encodeURIComponent(title)}`
);
const doc = new DOMParser().parseFromString(html, 'text/html');
const candidates = parseMyDramaListSearchResults(doc);
candidates.forEach(candidate => {
const existing = resultMap.get(candidate.url);
if (!existing) {
resultMap.set(candidate.url, candidate);
return;
}
// 同一个详情页可能被多个链接解析出来,合并标题。
candidate.titles.forEach(candidateTitle => {
if (!existing.titles.includes(candidateTitle)) {
existing.titles.push(candidateTitle);
}
});
if (!existing.year && candidate.year) {
existing.year = candidate.year;
}
});
} catch (error) {
console.warn(`MyDramaList 搜索失败:${title}`, error);
}
}
if (!resultMap.size) return null;
return chooseMyDramaListCandidate(
[...resultMap.values()],
titleInfo.titles,
titleInfo.year
);
}
function parseMyDramaListSearchResults(doc) {
const resultMap = new Map();
// 优先从真正的搜索结果卡片中取详情链接,避免抓到导航栏、
// 推荐区、排行榜及页面中其他无关链接。
const containers = [
...doc.querySelectorAll(
'.film-list .item, ' +
'.film-list .box, ' +
'.search-results .item, ' +
'.search-results .box, ' +
'.col-lg-8 .box'
)
];
containers.forEach(card => {
const links = [...card.querySelectorAll('a[href]')];
const detailLink = links.find(link => {
const href = link.getAttribute('href') || '';
return /^\/\d+-[^/?#]+\/?(?:[?#].*)?$/.test(href);
});
if (!detailLink) return;
const href = detailLink.getAttribute('href') || '';
const url = new URL(href, 'https://mydramalist.com').href
.replace(/[?#].*$/, '')
.replace(/\/$/, '');
const titles = [];
const addTitle = value => {
const title = String(value || '')
.replace(/\s+/g, ' ')
.trim();
if (
title &&
title.length <= 150 &&
!titles.includes(title)
) {
titles.push(title);
}
};
addTitle(detailLink.getAttribute('title'));
addTitle(detailLink.textContent);
// 搜索卡片中可能同时包含英文标题和原始标题。
card.querySelectorAll(
'.title, .text-primary, .text-muted, h2, h3, h4'
).forEach(element => {
addTitle(element.textContent);
});
const cardText = (card.innerText || '')
.replace(/\s+/g, ' ')
.trim();
const year =
cardText.match(/\b(?:19|20)\d{2}\b/)?.[0] ||
'';
if (!titles.length) return;
const existing = resultMap.get(url);
if (existing) {
titles.forEach(title => {
if (!existing.titles.includes(title)) {
existing.titles.push(title);
}
});
if (!existing.year && year) {
existing.year = year;
}
return;
}
resultMap.set(url, {
title: titles[0],
titles,
year,
url
});
});
return [...resultMap.values()];
}
function chooseMyDramaListCandidate(candidates, sourceTitles, expectedYear) {
const validSourceTitles = sourceTitles
.map(title => ({
raw: String(title || '').trim(),
normalized: normalizeMyDramaListTitle(title)
}))
.filter(item => item.normalized);
let bestCandidate = null;
let bestScore = -Infinity;
let bestMatchType = '';
candidates.forEach(candidate => {
const candidateTitles = (
candidate.titles?.length
? candidate.titles
: [candidate.title]
)
.map(title => ({
raw: String(title || '').trim(),
normalized: normalizeMyDramaListTitle(title)
}))
.filter(item => item.normalized);
let titleScore = -Infinity;
let matchType = '';
validSourceTitles.forEach(sourceTitle => {
candidateTitles.forEach(candidateTitle => {
const match = scoreMyDramaListTitlePair(
sourceTitle.raw,
candidateTitle.raw
);
if (match.score > titleScore) {
titleScore = match.score;
matchType = match.type;
}
});
});
if (!Number.isFinite(titleScore)) return;
let totalScore = titleScore;
if (expectedYear && candidate.year) {
const yearDifference = Math.abs(
Number(expectedYear) - Number(candidate.year)
);
// 同一作品在不同地区上线可能跨一年。
if (yearDifference === 0) {
totalScore += 15;
} else if (yearDifference === 1) {
totalScore += 4;
} else {
// 年份相差两年以上时直接排除。
return;
}
}
if (totalScore > bestScore) {
bestScore = totalScore;
bestCandidate = candidate;
bestMatchType = matchType;
}
});
if (!bestCandidate) return null;
/*
* 严格标题相等可以直接接受。
* 拉丁标题的高相似匹配要求更高阈值。
* 单纯包含关系不能作为可靠匹配依据。
*/
const accepted =
bestMatchType === 'exact' ||
bestMatchType === 'exact-number-equivalent' ||
(
bestMatchType === 'latin-similar' &&
bestScore >= 88
);
if (!accepted) {
console.debug('MyDramaList 未找到可信匹配:', {
sourceTitles,
expectedYear,
bestCandidate,
bestScore,
bestMatchType
});
return null;
}
return bestCandidate;
}
function scoreMyDramaListTitlePair(sourceTitle, candidateTitle) {
const source = normalizeMyDramaListTitle(sourceTitle);
const candidate = normalizeMyDramaListTitle(candidateTitle);
if (!source || !candidate) {
return {
score: 0,
type: 'none'
};
}
if (source === candidate) {
return {
score: 100,
type: 'exact'
};
}
/*
* 统一常见的续集数字表达:
* 2、Ⅱ、II、Part 2 等可以互相比较。
*/
const sourceNumberNormalized =
normalizeMyDramaListSequenceNumber(sourceTitle);
const candidateNumberNormalized =
normalizeMyDramaListSequenceNumber(candidateTitle);
if (
sourceNumberNormalized &&
sourceNumberNormalized === candidateNumberNormalized
) {
return {
score: 98,
type: 'exact-number-equivalent'
};
}
// 任意一边包含中日韩文字时,只接受严格匹配。
// 不使用“共同字符数量”,否则中文短标题极易误匹配。
if (
containsMyDramaListCjk(sourceTitle) ||
containsMyDramaListCjk(candidateTitle)
) {
return {
score: 0,
type: 'cjk-mismatch'
};
}
// 续集编号不同,直接排除。
if (!haveCompatibleMyDramaListSequenceNumbers(
sourceTitle,
candidateTitle
)) {
return {
score: 0,
type: 'sequence-mismatch'
};
}
const sourceTokens = tokenizeMyDramaListLatinTitle(sourceTitle);
const candidateTokens = tokenizeMyDramaListLatinTitle(candidateTitle);
if (!sourceTokens.length || !candidateTokens.length) {
return {
score: 0,
type: 'none'
};
}
const tokenScore = getMyDramaListTokenSimilarity(
sourceTokens,
candidateTokens
);
const editScore = getMyDramaListEditSimilarity(
sourceTokens.join(''),
candidateTokens.join('')
);
// token 相似度占主导,编辑距离用于辅助处理轻微拼写差异。
const similarity = tokenScore * 0.7 + editScore * 0.3;
return {
score: similarity * 100,
type: similarity >= 0.85
? 'latin-similar'
: 'latin-mismatch'
};
}
function normalizeMyDramaListTitle(title) {
return String(title || '')
.toLowerCase()
.normalize('NFKC')
.replace(/\(\s*(?:19|20)\d{2}\s*\)/g, '')
.replace(/\b(?:19|20)\d{2}\b/g, '')
.replace(/&/g, 'and')
.replace(/[^\p{L}\p{N}]+/gu, '');
}
function normalizeMyDramaListSequenceNumber(title) {
return String(title || '')
.toLowerCase()
.normalize('NFKC')
.replace(/\(\s*(?:19|20)\d{2}\s*\)/g, '')
.replace(/\b(?:19|20)\d{2}\b/g, '')
.replace(/\bpart\s*(\d+)\b/gi, '$1')
.replace(/\bseason\s*(\d+)\b/gi, '$1')
.replace(/第\s*([一二三四五六七八九十\d]+)\s*[部季]/g, '$1')
.replace(/ⅰ{3}/g, '3')
.replace(/ⅱ/g, '2')
.replace(/\biii\b/g, '3')
.replace(/\bii\b/g, '2')
.replace(/[^a-z0-9\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]+/gu, '');
}
function containsMyDramaListCjk(value) {
return /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u
.test(String(value || ''));
}
function extractMyDramaListSequenceNumbers(title) {
const value = String(title || '')
.toLowerCase()
.normalize('NFKC')
.replace(/\b(?:19|20)\d{2}\b/g, '');
const numbers = new Set();
for (const match of value.matchAll(
/(?:\bpart\s*|\bseason\s*|第\s*)?(\d+)(?:\s*[部季])?/g
)) {
numbers.add(Number(match[1]));
}
if (/\bii\b|ⅱ/u.test(value)) numbers.add(2);
if (/\biii\b|ⅲ/u.test(value)) numbers.add(3);
return [...numbers];
}
function haveCompatibleMyDramaListSequenceNumbers(a, b) {
const numbersA = extractMyDramaListSequenceNumbers(a);
const numbersB = extractMyDramaListSequenceNumbers(b);
if (!numbersA.length && !numbersB.length) return true;
if (!numbersA.length || !numbersB.length) return false;
return numbersA.some(number => numbersB.includes(number));
}
function tokenizeMyDramaListLatinTitle(title) {
const ignoredWords = new Set([
'a',
'an',
'the',
'of',
'and',
'to',
'in',
'on',
'for'
]);
return String(title || '')
.toLowerCase()
.normalize('NFKD')
.replace(/[\u0300-\u036f]/g, '')
.replace(/\b(?:19|20)\d{2}\b/g, '')
.replace(/&/g, ' and ')
.split(/[^\p{L}\p{N}]+/u)
.filter(token => token && !ignoredWords.has(token));
}
function getMyDramaListTokenSimilarity(tokensA, tokensB) {
const setA = new Set(tokensA);
const setB = new Set(tokensB);
let intersection = 0;
setA.forEach(token => {
if (setB.has(token)) intersection++;
});
if (!intersection) return 0;
// Sørensen–Dice 系数。
return (2 * intersection) / (setA.size + setB.size);
}
function getMyDramaListEditSimilarity(a, b) {
if (!a || !b) return 0;
if (a === b) return 1;
const previous = Array.from(
{ length: b.length + 1 },
(_, index) => index
);
for (let i = 1; i <= a.length; i++) {
const current = [i];
for (let j = 1; j <= b.length; j++) {
current[j] = Math.min(
current[j - 1] + 1,
previous[j] + 1,
previous[j - 1] + (
a[i - 1] === b[j - 1] ? 0 : 1
)
);
}
previous.splice(0, previous.length, ...current);
}
return 1 - previous[b.length] / Math.max(a.length, b.length);
}
async function fetchMyDramaListRating(result) {
const html = await requestMyDramaList(result.url);
const doc = new DOMParser().parseFromString(html, 'text/html');
const ratingData = parseMyDramaListRating(doc, html);
if (!ratingData || !ratingData.score) return null;
return {
...result,
...ratingData
};
}
function parseMyDramaListRating(doc, rawHtml) {
let score = '';
let votes = '';
// 用户提供的页面结构:
// .col-film-rating .box.deep-orange => 8.5
score =
doc.querySelector('.col-film-rating .box.deep-orange')?.textContent.trim() ||
doc.querySelector('.col-film-rating .box')?.textContent.trim() ||
'';
// Ratings: 8.5/10 from 7,188 users
const ratingText = [
...doc.querySelectorAll('.hfs')
].find(element => /Ratings?\s*:/i.test(element.textContent || ''))
?.textContent
.replace(/\s+/g, ' ')
.trim() || '';
if (!score) {
score =
ratingText.match(/Ratings?\s*:\s*([\d.]+)\s*\/\s*10/i)?.[1] ||
'';
}
votes =
ratingText.match(/from\s+([\d,.\s]+)\s+users?/i)?.[1]?.trim() ||
'';
// 兼容结构变化
if (!score) {
score =
doc.querySelector('[itempropx="ratingValue"]')?.textContent.trim() ||
doc.querySelector('[itemprop="ratingValue"]')?.textContent.trim() ||
'';
}
// JSON-LD 兜底
if (!score) {
const jsonLdElements = [
...doc.querySelectorAll('script[type="application/ld+json"]')
];
for (const element of jsonLdElements) {
try {
const value = JSON.parse(element.textContent);
const entries = Array.isArray(value) ? value : [value];
for (const entry of entries) {
const aggregate = entry?.aggregateRating;
if (aggregate?.ratingValue) {
score = String(aggregate.ratingValue);
votes = String(
aggregate.ratingCount ||
aggregate.reviewCount ||
votes
);
break;
}
}
} catch (error) {
// 忽略无效 JSON-LD。
}
if (score) break;
}
}
// 原始 HTML 正则最终兜底
if (!score) {
score =
rawHtml.match(
/Ratings?\s*:\s*(?:<[^>]+>\s*)*([\d.]+)\s*(?:<[^>]+>\s*)*\/\s*10/i
)?.[1] ||
rawHtml.match(
/class=["'][^"']*\bbox\b[^"']*\bdeep-orange\b[^"']*["'][^>]*>\s*([\d.]+)/
)?.[1] ||
'';
}
if (!votes) {
votes =
rawHtml.match(/from\s+([\d,.\s]+)\s+users?/i)?.[1]?.trim() ||
'';
}
const numericScore = Number.parseFloat(score);
if (
!Number.isFinite(numericScore) ||
numericScore < 0 ||
numericScore > 10
) {
return null;
}
return {
score: numericScore.toFixed(1),
votes: votes.replace(/\s+/g, '')
};
}
function renderMyDramaListRating(data) {
const score = Number.parseFloat(data.score);
if (!Number.isFinite(score)) return;
// 完全复用 IMDb / TMDB / NeoDB 的侧栏评分模板。
// 因而字体、大小、星星、评价数、分隔线均保持一致。
renderScoreToBlock(
'block-mydramalist',
'MyDL评分',
score.toFixed(1),
data.votes || '0',
data.url
);
// MyDL 经常最后才返回;主动要求客观评分补算。
setTimeout(renderObjectiveRating, 0);
}
function requestMyDramaList(url) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: 'GET',
url,
headers: {
'Accept': 'text/html,application/xhtml+xml',
'Accept-Language': 'en-US,en;q=0.9',
'Referer': 'https://mydramalist.com/',
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' +
'AppleWebKit/537.36 (KHTML, like Gecko) ' +
'Chrome/131.0.0.0 Safari/537.36'
},
timeout: 15000,
onload(response) {
if (
response.status >= 200 &&
response.status < 400 &&
response.responseText
) {
resolve(response.responseText);
return;
}
reject(
new Error(
`MyDramaList 请求失败,状态码:${response.status}`
)
);
},
ontimeout() {
reject(new Error('MyDramaList 请求超时'));
},
onerror() {
reject(new Error('MyDramaList 网络请求失败'));
}
});
});
}
function escapeMyDramaListHtml(value) {
return String(value ?? '')
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
// ================================================================
// 核心逻辑:Wikidata 桥梁与双评分系统
// ================================================================
// 1. Wikidata 统一查询 (一次请求获取多个 ID 和各地区译名)
function getExtraIdsFromWikidata(imdbId, callback) {
if (!imdbId) return;
const searchUrl = "https://www.wikidata.org/w/api.php?action=query&list=search&srsearch=haswbstatement:P345=" + imdbId + "&format=json&srnamespace=0";
GM_xmlhttpRequest({
method: "GET", url: searchUrl,
onload: function(res) {
try {
const searchData = JSON.parse(res.responseText);
const entityId = searchData.query.search[0]?.title;
if (!entityId) { callback({}); return; }
const entityUrl = "https://www.wikidata.org/w/api.php?action=wbgetentities&ids=" + entityId + "&props=claims|labels&format=json";
GM_xmlhttpRequest({
method: "GET", url: entityUrl,
onload: function(res2) {
try {
const data = JSON.parse(res2.responseText);
const entity = data.entities[entityId];
callback({
mcId: entity.claims.P1712?.[0]?.mainsnak?.datavalue?.value,
allocineId: entity.claims.P1265?.[0]?.mainsnak?.datavalue?.value,
labels: entity.labels || {},
claims: entity.claims || {} // 必须包含这一行
});
} catch (e) { callback({}); }
}
});
} catch (e) { callback({}); }
}
});
}
// 影视分级全能版
function renderRatingModule(claims) {
if (!claims) return;
// MyDL 异步分级返回后,需要拿这份 Wikidata 数据重新渲染。
latestRatingModuleClaims = claims;
const RATING_DB = {
// MPA (P1657)
"Q219557": "G (全年龄)", "Q18665337": "G (全年龄)",
"Q219555": "PG (建议陪同)", "Q18665338": "PG (建议陪同)", "Q18665334": "PG (建议陪同)", // 千与千寻专用
"Q219554": "PG-13 (13+)", "Q18665339": "PG-13 (13+)",
"Q219552": "R (17+)", "Q18665340": "R (17+)", "Q18665344": "R (17+)",
"Q219553": "NC-17 (18+)", "Q18665341": "NC-17 (18+)",
// EIRIN (P2756 & P1144 - 日本)
"Q23790275": "G (全年龄)", "Q6708782": "G (全年龄)",
"Q23790278": "PG12 (12+)", "Q23790279": "PG12 (12+)", "Q6708785": "PG12 (12+)",
"Q23790281": "R15+ (15+)", "Q6708788": "R15+ (15+)",
"Q23790282": "R18+ (18+)", "Q6708790": "R18+ (18+)",
// Kijkwijzer (P2684 & P1142 - 荷兰)
"Q23649977": "AL (全年龄)", "Q12061936": "AL (全年龄)",
"Q23649978": "6 (6+)", "Q12061937": "6 (6+)",
"Q23649981": "9 (9+)", "Q23649982": "9 (9+)","Q12061938": "9 (9+)",
"Q23649983": "12 (12+)", "Q12061939": "12 (12+)",
"Q108130768": "14 (14+)",
"Q23649984": "16 (16+)", "Q12061940": "16 (16+)",
"Q112246473": "18 (18+)",
// ClassInd (P3216 & P2334 - 巴西)
"Q26678725": "L (全年龄)", "Q26678731": "L (全年龄)",
"Q26678730": "10 (10+)",
"Q26678733": "12 (12+)",
"Q26678735": "14 (14+)",
"Q26678737": "16 (16+)",
"Q26678741": "18 (18+)",
// IFCO (P1981 - 爱尔兰)
"Q74434521": "G (全年龄)","Q74434523": "G (全年龄)",
"Q74434526": "PG (建议陪同)",
"Q74434531": "12A (12+)",
"Q74434535": "15A (15+)",
"Q74434539": "16 (16+)",
"Q74434542": "18 (18+)",
// Hong Kong (P8874 - 香港)
"Q10563603": "I (全年龄)","Q100711980": "I (全年龄)",
"Q10563604": "IIA (12+)",
"Q10563605": "IIB (15+)","Q100711984": "IIB (15+)",
"Q10563606": "III (18+)",
};
const PROPS = [
{ id: 'P1657', name: 'MPA', country: '美国' },
{ id: 'P2756', name: 'EIRIN', country: '日本' },
{ id: 'P1144', name: 'EIRIN', country: '日本' },
{ id: 'P2684', name: 'Kijkw', country: '荷兰' },
{ id: 'P1142', name: 'Kijkw', country: '荷兰' },
{ id: 'P3216', name: 'ClassI', country: '巴西' },
{ id: 'P2334', name: 'ClassI', country: '巴西' },
{ id: 'P1981', name: 'IFCO', country: '爱尔兰' },
{ id: 'P7573', name: 'IFCO', country: '爱尔兰' },
{ id: 'P8874', name: 'HK', country: '中国香港' }
];
const ratingGroups = new Map();
PROPS.forEach(prop => {
const propertyClaims = claims[prop.id];
if (!Array.isArray(propertyClaims)) return;
const ratings = [];
propertyClaims.forEach(claim => {
const value = claim?.mainsnak?.datavalue?.value;
const key = typeof value === 'object' ? value?.id : value;
const rating = key ? RATING_DB[key] : null;
if (rating && !ratings.includes(rating)) {
ratings.push(rating);
}
});
if (!ratings.length) return;
const previous = ratingGroups.get(prop.name);
if (previous) {
ratings.forEach(rating => {
if (!previous.ratings.includes(rating)) {
previous.ratings.push(rating);
}
});
return;
}
ratingGroups.set(prop.name, {
name: prop.name,
country: prop.country,
ratings
});
});
if (myDramaListContentRating) {
ratingGroups.set('MyDL', {
name: 'MyDL',
country: '国际',
ratings: [myDramaListContentRating]
});
}
if (commonSenseMediaRating) {
ratingGroups.set('CSM', {
name: 'CSM',
country: '美国',
ratings: [commonSenseMediaRating]
});
}
// 即便 Wikidata 没有任何分级,只要 MyDL 或 CSM 有信息,也照样显示模块。
if (!ratingGroups.size) return;
const getRatingDescription = (rating) => {
const text = String(rating).trim();
/*
* MyDramaList 通常使用纯年龄码,例如:
* 13+、15+、18+
*
* 但 G 是例外,不代表年龄,而是 General Audience,
* 即全年龄观众均可观看。
*/
if (/^G$/i.test(text)) {
return '全年龄可观看';
}
// MyDL 返回纯年龄码,例如 13+。
if (/^\d+\+$/.test(text)) {
return `适合 ${text.replace('+', ' 岁以上')}`;
}
const match = text.match(/\(([^)]+)\)/);
const detail = match?.[1] || '';
if (detail === '全年龄') return '全年龄可观看';
if (detail === '建议陪同') {
return '建议家长陪同';
}
if (/^\d+\+$/.test(detail)) {
return `适合 ${detail.replace('+', ' 岁以上')}`;
}
return detail || '分级信息';
};
const getRatingCode = (rating) => {
return String(rating)
.replace(/\s*\([^)]+\)/, '')
.trim();
};
const htmlItems = [...ratingGroups.values()]
.map(({ name, country, ratings }) => {
const rating = ratings.join(' / ');
const code = ratings.map(getRatingCode).join(' / ');
const description = ratings
.map(getRatingDescription)
.filter(Boolean)
.join(' / ');
return `
${name}
${country}
${code}
${description}
`;
})
.join('');
const aside = document.querySelector('.aside');
if (!aside) return;
document.getElementById('db-movie-ratings-pro')?.remove();
const ratingDiv = document.createElement('div');
ratingDiv.className = 'indent';
ratingDiv.id = 'db-movie-ratings-pro';
ratingDiv.style.marginBottom = '30px';
ratingDiv.innerHTML = `
影视分级 · · · · · ·
`;
insertAsideModule(ratingDiv);
}
// --- 影视名称(基于 Box Office Mojo 页面高效解析,避免额外请求 IMDb) ---
function addMediaTitle() {
// 防止重复添加
if ($('#info').find('span.pl:contains("影视名称")').length) return;
var year = '';
try { year = $('span.year').text().match(/\d+/)[0]; } catch (e) {}
var ch_name = $('h1').find('span:first').text().split(' ')[0];
// 清洗规则
function formatName(str) {
return str.replace(/\s+/g, ' ')
.replace(/ /g, '.')
.replace(/:\./, '.')
.replace('-.', '-')
.replace('..', '.')
.replace('.-', '-')
.replace(/\.+$/, ''); // ★ 确保末尾没有 .
}
function appendName(en_name) {
var raw = en_name ? `${ch_name} ${en_name} ${year}` : `${ch_name} ${year}`;
var name = formatName(raw);
var html = `影视名称: ${name}
`;
$('#info').append(html);
}
// 兜底逻辑:无 IMDb 时从“又名”中提取英文名
function fallbackAkaName() {
var en_name = null;
try {
var akaNode = $('#info span.pl:contains("又名")')[0];
if (akaNode && akaNode.nextSibling) {
akaNode.nextSibling.textContent.trim().split('/').forEach(function (e) {
e = e.trim();
if (e && e.match(/^[a-zA-Z0-9 '-:]*$/)) en_name = e;
});
}
} catch (err) {}
appendName(en_name);
}
var imdbMatch = $('#info').html().match(/tt\d+/i);
if (!imdbMatch) { fallbackAkaName(); return; }
// 改为请求 Box Office Mojo 页面来获取影片英文标题
GM_xmlhttpRequest({
method: 'GET',
url: 'https://www.boxofficemojo.com/title/' + imdbMatch[0] + '/',
timeout: 15000,
headers: {
'Accept-Language': 'en-US,en;q=0.9',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
},
onload: function (res) {
try {
var doc = new DOMParser().parseFromString(res.responseText, 'text/html');
var pageTitle = doc.querySelector('title')?.textContent || '';
var en_name = pageTitle.split('-')[0].trim();
if (!en_name || en_name.toLowerCase().includes('box office mojo')) {
en_name = doc.querySelector('h1.a-size-extra-large, h1[class*="a-size-extra-large"]')?.textContent?.trim() || '';
}
// 剧集:拼接季号 Sxx
if ($('span.pl:contains("季数")').length) {
var number = $('#season option:selected').text().trim();
if (!number) {
var sn = $('span.pl:contains("季数")')[0];
if (sn && sn.nextSibling) number = sn.nextSibling.textContent.trim();
}
if (number) {
if (number.length < 2) number = '0' + number;
en_name = en_name + ' S' + number;
}
}
appendName(en_name || null);
} catch (err) {
console.warn('影视名称生成失败:', err);
fallbackAkaName();
}
},
onerror: fallbackAkaName,
ontimeout: fallbackAkaName
});
}
// ==================== 电影收支模块(基于 Box Office Mojo 高效获取 + 保留全套汇率折算) ====================
const DB_EXCHANGE_RATE_CACHE_KEY = 'db_movie_exchange_rates_v2';
const DB_EXCHANGE_RATE_CACHE_TTL = 24 * 60 * 60 * 1000;
const DB_EXCHANGE_RATE_API =
'https://api.frankfurter.dev/v1/latest?base=USD&symbols=JPY,CNY,EUR,GBP,HKD,INR,TWD,CHF,CAD';
async function getLiveExchangeRates() {
const fallbackRates = {
USD: 1,
JPY: 155,
CNY: 7.25,
EUR: 0.93,
GBP: 0.79,
HKD: 7.8,
INR: 83.5,
TWD: 31.8,
CHF: 0.88,
CAD: 1.37
};
try {
const cacheText = localStorage.getItem(
DB_EXCHANGE_RATE_CACHE_KEY
);
if (cacheText) {
const cache = JSON.parse(cacheText);
if (
cache?.timestamp &&
cache?.rates &&
Date.now() - cache.timestamp <
DB_EXCHANGE_RATE_CACHE_TTL
) {
const required = [
'USD',
'JPY',
'CNY',
'EUR',
'GBP',
'HKD',
'INR',
'TWD',
'CHF',
'CAD'
];
if (required.every(k => typeof cache.rates[k] === 'number')) {
return cache.rates;
}
}
}
} catch (error) {
console.warn('读取汇率缓存失败:', error);
}
try {
const response = await fetch(DB_EXCHANGE_RATE_API, {
method: 'GET',
cache: 'no-cache'
});
if (!response.ok) {
throw new Error(
`汇率接口请求失败:${response.status}`
);
}
const data = await response.json();
const rates = {
USD: 1,
JPY: Number(data.rates?.JPY) || fallbackRates.JPY,
CNY: Number(data.rates?.CNY) || fallbackRates.CNY,
EUR: Number(data.rates?.EUR) || fallbackRates.EUR,
GBP: Number(data.rates?.GBP) || fallbackRates.GBP,
HKD: Number(data.rates?.HKD) || fallbackRates.HKD,
INR: Number(data.rates?.INR) || fallbackRates.INR,
TWD: Number(data.rates?.TWD) || fallbackRates.TWD,
CHF: Number(data.rates?.CHF) || fallbackRates.CHF,
CAD: Number(data.rates?.CAD) || fallbackRates.CAD
};
try {
localStorage.setItem(
DB_EXCHANGE_RATE_CACHE_KEY,
JSON.stringify({
timestamp: Date.now(),
rates
})
);
} catch (error) {
console.warn('写入汇率缓存失败:', error);
}
return rates;
} catch (error) {
console.warn(
'联网获取汇率失败,使用内置近似汇率:',
error
);
return fallbackRates;
}
}
function isFilmEntity(claims) {
if (!claims || !Array.isArray(claims.P31)) return false;
const instanceIds = claims.P31
.map(item => item?.mainsnak?.datavalue?.value?.id)
.filter(Boolean);
const tvQids = [
'Q5398426', // television series
'Q1259759', // television program
'Q15416', // television series episode
'Q24856', // television season
'Q522618' // television miniseries
];
const filmQids = [
'Q11424', // film
'Q202866', // animated film
'Q20650540', // 日本动画电影
'Q24870', // short film
'Q282430', // documentary film
'Q226730', // silent film
'Q291688' // animated short film
];
return instanceIds.some(id => filmQids.includes(id)) &&
!instanceIds.some(id => tvQids.includes(id));
}
async function fetchMaoyanBoxOffice(movieUrl) {
if (!movieUrl) return null;
const match = String(movieUrl || '').match(/\/films\/(\d+)/);
if (!match) return null;
const movieId = match[1];
const awardUrl = `https://www.maoyan.com/ajax/films/${movieId}`;
const html = await gmMaoyanRequest(awardUrl);
if (!html) return null;
const doc = new DOMParser().parseFromString(html, 'text/html');
const items = [...doc.querySelectorAll('.film-mbox-item')];
let chinaOpeningRmbWan = 0;
let chinaTotalRmbWan = 0;
items.forEach(item => {
const names = [...item.querySelectorAll('.mbox-name')].map(el => el.textContent.trim());
if (names.length >= 2) {
const val = parseFloat(names[0]);
const label = names[1];
if (!isNaN(val) && val > 0) {
if (label.includes('首周票房')) {
chinaOpeningRmbWan = val;
} else if (label.includes('累计票房')) {
chinaTotalRmbWan = val;
}
}
}
});
return {
chinaOpeningRmb: chinaOpeningRmbWan > 0 ? chinaOpeningRmbWan * 10000 : null,
chinaTotalRmb: chinaTotalRmbWan > 0 ? chinaTotalRmbWan * 10000 : null
};
}
function parseMojoMoney(text) {
if (!text) return 0;
const cleaned = text.replace(/[^0-9.]/g, '');
const value = parseFloat(cleaned);
return Number.isFinite(value) && value > 0 ? value : 0;
}
function fetchMojoBoxOffice(targetId) {
return new Promise((resolve, reject) => {
const url = `https://www.boxofficemojo.com/title/${targetId}/`;
GM_xmlhttpRequest({
method: 'GET',
url: url,
headers: {
'Accept-Language': 'en-US,en;q=0.9',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
},
onload(response) {
if (response.status !== 200) {
reject(new Error(`Box Office Mojo 返回 HTTP ${response.status}`));
return;
}
const doc = new DOMParser().parseFromString(response.responseText, 'text/html');
let budget = 0;
let usCanada = 0;
let openingWeekend = 0;
let worldwide = 0;
let southKorea = 0;
let japan = 0;
let hongKong = 0;
let taiwan = 0;
let mojoChina = 0;
const summaryValues = doc.querySelector('.mojo-summary-values') || doc.querySelector('[class*="mojo-summary-values"]');
if (summaryValues && summaryValues.childNodes) {
for (let i = 0; i < summaryValues.childNodes.length; i++) {
const node = summaryValues.childNodes[i];
if (node.nodeType === Node.ELEMENT_NODE) {
const textContent = node.textContent || '';
const moneyEl = node.querySelector('.money');
if (moneyEl) {
if (textContent.includes('Budget')) {
budget = parseMojoMoney(moneyEl.textContent);
} else if (textContent.includes('Domestic Opening')) {
openingWeekend = parseMojoMoney(moneyEl.textContent);
}
}
}
}
}
const summaryTable = doc.querySelectorAll('.mojo-performance-summary-table div, [class*="mojo-performance-summary-table"] div');
for (let i = 0; i < summaryTable.length; i++) {
const item = summaryTable[i];
const header = item.querySelector('.a-size-small');
const value = item.querySelector('.money');
if (header && value) {
const headerText = header.textContent || '';
if (headerText.includes('Domestic')) {
usCanada = parseMojoMoney(value.textContent);
} else if (headerText.includes('Worldwide')) {
worldwide = parseMojoMoney(value.textContent);
}
}
}
// 抓取各国家/地区总票房表格行(含 South Korea, Japan, Hong Kong, Taiwan, China)
const tableRows = doc.querySelectorAll('table tr');
for (let i = 0; i < tableRows.length; i++) {
const row = tableRows[i];
const cells = row.querySelectorAll('td');
if (cells.length >= 3) {
const countryName = cells[0].textContent.trim();
// 总票房通常在行内的最后一个 .money 元素中
const moneyEls = row.querySelectorAll('.money');
if (moneyEls.length > 0) {
const lastMoneyEl = moneyEls[moneyEls.length - 1];
const moneyVal = parseMojoMoney(lastMoneyEl.textContent);
const nameLower = countryName.toLowerCase();
if (nameLower === 'south korea') {
southKorea = moneyVal;
} else if (nameLower === 'japan') {
japan = moneyVal;
} else if (nameLower === 'hong kong') {
hongKong = moneyVal;
} else if (nameLower === 'taiwan') {
taiwan = moneyVal;
} else if (nameLower === 'china') {
mojoChina = moneyVal;
}
}
}
}
if (!budget && !usCanada && !worldwide && !openingWeekend && !southKorea && !japan && !hongKong && !taiwan && !mojoChina) {
reject(new Error('未在 Box Office Mojo 页面提取到有效的收支数据'));
return;
}
resolve({
budget: budget > 0 ? budget : null,
usCanada: usCanada > 0 ? usCanada : null,
openingWeekend: openingWeekend > 0 ? openingWeekend : null,
worldwide: worldwide > 0 ? worldwide : null,
southKorea: southKorea > 0 ? southKorea : null,
japan: japan > 0 ? japan : null,
hongKong: hongKong > 0 ? hongKong : null,
taiwan: taiwan > 0 ? taiwan : null,
mojoChina: mojoChina > 0 ? mojoChina : null
});
},
onerror() {
reject(new Error('Box Office Mojo 请求网络错误'));
}
});
});
}
async function renderBoxOfficeModule(claims, imdbId) {
if (!claims) return;
if (!isFilmEntity(claims)) {
console.log('[豆瓣票房] 非电影条目,跳过电影收支模块');
return;
}
const aside = document.querySelector('.aside');
if (!aside) return;
document.getElementById('db-movie-boxoffice-enhanced')?.remove();
const liveRates = await getLiveExchangeRates();
let maoyanBoxOffice = null;
try {
const title = getDoubanMovieTitle();
if (title) {
const maoyanMovie = await getMaoyanMovie(title);
if (maoyanMovie?.url) {
maoyanBoxOffice = await fetchMaoyanBoxOffice(maoyanMovie.url);
}
}
} catch (e) {
console.warn('[豆瓣票房] 猫眼中国票房获取失败:', e);
}
const currencyToUSD = {
Q4917: 1, // USD
Q25224: 1 / liveRates.JPY, // JPY:yen(标准货币实体)
Q8146: 1 / liveRates.JPY, // JPY:当前 Wikidata 返回的 yen 单位
Q39099: 1 / liveRates.CNY, // CNY
Q4916: 1 / liveRates.EUR, // EUR
Q25344: 1 / liveRates.GBP, // GBP
Q31015: 1 / liveRates.HKD, // HKD
Q188: 1 / 1.95583 / liveRates.EUR, // DEM
Q208526: 1 / liveRates.TWD, // TWD
Q39: 1 / liveRates.CHF, // CHF
Q1104069: 1 / liveRates.CAD // CAD:Canadian dollar
};
function parseAmount(amount) {
const value = parseFloat(amount);
return Number.isFinite(value) ? Math.abs(value) : 0;
}
function getUnitQid(unitUrl) {
return unitUrl?.split('/').pop() || 'Q4917';
}
function isJapaneseFilm() {
const infoText =
document.getElementById('info')?.innerText || '';
return /制作国家\/地区[::]\s*日本|制片国家\/地区[::]\s*日本/.test(
infoText
);
}
function convertToUSD(amount, unitUrl) {
const number = parseAmount(amount);
const currencyQid = getUnitQid(unitUrl);
// 只有 Q39099 才是人民币。
// Q8146 虽然在此数据中表示 yen,但绝不能归为人民币。
const isRmbQid = currencyQid === 'Q39099';
const rate = currencyToUSD[currencyQid];
if (!rate) {
console.warn(
`[豆瓣票房] 未支持的货币单位:${currencyQid},跳过换算`
);
return {
amount: 0,
cnyAmount: 0,
isRMB: false,
unsupportedCurrency: true
};
}
const amountInUSD = number * rate;
if (isRmbQid) {
return {
amount: amountInUSD,
cnyAmount: number,
isRMB: true
};
}
return {
amount: amountInUSD,
cnyAmount: amountInUSD * liveRates.CNY,
isRMB: false
};
}
function formatCNYChinese(usd) {
if (!Number.isFinite(usd) || usd <= 0) {
return '—';
}
const cny = usd * liveRates.CNY;
if (cny >= 100000000) {
return `
${(cny / 100000000).toFixed(2)}
亿元
`;
}
if (cny >= 10000) {
return `
${(cny / 10000).toFixed(2)}
万元
`;
}
return `
${Math.round(cny)}
元
`;
}
function formatPureCNY(cny) {
if (!Number.isFinite(cny) || cny <= 0) {
return '—';
}
if (cny >= 100000000) {
return `
${(cny / 100000000).toFixed(2)}
亿元
`;
}
if (cny >= 10000) {
return `
${(cny / 10000).toFixed(2)}
万元
`;
}
return `
${Math.round(cny)}
元
`;
}
function readFinancialClaim(property) {
const claim = claims[property]?.find(item => {
return item?.mainsnak?.datavalue?.value?.amount !== undefined;
});
if (!claim) return null;
const value = claim.mainsnak.datavalue.value;
const originalAmount = parseAmount(value.amount);
if (!originalAmount) return null;
const converted = convertToUSD(
value.amount,
value.unit,
);
if (converted.isRMB) {
return {
cny: converted.cnyAmount,
usd: converted.amount,
isRMB: true
};
}
return {
usd: converted.amount,
cny: converted.cnyAmount,
isRMB: false
};
}
const cost = readFinancialClaim('P2130');
const boxOffice = readFinancialClaim('P2142');
let mojoBoxOffice = null;
if (imdbId) {
try {
mojoBoxOffice = await fetchMojoBoxOffice(imdbId);
} catch (error) {
console.warn('[豆瓣票房] Box Office Mojo 票房抓取失败:', error);
}
}
function isReasonableBudget(budget, worldwide) {
if (!budget || budget <= 0) return false;
if (!worldwide || worldwide <= 0) return budget < 500_000_000;
return budget <= worldwide * 10;
}
const finalCost = cost || (
isReasonableBudget(mojoBoxOffice?.budget, mojoBoxOffice?.worldwide)
? { usd: mojoBoxOffice.budget, isRMB: false, source: 'mojo' }
: null
);
const finalBoxOffice = boxOffice?.usd
? { ...boxOffice, source: 'wikidata' }
: (
mojoBoxOffice?.worldwide
? { usd: mojoBoxOffice.worldwide, source: 'mojo' }
: null
);
const financeItems = [];
function createFinanceCard({
label,
value,
icon,
type = '',
featured = false
}) {
return `
${label}
${icon}
${value}
`;
}
// 大陆首周票房(来自猫眼)
if (maoyanBoxOffice?.chinaOpeningRmb) {
financeItems.push(
createFinanceCard({
label: '大陆首周票房',
value: formatPureCNY(maoyanBoxOffice.chinaOpeningRmb),
icon: '¥',
type: 'is-revenue is-cny'
})
);
}
// 大陆总票房(来自猫眼)
if (maoyanBoxOffice?.chinaTotalRmb) {
financeItems.push(
createFinanceCard({
label: '大陆总票房',
value: formatPureCNY(maoyanBoxOffice.chinaTotalRmb),
icon: '¥',
type: 'is-revenue is-cny'
})
);
}
// 香港总票房(来自 Mojo)
if (mojoBoxOffice?.hongKong) {
financeItems.push(
createFinanceCard({
label: '香港总票房',
value: formatCNYChinese(mojoBoxOffice.hongKong),
icon: '¥',
type: 'is-revenue is-usd'
})
);
}
// 台湾总票房(来自 Mojo)
if (mojoBoxOffice?.taiwan) {
financeItems.push(
createFinanceCard({
label: '台湾总票房',
value: formatCNYChinese(mojoBoxOffice.taiwan),
icon: '¥',
type: 'is-revenue is-usd'
})
);
}
// 中国总票房(来自 Mojo)
if (mojoBoxOffice?.mojoChina) {
financeItems.push(
createFinanceCard({
label: '中国总票房',
value: formatCNYChinese(mojoBoxOffice.mojoChina),
icon: '¥',
type: 'is-revenue is-usd'
})
);
}
// 韩国总票房
if (mojoBoxOffice?.southKorea) {
financeItems.push(
createFinanceCard({
label: '韩国总票房',
value: formatCNYChinese(mojoBoxOffice.southKorea),
icon: '₩',
type: 'is-revenue is-usd'
})
);
}
// 日本总票房
if (mojoBoxOffice?.japan) {
financeItems.push(
createFinanceCard({
label: '日本总票房',
value: formatCNYChinese(mojoBoxOffice.japan),
icon: '¥',
type: 'is-revenue is-usd'
})
);
}
if (mojoBoxOffice?.openingWeekend) {
financeItems.push(
createFinanceCard({
label: '北美首周票房',
value: formatCNYChinese(mojoBoxOffice.openingWeekend),
icon: '$',
type: 'is-revenue is-usd'
})
);
}
if (mojoBoxOffice?.usCanada) {
financeItems.push(
createFinanceCard({
label: '北美总票房',
value: formatCNYChinese(mojoBoxOffice.usCanada),
icon: '$',
type: 'is-revenue is-usd'
})
);
}
if (finalCost) {
financeItems.push(
createFinanceCard({
label: '制作成本',
value: finalCost.isRMB ? formatPureCNY(finalCost.cny) : formatCNYChinese(finalCost.usd),
icon: '⚙',
type: 'is-cost'
})
);
}
if (finalBoxOffice) {
financeItems.push(
createFinanceCard({
label: '全球票房',
value: formatCNYChinese(finalBoxOffice.usd),
icon: '💰',
type: 'is-global',
featured: true
})
);
}
// 只要没有任何一项有效数据,直接终止渲染,绝不显示空模块
if (financeItems.length === 0) return;
let roiHtml = '';
const effectiveBoxOfficeUSD =
finalBoxOffice?.usd ||
(
maoyanBoxOffice?.chinaTotalRmb
? maoyanBoxOffice.chinaTotalRmb / liveRates.CNY
: null
);
const costUSD = finalCost ? (finalCost.isRMB ? finalCost.cny / liveRates.CNY : finalCost.usd) : 0;
if (finalCost && effectiveBoxOfficeUSD && costUSD > 0) {
const roi =
((effectiveBoxOfficeUSD - costUSD) / costUSD) *
100;
let roiColor = '#8a9490';
let roiLabel = '低于制作成本';
let roiClass = 'is-low';
if (roi >= 500) {
roiColor = '#d9362b';
roiLabel = '票房爆发';
roiClass = 'is-explosive';
} else if (roi >= 200) {
roiColor = '#b77909';
roiLabel = '高回报';
roiClass = 'is-high';
} else if (roi >= 100) {
roiColor = '#14834a';
roiLabel = '表现优异';
roiClass = 'is-excellent';
} else if (roi >= 0) {
roiColor = '#5c851a';
roiLabel = '正向回报';
roiClass = 'is-positive';
}
const roiSign = roi > 0 ? '+' : '';
roiHtml = `
投资回报率
${roiSign}${roi.toFixed(1)}%
${roiLabel}
`;
}
const boxDiv = document.createElement('div');
boxDiv.className = 'indent';
boxDiv.id = 'db-movie-boxoffice-enhanced';
boxDiv.style.marginBottom = '30px';
boxDiv.innerHTML = `
电影收支 · · · · · ·
${financeItems.join('')}
${roiHtml}
i
按当前汇率折算人民币,回报率仅供参考。
`;
insertAsideModule(boxDiv);
}
// 新增:汉字圈名字对比模块(含澳门、日本,过滤条件计入澳门、不计日本)
function renderKanjiZoneComparison(labels) {
if (!labels) return;
// 1. 定义大中华五地配置(计入过滤条件)
const gcRegions = [
{ name: "中国大陆", langCodes: ["zh-cn", "zh-hans"] },
{ name: "中国香港", langCodes: ["zh-hk", "zh-hant-hk"] },
{ name: "中国澳门", langCodes: ["zh-mo", "zh-hant-mo"] },
{ name: "中国台湾", langCodes: ["zh-tw", "zh-hant-tw"] },
{ name: "新加坡", langCodes: ["zh-sg", "zh-hans-sg"] }
];
// 2. 定义日本配置(不计入过滤条件)
const jpRegion = { name: "日本", langCodes: ["ja"] };
// 3. 提取大中华五地的有效译名
const gcResults = [];
gcRegions.forEach(region => {
let value = "";
for (const code of region.langCodes) {
if (labels[code] && labels[code].value) {
value = labels[code].value.trim();
break;
}
}
if (value) {
gcResults.push({
label: region.name,
value
});
}
});
// 大中华五地仅有一项或零项有值,则不显示模块
if (gcResults.length <= 1) return;
// 4. 提取日本译名;日本不参与模块显示过滤
const finalResults = [...gcResults];
let jpVal = "";
for (const code of jpRegion.langCodes) {
if (labels[code] && labels[code].value) {
jpVal = labels[code].value.trim();
break;
}
}
if (jpVal) {
finalResults.push({
label: jpRegion.name,
value: jpVal
});
}
// 5. 寻找豆瓣右侧边栏容器并渲染
const aside = document.querySelector(".aside");
if (!aside) return;
const comparisonDiv = document.createElement("div");
comparisonDiv.id = "db-kanji-comparison";
comparisonDiv.className = "indent";
const regionCodes = {
中国大陆: "CN",
中国香港: "HK",
中国澳门: "MO",
中国台湾: "TW",
新加坡: "SG",
日本: "JP"
};
const regionHints = {
中国大陆: "大陆译名",
中国香港: "香港译名",
中国澳门: "澳门译名",
中国台湾: "台湾译名",
新加坡: "新加坡译名",
日本: "日文片名"
};
const escapeHtml = value => String(value)
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
const htmlItems = finalResults.map(item => `
${escapeHtml(item.label)}
${regionCodes[item.label] || ""}
${escapeHtml(item.value)}
${regionHints[item.label] || "地区译名"}
`).join("");
comparisonDiv.innerHTML = `
汉字圈名字对比 · · · · · ·
`;
// 位置由统一排序函数决定(位于电影收支之下)
insertAsideModule(comparisonDiv);
}
// 2. 修正后的 Allocine 抓取逻辑 (将法式逗号替换为点号)
function fetchAllocineDualScores(acId) {
const url = "https://www.allocine.fr/film/fichefilm_gen_cfilm=" + acId + ".html";
GM_xmlhttpRequest({
method: "GET",
url: url,
onload: function(resp) {
if (resp.status !== 200) return;
const doc = new DOMParser().parseFromString(resp.responseText, "text/html");
let pressScore = "N/A", userScore = "N/A";
const ratingItems = doc.querySelectorAll('.rating-item');
ratingItems.forEach(item => {
const titleEl = item.querySelector('.rating-title');
const noteEl = item.querySelector('.stareval-note');
if (titleEl && noteEl) {
const titleText = titleEl.textContent.trim();
// 这里是核心改动:获取文本后立即将 ',' 替换为 '.'
const noteText = noteEl.textContent.trim().replace(',', '.');
if (noteText !== "--") {
if (titleText.includes("Presse")) {
pressScore = noteText;
} else if (titleText.includes("Spectateurs")) {
userScore = noteText;
}
}
}
});
if (pressScore !== "N/A" || userScore !== "N/A") {
renderAllocineDoubleCapsule(acId, pressScore, userScore);
}
}
});
}
// 3. Allocine 渲染 (按要求配色)
function renderAllocineDoubleCapsule(id, press, user) {
const html = '';
renderHeaderCapsule('header-alc-score', html);
}
function processLetterboxd(imdbId) {
const url = "https://letterboxd.com/imdb/" + imdbId;
GM_xmlhttpRequest({
method: "GET", url: url,
onload: function(res) {
const ratingMatch = res.responseText.match(/\"ratingValue\":\s*([0-9.]+)/);
if (ratingMatch) {
const score = parseFloat(ratingMatch[1]).toFixed(1);
const html = ``;
renderHeaderCapsule('header-lbx-score', html);
}
}
});
}
// --- 其他渲染逻辑 ---
function processOMDb(imdbId) {
GM_xmlhttpRequest({
method: "GET",
url: "https://www.omdbapi.com/?i=" + imdbId + "&apikey=thewdb", // 确认apikey正确
onload: function(res) {
if (res.status === 200) {
const data = JSON.parse(res.responseText);
if (data.Response === "True") {
// 1. 原有的 IMDb 渲染逻辑 (保持不变)
if (data.imdbRating !== 'N/A') {
renderScoreToBlock("block-imdb", "IMDb评分", data.imdbRating, data.imdbVotes, "https://www.imdb.com/title/" + imdbId + "/");
}
// 2. 插入新的 Metacritic 联动流程
// 注意:这里不再用 .then(),改用回调函数以获得最佳兼容性
getExtraIdsFromWikidata(imdbId, function(ids) {
fetchMetacriticDualScores(ids.mcId, data.Title);
});
}
}
}
});
}
function processTMDB(imdbId) {
GM_xmlhttpRequest({
method: "GET", url: `https://api.themoviedb.org/3/find/${imdbId}?external_source=imdb_id`,
headers: { "Authorization": tmdbAuth },
onload: function(res) {
try {
const data = JSON.parse(res.responseText);
const result = data.tv_results?.[0] || data.movie_results?.[0];
if (result) {
const type = data.tv_results?.length > 0 ? 'tv' : 'movie';
renderScoreToBlock("block-tmdb", "TMDB评分", result.vote_average.toFixed(1), result.vote_count.toLocaleString(), `https://www.themoviedb.org/${type}/${result.id}`);
}
} catch(e) {}
}
});
}
function processNeoDB(doubanId) {
if (!doubanId) return;
GM_xmlhttpRequest({
method: "GET",
url: "https://neodb.social/api/catalog/fetch?url=https://movie.douban.com/subject/" + doubanId + "/",
headers: { "Accept": "application/json" },
onload: function(res) {
if (res.status === 200) {
try {
const data = JSON.parse(res.responseText);
if (data && data.rating) {
// 渲染至我们刚刚在侧边栏创建的 block-neodb 容器中
renderScoreToBlock("block-neodb", "NeoDB评分", data.rating.toFixed(1), data.rating_count.toLocaleString(), "https://neodb.social" + data.url);
}
} catch(e) { console.error("NeoDB 解析失败"); }
}
}
});
}
function processAniList(imdbId) {
const sparql = `SELECT ?anilistID WHERE {?s wdt:P345 "${imdbId}"; wdt:P8729 ?anilistID.}`;
GM_xmlhttpRequest({
method: "GET", url: `https://query.wikidata.org/sparql?format=json&query=${encodeURIComponent(sparql)}`,
onload: function(res) {
try {
const anilistId = JSON.parse(res.responseText).results.bindings[0]?.anilistID?.value;
if (!anilistId) return;
const query = `query ($id: Int) { Media (id: $id, type: ANIME) { id meanScore siteUrl } }`;
GM_xmlhttpRequest({
method: "POST", url: "https://graphql.anilist.co",
headers: { "Content-Type": "application/json" },
data: JSON.stringify({ query, variables: { id: parseInt(anilistId) } }),
onload: function(response) {
const result = JSON.parse(response.responseText);
const rating = result.data.Media.meanScore; // 原始百分比分数 (如 85)
const siteUrl = result.data.Media.siteUrl;
if (rating) {
// 保持百分比格式,拼接 %
const formattedScore = `${rating}%`;
// 直接使用脚本统一的 drdm-capsule yellow-cap 样式
const html = ``;
renderHeaderCapsule('header-anilist-score', html);
}
}
});
} catch(e) {}
}
});
}
// ==================== 新增:动画评分获取与渲染模块 ====================
// 辅助延迟函数
const randomSleep = (max = 400, min = 200) => new Promise(resolve => setTimeout(resolve, Math.floor(Math.random() * (max - min + 1)) + min));
// 1. 获取 Bangumi 评分
function processBangumi(title) {
const searchUrl = `https://bgm.tv/subject_search/${encodeURIComponent(title)}?cat=2`;
// 获取豆瓣本页面的年份
const yearEl = document.querySelector(".year");
const dbYear = yearEl ? yearEl.textContent.replace(/\(|\)/g, "").trim() : null;
GM_xmlhttpRequest({
method: "GET",
url: searchUrl,
onload: function(res) {
try {
const parser = new DOMParser();
const doc = parser.parseFromString(res.responseText, "text/html");
const items = doc.querySelectorAll("#browserItemList li");
let matchedResult = null;
// 如果豆瓣页面有年份,优先匹配年份一致的项目
if (dbYear && items.length > 0) {
for (let i = 0; i < items.length; i++) {
const infoEl = items[i].querySelector(".info");
if (infoEl && infoEl.textContent.includes(dbYear)) {
matchedResult = items[i];
break;
}
}
}
// 如果按年份没有匹配到,默认取第一项
if (!matchedResult && items.length > 0) {
matchedResult = items[0];
}
if (matchedResult) {
const linkEl = matchedResult.querySelector("a.subjectCover");
const scoreEl = matchedResult.querySelector(".fade");
const countEl = matchedResult.querySelector(".tip_j");
if (linkEl && scoreEl) {
const bgmUrl = "https://bgm.tv" + linkEl.getAttribute("href");
const score = parseFloat(scoreEl.innerText).toFixed(1);
const count = countEl ? countEl.innerText.replace(/[^0-9]/g, "") : "0";
// 渲染到侧边栏,样式与 TMDB 完全保持一致
renderScoreToBlock("block-bangumi", "Bangumi评分", score, parseInt(count).toLocaleString(), bgmUrl);
}
}
} catch (e) {
console.error("解析 Bangumi 评分失败", e);
}
}
});
}
// 2. 获取 MyAnimeList (MAL) 评分
function processMAL(title) {
const searchUrl = `https://myanimelist.net/search/prefix.json?type=anime&keyword=${encodeURIComponent(title)}&v=1`;
// 获取豆瓣本页面的年份
const yearEl = document.querySelector(".year");
const dbYear = yearEl ? yearEl.textContent.replace(/\(|\)/g, "").trim() : null;
GM_xmlhttpRequest({
method: "GET",
url: searchUrl,
onload: function(res) {
try {
const info = JSON.parse(res.responseText);
const items = info.categories?.[0]?.items;
if (items && items.length > 0) {
let matchedItem = null;
// 如果豆瓣页面有年份,优先匹配年份一致的项目
if (dbYear) {
for (let i = 0; i < items.length; i++) {
const item = items[i];
// MAL 返回的 aired 格式通常为 "Oct 2015" 或 "2015"
const airedStr = item.payload?.aired || "";
if (airedStr.includes(dbYear)) {
matchedItem = item;
break;
}
}
}
// 如果按年份没匹配到,则默认取第一项
if (!matchedItem) {
matchedItem = items[0];
}
if (matchedItem && matchedItem.url) {
const pageUrl = matchedItem.url;
// 延迟请求避免高频被ban
randomSleep(300, 100).then(() => {
GM_xmlhttpRequest({
method: "GET",
url: pageUrl,
onload: function(pageRes) {
try {
const parser = new DOMParser();
const doc = parser.parseFromString(pageRes.responseText, "text/html");
const scoreEl = doc.querySelector(".fl-l.score");
if (scoreEl) {
const scoreText = scoreEl.textContent.trim();
// 排除未开播无评分的情况 (N/A)
if (scoreText === "N/A" || !scoreText) return;
const score = parseFloat(scoreText).toFixed(1);
const count = scoreEl.dataset.user ? scoreEl.dataset.user.replace(/users|,/g, "").trim() : "0";
renderScoreToBlock("block-mal", "MAL评分", score, parseInt(count).toLocaleString(), pageUrl);
}
} catch (err) {
console.error("解析 MAL 页面失败", err);
}
}
});
});
}
}
} catch (e) {
console.error("获取 MAL 数据失败", e);
}
}
});
}
function renderScoreToBlock(blockId, label, score, votes, href) {
const container = document.getElementById(blockId);
if (!container || !score || score === "0.0" || score === "N/A") return;
const starNum = Math.round(parseFloat(score)) * 5;
container.innerHTML = ``;
}
initRankings();
initMain();
injectTrendChart();
setTimeout(function() {
injectSteamUI();
}, 200);
(function addViewOriginalLogic() {
const mainPic = document.querySelector('#mainpic');
if (!mainPic) return;
const img = mainPic.querySelector('img');
if (img) {
const rawUrl = img.src.replace(/photo\/[sl](_ratio_poster|pic)\/public\/(p\d+).+$/, "photo/l/public/$2.jpg");
const btn = document.createElement('a');
btn.className = 'view-original-btn';
btn.href = rawUrl;
btn.target = '_blank';
btn.innerText = '查看原图';
mainPic.appendChild(btn);
}
})();
// =====================================================================
// --- 复制简介模块(完整版格式增强)---
// ① ◎译 名 / ◎片 名(拆分中英文标题)
// ② 完整演职员表:导演 / 演员 / 编剧 / 音乐 / 摄影 / 美术 / 服装 / 动作特技 …
// ③ 人名中文与外文名以 " / " 分隔,演员附角色名(仅保留"配/饰"角色,去掉"配音 Voice"前缀)
// ④ ◎获奖情况(抓取豆瓣奖项页,位于简介之后)
// =====================================================================
// --- 1. 复制简介按钮 ---
function addCopyIntroButton() {
const mainpic = document.querySelector('#mainpic');
if (!mainpic) return;
const allLinks = mainpic.getElementsByTagName('a');
let originalBtn = null;
for (let a of allLinks) {
if (a.innerText.trim() === '查看原图') {
originalBtn = a;
break;
}
}
if (!originalBtn || document.getElementById('copy-intro-btn')) return;
originalBtn.style.cssText = `
display: inline-block !important;
width: 48% !important;
float: left !important;
text-align: center !important;
padding: 6px 0 !important;
margin: 10px 0 0 0 !important;
box-sizing: border-box !important;
font-size: 13px !important;
height: auto !important;
line-height: normal !important;
`;
const copyBtn = document.createElement('a');
copyBtn.id = 'copy-intro-btn';
copyBtn.href = 'javascript:void(0);';
copyBtn.innerText = '复制简介';
copyBtn.className = originalBtn.className;
copyBtn.style.cssText = `
display: inline-block !important;
width: 48% !important;
float: right !important;
text-align: center !important;
padding: 6px 0 !important;
margin: 10px 0 0 0 !important;
box-sizing: border-box !important;
font-size: 13px !important;
cursor: pointer !important;
height: auto !important;
line-height: normal !important;
`;
copyBtn.onclick = async function (e) {
e.preventDefault();
if (copyBtn.dataset.busy === '1') return;
copyBtn.dataset.busy = '1';
copyBtn.innerText = '获取中…';
try {
const fullContent = await generateMovieInfo();
await _afCopyText(fullContent);
copyBtn.innerText = '已复制 ✔';
} catch (err) {
console.warn('复制简介失败:', err);
copyBtn.innerText = '复制失败 ✘';
}
setTimeout(() => { copyBtn.innerText = '复制简介'; copyBtn.dataset.busy = '0'; }, 1500);
};
originalBtn.after(copyBtn);
const clearer = document.createElement('div');
clearer.style.cssText = "clear: both !important; width: 100% !important; height: 1px !important;";
copyBtn.after(clearer);
}
// --- 2. 工具函数 ---
function _afCopyText(text) {
if (navigator.clipboard && window.isSecureContext) {
return navigator.clipboard.writeText(text);
}
return new Promise(function (resolve, reject) {
try {
if (typeof GM_setClipboard === 'function') { GM_setClipboard(text); resolve(); return; }
const ta = document.createElement('textarea');
ta.value = text;
ta.style.cssText = 'position:fixed;top:-9999px;opacity:0;';
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
ta.remove();
resolve();
} catch (e) { reject(e); }
});
}
function _afAddComma(num) {
const n = parseInt(String(num).replace(/[^\d]/g, ''), 10);
return isNaN(n) ? String(num) : n.toLocaleString('en-US');
}
function _afSplitTitle(fullTitle) {
fullTitle = (fullTitle || '').trim().replace(/\s+/g, ' ');
if (!fullTitle) return { chs: '', original: '' };
const m = fullTitle.match(/^(.+[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff][0-9]*)\s+(\S.*)$/);
if (m) return { chs: m[1].trim(), original: m[2].trim() };
if (/[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff]/.test(fullTitle)) return { chs: fullTitle, original: fullTitle };
return { chs: '', original: fullTitle };
}
function _afSplitName(fullName) {
fullName = (fullName || '').trim().replace(/\s+/g, ' ');
if (!fullName) return { chs: '', foreign: '' };
const idx = fullName.indexOf(' ');
if (idx > 0 && /[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff]/.test(fullName.slice(0, idx))) {
return { chs: fullName.slice(0, idx).trim(), foreign: fullName.slice(idx + 1).trim() };
}
if (/[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff]/.test(fullName)) return { chs: fullName, foreign: '' };
return { chs: '', foreign: fullName };
}
// --- 3. 抓取并解析豆瓣"演职员"整页(同源 fetch) ---
const _afCreditsCache = {};
function _afFetchCredits(subjectId) {
if (_afCreditsCache[subjectId]) return _afCreditsCache[subjectId];
_afCreditsCache[subjectId] = fetch('https://movie.douban.com/subject/' + subjectId + '/celebrities', { credentials: 'same-origin' })
.then(function (r) {
if (!r.ok) throw new Error('HTTP ' + r.status);
return r.text();
})
.then(function (html) {
return _afParseCreditsDoc(new DOMParser().parseFromString(html, 'text/html'));
})
.catch(function (err) {
console.warn('演职员页面获取失败,将退回精简模式:', err);
return [];
});
return _afCreditsCache[subjectId];
}
function _afParseCreditsDoc(doc) {
const root = doc.querySelector('#celebrities') || doc;
let wrappers = Array.from(root.querySelectorAll('.list-wrapper'));
if (!wrappers.length) {
wrappers = Array.from(root.querySelectorAll('h2')).filter(function (h) {
return h.nextElementSibling && h.nextElementSibling.tagName === 'UL';
});
}
const sections = [];
for (let w of wrappers) {
const h2 = w.tagName === 'H2' ? w : w.querySelector('h2');
if (!h2) continue;
const ul = (w.tagName === 'H2' ? w.nextElementSibling : null) || w.querySelector('ul.celebrities-list') || w.querySelector('ul');
if (!ul) continue;
const h2c = h2.cloneNode(true);
h2c.querySelectorAll('.pl, a').forEach(function (n) { n.remove(); });
let position = h2c.textContent.replace(/\s+/g, '');
position = position.replace(/[A-Za-z]+/g, '');
if (!position) continue;
if (position === '主演') position = '演员';
if (/影人|合作|作品/.test(position)) continue;
let lis = ul.querySelectorAll('li.celebrity');
if (!lis.length) lis = ul.querySelectorAll('li');
const people = Array.from(lis).map(function (li) {
const nameEl = li.querySelector('.name') || li.querySelector('a');
const roleEl = li.querySelector('.role');
const fullName = (nameEl ? nameEl.textContent : '').trim().replace(/\s+/g, ' ');
const role = ((roleEl && (roleEl.getAttribute('title') || roleEl.textContent)) || '').trim();
const parts = _afSplitName(fullName);
// 提取角色:优先取括号内的 (配 xxx) / (饰 xxx),否则取独立的 饰/配 xxx;纯"配音"则留空
let character = '';
if (role) {
const bracketMatch = role.match(/[((]\s*(配|饰)\s+(.+?)\s*[))]/);
if (bracketMatch) {
character = bracketMatch[1] + ' ' + bracketMatch[2];
} else if (/^\s*(饰|配)\s+/.test(role)) {
character = role.replace(/^\s+/, '');
}
}
return { chs: parts.chs, foreign: parts.foreign, character: character };
}).filter(function (p) { return p.chs || p.foreign; });
if (people.length) sections.push({ position: position, people: people });
}
sections.sort(function (a, b) {
const rank = function (p) { return p === '导演' ? 0 : (p === '演员' ? 1 : 2); };
return rank(a.position) - rank(b.position);
});
return sections;
}
// --- 4. 渲染演职员(职位与人名平齐,无换行) ---
function _afFormatCelebrities(sections) {
return sections.map(function (sec) {
const position = sec.position;
let title = '◎';
switch (position.length) {
case 1:
title += ' ' + position + ' ';
break;
case 2:
title += position.split('').join(' ') + ' ';
break;
case 3:
title += position.split('').join(' ') + ' ';
break;
case 4:
title += position + ' ';
break;
default:
title += position + ' ';
}
const people = sec.people.map(function (f, i) {
const name = f.chs ? (f.foreign ? f.chs + ' / ' + f.foreign : f.chs) : f.foreign;
return (i > 0 ? ' ' : '') + name + (f.character ? ' (' + f.character + ')' : '');
}).join('\n');
return title + people;
}).join('\n');
}
// --- 4b. 抓取并解析豆瓣"获奖情况"整页 ---
const _afAwardsCache = {};
function _afFetchAwards(subjectId) {
if (!subjectId) return Promise.resolve([]);
if (_afAwardsCache[subjectId] !== undefined) return _afAwardsCache[subjectId];
_afAwardsCache[subjectId] = fetch('https://movie.douban.com/subject/' + subjectId + '/awards/', { credentials: 'same-origin' })
.then(function (r) {
if (!r.ok) throw new Error('HTTP ' + r.status);
return r.text();
})
.then(function (html) {
const doc = new DOMParser().parseFromString(html, 'text/html');
const awardDivs = doc.querySelectorAll('div.awards');
const results = [];
awardDivs.forEach(function (div) {
const h2 = div.querySelector('.hd > h2');
if (!h2) return;
const nameLink = h2.querySelector('a');
const yearSpan = h2.querySelector('.year');
const name = nameLink ? nameLink.textContent.trim() : h2.textContent.trim();
let year = '';
if (yearSpan) {
const ym = yearSpan.textContent.match(/\d+/);
if (ym) year = ym[0];
}
const awardEls = div.querySelectorAll('.award');
const items = [];
awardEls.forEach(function (aw) {
const lis = aw.querySelectorAll('li');
if (!lis.length) return;
const itemName = lis[0].textContent.trim();
let people = [];
if (lis[1]) {
people = lis[1].textContent.split('/').map(function (s) { return s.trim(); }).filter(Boolean);
}
items.push({ name: itemName, people: people });
});
if (name && items.length) {
results.push({ name: name, year: year, awards: items });
}
});
return results;
})
.catch(function (err) {
console.warn('获奖情况获取失败:', err);
return [];
});
return _afAwardsCache[subjectId];
}
function _afFormatAwards(awards) {
if (!awards || !awards.length) return '';
return '◎获奖情况\n\n' + awards.map(function (e) {
const awardName = ' ' + e.name + (e.year ? ' (' + e.year + ')' : '') + '\n';
const awardItems = e.awards.map(function (item) {
return ' ' + item.name + (item.people && item.people.length ? ' ' + item.people.join(' / ') : '');
}).join('\n');
return awardName + awardItems;
}).join('\n\n') + '\n\n';
}
// --- 5. IMDb 评分获取 ---
const _afImdbCache = {};
function _afGetImdbScore(imdbId) {
if (!imdbId) return Promise.resolve(null);
if (_afImdbCache[imdbId] !== undefined) return _afImdbCache[imdbId];
_afImdbCache[imdbId] = (async function () {
try {
const fromDom = _afScrapeImdbFromDom(imdbId);
if (fromDom && fromDom.rating) return fromDom;
} catch (e) {}
try {
const fromGql = await _afFetchImdbGraphQL(imdbId);
if (fromGql && fromGql.rating) return fromGql;
} catch (e) {}
try {
const fromLd = await _afFetchImdbLdJson(imdbId);
if (fromLd && fromLd.rating) return fromLd;
} catch (e) {}
return null;
})();
return _afImdbCache[imdbId];
}
function _afScrapeImdbFromDom(imdbId) {
const link = document.querySelector('[id^="block-"] a[href*="imdb.com/title/' + imdbId + '"]');
if (!link) return null;
const box = link.closest('[id^="block-"]');
if (!box) return null;
const text = (box.textContent || '').replace(/\s+/g, ' ');
const m = text.match(/(\d{1,2}\.\d)/);
if (!m) return null;
const rating = parseFloat(m[1]);
if (!(rating > 0 && rating <= 10)) return null;
const vm = text.match(/([\d,]{3,})\s*(?:人|users|votes)/i);
return { rating: rating, votes: vm ? vm[1].replace(/[^\d]/g, '') : '' };
}
function _afFetchImdbGraphQL(imdbId) {
return new Promise(function (resolve) {
try {
GM_xmlhttpRequest({
method: 'POST',
url: 'https://api.graphql.imdb.com/',
headers: { 'Content-Type': 'application/json' },
data: JSON.stringify({
query: 'query { title(id: "' + imdbId + '") { ratingsSummary { aggregateRating voteCount } } }'
}),
timeout: 8000,
onload: function (res) {
try {
const s = JSON.parse(res.responseText).data.title.ratingsSummary;
if (s && s.aggregateRating) resolve({ rating: s.aggregateRating, votes: s.voteCount || '' });
else resolve(null);
} catch (e) { resolve(null); }
},
onerror: function () { resolve(null); },
ontimeout: function () { resolve(null); }
});
} catch (e) { resolve(null); }
});
}
function _afFetchImdbLdJson(imdbId) {
return new Promise(function (resolve) {
try {
GM_xmlhttpRequest({
method: 'GET',
url: 'https://www.imdb.com/title/' + imdbId + '/',
headers: { 'User-Agent': navigator.userAgent, 'Accept-Language': 'en-US,en;q=0.9' },
timeout: 8000,
onload: function (res) {
try {
const m = res.responseText.match(/"aggregateRating"\s*:\s*\{[^}]*?"ratingCount"\s*:\s*(\d+)[^}]*?"ratingValue"\s*:\s*([\d.]+)/) ||
res.responseText.match(/"aggregateRating"\s*:\s*\{[^}]*?"ratingValue"\s*:\s*([\d.]+)[^}]*?"ratingCount"\s*:\s*(\d+)/);
if (!m) return resolve(null);
if (String(m[1]).indexOf('.') > -1 && parseFloat(m[1]) <= 10) resolve({ rating: parseFloat(m[1]), votes: m[2] });
else resolve({ rating: parseFloat(m[2]), votes: m[1] });
} catch (e) { resolve(null); }
},
onerror: function () { resolve(null); },
ontimeout: function () { resolve(null); }
});
} catch (e) { resolve(null); }
});
}
// --- 5b. TMDB 评分获取(从 TMDB 影片页 LD+JSON 读取)---
const _afTmdbCache = {};
function _afGetTmdbScore(tmdbUrl) {
if (!tmdbUrl) return Promise.resolve(null);
if (_afTmdbCache[tmdbUrl] !== undefined) return _afTmdbCache[tmdbUrl];
_afTmdbCache[tmdbUrl] = (async function () {
try {
const fromLd = await _afFetchTmdbLdJson(tmdbUrl);
if (fromLd && fromLd.rating) return fromLd;
} catch (e) {}
return null;
})();
return _afTmdbCache[tmdbUrl];
}
function _afFetchTmdbLdJson(tmdbUrl) {
return new Promise(function (resolve) {
try {
GM_xmlhttpRequest({
method: 'GET',
url: tmdbUrl,
headers: { 'User-Agent': navigator.userAgent, 'Accept-Language': 'en-US,en;q=0.9' },
timeout: 8000,
onload: function (res) {
try {
const ldMatch = res.responseText.match(/