您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
匹配章节简介和参与列表,显示章节制作人员
// ==UserScript== // @name BGM动画条目显示各章节参与制作人员信息 // @namespace bgmanime // @version 0.3.12 // @description 匹配章节简介和参与列表,显示章节制作人员 // @author heybye, hyary // @include /^https?://(bangumi|bgm|chii).(tv|in)/ // @run-at document-idle // @license MIT // ==/UserScript== const svgExpand = ` <svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M15 3h6v6M14 10l6.1-6.1M9 21H3v-6M10 14l-6.1 6.1"/> </svg> `; const svgCollapse = ` <svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M4 14h6v6M3 21l6.1-6.1M20 10h-6V4M21 3l-6.1 6.1"/> </svg> `; (function () { // 表单提交后刷新缓存 $("form").each((_, form) => { form.addEventListener("submit", () => { Object.keys(sessionStorage) .filter(key => key.startsWith('epinfo-')) .forEach(key => sessionStorage.removeItem(key)); }); }); const url = document.URL; const sitePattern = /^https?:\/\/(bangumi|bgm|chii)\.(tv|in)\//; const site = url.match(sitePattern)?.[0]; let epUl, epEl, mode; if (!site) return; // 如果不匹配站点,直接返回 switch (true) { case /^https?:\/\/(bangumi|bgm|chii)\.(tv|in)\/(subject\/\d+)?$/.test(url): epUl = $("ul.prg_list").get(); epEl = $("ul.prg_list > li a").get(); mode = 0; if (epEl.length === 0) return; // 如果没有单集列表,直接返回 break; case /^https?:\/\/(bangumi|bgm|chii)\.(tv|in)\/subject\/\d+\/ep$/.test(url): epEl = $("ul.line_list > li").get(); mode = 1; break; case /^https?:\/\/(bangumi|bgm|chii)\.(tv|in)\/ep\/\d+$/.test(url): mode = 2; break; default: return; } $("head").append(`<style> .epinfo { font-size: 12px; } .epinfo-hidden { display: none; } .subject_ep_section { position: relative; } .epinfo-toggle { position: absolute; right: 0.5rem; top: 0.5rem; width: 1.2rem; height: 1.2rem; color: #aaa; cursor: pointer; text-align: center; } .ps{ padding-left: 1.1rem; } ul.line_list li.cat { padding-left: 5px; } a.toggle-pointer{ cursor:pointer; } .ep-personinfo{ margin-top: 5px; margin-bottom: 5px; } </style>`); //正则表达式匹配单集desc const regexes_per = { "脚本": /(?<=[\u3040-\u9fa5]*?(脚本|シナリオ|剧本|编剧|プロット|大纲)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|・|、|=|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, "分镜": /(?<=[\u3040-\u9fa5]*?(分镜|コンテ)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|・|、|=|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, "演出": /(?<=[\u3040-\u9fa5]*?(演出)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|、|=|&|\u0026|、|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, "构图": /(?<=[\u3040-\u9fa5]*?(レイアウト|构图|layout|レイアウター)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|、|=|&|\u0026|、|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, "作画监督": /(?<=[\u3040-\u9fa5]*?(?<!総|总|アクション|メカ|ニック|エフェクト|动作|机械|特效)(作監|作画監督|作监|作画监督|作艦)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|=|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, "总作画监督": /(?<=(総|总)(作監|作画監督|作监|作画监督|作艦)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|、|=|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, "动作作画监督": /(?<=(アクション|动作)(作監|作画監督|設計|设计|ディレクター|作监|作画监督|作艦)\s*?(?:\uff1a|\u003A|】|\/|/|·|・|、|・|=|、|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, "机械作画监督": /(?<=(メカ|メカニック|机械)(作監|作画監督|作监|作画监督|作艦)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|=|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, "特效作画监督": /(?<=(エフェクト|特效|特技)(作監|作画監督|作监|作画监督|作艦)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|・|=|、|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, "原画": /(?<=(原画|作画)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|・|、|=|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, "作画监督助理": /(?<=[\u3040-\u9fa5]*?(?<!総|总)(作監|作画監督|作监|作画监督|作艦)(補佐|补佐|协力|協力|辅佐|辅助|助理)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|=|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, "演出助理": /(?<=(演出|(?<!作画)監督)(補佐|补佐|协力|協力|辅佐|辅助|助理|助手)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|、|=|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, "剪辑": /(?<=(剪辑|編集)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|、|=|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, "CG 导演":/(?<=(3DCGディレクター|CGディレクター|3DCG导演|CG导演)\s*?(?:\uff1a|\u003A|】|\/|/|·|・|、|=|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, "美术监督":/(?<=(美術|美术|美術監督|美术监督)\s*?(?:\uff1a|\u003A|】|\/|/|·|・|、|・|=|、|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, "背景美术":/(?<=(背景)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|・|、|=|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, "制作进行":/(?<=(制作进行|制作進行)\s*?(?:\uff1a|\u003A|】|\/|/|·|・|、|・|=|、|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, "设定制作": /(?<=(设定制作|設定制作)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|、|=|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, "制作管理":/(?<=(制作デスク|制作管理|制作主任)\s*?(?:\uff1a|\u003A|】|\/|/|=|·|・|、|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, "制作协力": /(?<=[\u3040-\u9fa5]*?(制作協力|制作协力|協力プロダクション)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|=|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, //以下为bangumi没有的职位 "总作画监督助理": /(?<=(総|总)(作監|作画監督|作监|作画监督|作艦)(補佐|补佐|协力|協力|辅佐|辅助|助理)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|=|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, "色彩演出": /(?<=(カラースクリプト)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|、|=|&|\u0026|、|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g,//汉字来自日本制作厨森甲斐的职种确认表。 "氛围稿": /(?<=(イメージボード)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|、|=|&|\u0026|、|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:)))(\W|\w)+?(?=\n|$)/g, }; const regexes_role_per = { "脚本": /[\u3040-\u9fa5]*?(脚本|シナリオ|剧本|编剧|プロット|大纲)\s*?(?:\uff1a|\u003A|】|\/|/|=|·|・|、|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "分镜": /[\u3040-\u9fa5]*?(分镜|コンテ)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|・|=|、|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "演出": /[\u3040-\u9fa5]*?(演出)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|・|、|=|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "构图": /[\u3040-\u9fa5]*?(レイアウト|构图|layout|レイアウター)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|・|、|=|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "作画监督": /[\u3040-\u9fa5]*?(?<!総|总|アクション|メカ|ニック|エフェクト|动作|机械|特效)(作監|作画監督|作监|作画监督|作艦)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|=|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "总作画监督": /(総|总)(作監|作画監督|作监|作画监督|作艦)\s*?(?:\uff1a|\u003A|】|\/|/|·|=|、|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "动作作画监督": /(アクション|动作)(作監|作画監督|設計|设计|ディレクター|作监|作画监督|作艦)\s*?(?:\uff1a|\u003A|】|\/|/|·|・|=|、|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "机械作画监督": /(メカ|メカニック|机械)(作監|作画監督|作监|作画监督|作艦)\s*?(?:\uff1a|\u003A|】|\/|/|·|・|、|=|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "特效作画监督": /(エフェクト|特效|特技)(作監|作画監督|作监|作画监督|作艦)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|=|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "原画": /(原画|作画)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|・|、|=|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "作画监督助理": /[\u3040-\u9fa5]*?(?<!総|总)(作監|作画監督|作监|作画监督|作艦)(補佐|补佐|協力|协力|辅佐|辅助|助理)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|=|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "演出助理": /(演出|(?<!作画)監督)(補佐|补佐|協力|协力|辅佐|辅助|助理|助手)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|、|=|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "剪辑": /(剪辑|編集)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|・|=|、|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "CG 导演":/(3DCGディレクター|CGディレクター|3DCG导演|CG导演)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "美术监督":/(美術|美术|美術監督|美术监督)\s*?(?:\uff1a|\u003A|】|\/|/|·|=|、|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "背景美术":/(背景)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|=|・|、|・|&|\u0026|•|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "制作进行":/(制作进行|制作進行)\s*?(?:\uff1a|\u003A|】|\/|/|·|=|、|・|・|、|&|\u0026|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "制作管理":/(制作デスク|制作管理|制作主任)\s*?(?:\uff1a|\u003A|】|\/|/|=|·|・|、|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "设定制作": /(设定制作|設定制作)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|、|=|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "制作协力": /[\u3040-\u9fa5]*?(制作協力|制作协力|協力プロダクション)\s*?(?:\uff1a|\u003A|】|\/|/|·|・|、|・|=|、|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, //以下为bangumi没有的职位 "总作画监督助理": /(総|总)(作監|作画監督|作监|作画监督|作艦)(補佐|补佐|协力|協力|辅佐|辅助|助理)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|=|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "色彩演出": /(カラースクリプト)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|、|=|&|\u0026|、|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, "氛围稿": /(イメージボード)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|、|=|&|\u0026|、|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))(\W|\w)+?(?=\n|$)/g, }; const regexes_role = { "脚本": /[\u3040-\u9fa5]*?(脚本|シナリオ|剧本|编剧|プロット|大纲)\s*?(?:\uff1a|\u003A|】|\/|/|=|·|・|、|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, "分镜": /[\u3040-\u9fa5]*?(分镜|コンテ)\s*?(?:\uff1a|\u003A|】|\/|/|·|・|、|・|、|=|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, "演出": /[\u3040-\u9fa5]*?(演出)\s*?(?:\uff1a|\u003A|】|\/|/|·|・|、|・|、|=|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, "构图": /[\u3040-\u9fa5]*?(レイアウト|构图|layout|レイアウター)\s*?(?:\uff1a|\u003A|】|\/|/|·|・|、|・|、|=|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, "作画监督": /[\u3040-\u9fa5]*?(?<!総|总|アクション|メカ|ニック|エフェクト|动作|机械|特效)(作監|作画監督|作监|作画监督|作艦)\s*?(?:\uff1a|\u003A|】|\/|/|=|·|・|、|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, "总作画监督": /(総|总)(作監|作画監督|作监|作画监督|作艦)\s*?(?:\uff1a|\u003A|】|\/|/|=|·|・|、|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, "动作作画监督": /(アクション|动作)(作監|作画監督|設計|设计|ディレクター|作监|作画监督|作艦)\s*?(?:\uff1a|\u003A|】|\/|/|·|=|、|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, "机械作画监督": /(メカ|メカニック|机械)(作監|作画監督|作监|作画监督|作艦)\s*?(?:\uff1a|\u003A|】|\/|/|=|·|・|、|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, "特效作画监督": /(エフェクト|特效|特技)(作監|作画監督|作监|作画监督|作艦)\s*?(?:\uff1a|\u003A|】|\/|/|·|・|、|・|=|、|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, "原画": /(原画|作画)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|・|、|=|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, "作画监督助理": /[\u3040-\u9fa5]*?(?<!総|总)(作監|作画監督|作监|作画监督|作艦)(補佐|补佐|协力|協力|辅佐|辅助|助理)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|=|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, "演出助理": /(演出|(?<!作画)監督)(補佐|补佐|协力|辅佐|辅助|協力|助理|助手)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|、|=|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, "剪辑": /(剪辑|編集)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|・|、|&|=|\u0026|•|♦|◆|■|\s(?!:|:))/g, "CG 导演":/(3DCGディレクター|CGディレクター|3DCG导演|CG导演)\s*?(?:\uff1a|\u003A|】|\/|/|·|・|、|=|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, "美术监督":/(美術|美术|美術監督|美术监督)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|・|、|=|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, "背景美术":/(背景)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|、|・|=|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, "制作进行":/(制作进行|制作進行)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|・|=|、|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, "制作管理":/(制作デスク|制作管理|制作主任)\s*?(?:\uff1a|\u003A|】|\/|/|=|·|、|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, "设定制作": /(设定制作|設定制作)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|、|=|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, "制作协力": /[\u3040-\u9fa5]*?(制作協力|制作协力|協力プロダクション)\s*?(?:\uff1a|\u003A|】|\/|/|·|=|、|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, //以下为bangumi没有的职位 "总作画监督助理": /(総|总)(作監|作画監督|作监|作画监督|作艦)(補佐|补佐|协力|協力|辅佐|辅助|助理)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|=|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, "色彩演出": /(カラースクリプト)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|、|=|&|\u0026|、|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, "氛围稿": /(イメージボード)\s*?(?:\uff1a|\u003A|】|\/|/|·|、|・|、|=|&|\u0026|、|・|・|、|&|\u0026|•|♦|◆|■|\s(?!:|:))/g, }; const regex_sym = /[\uff1a\u003A【】\//、、&\u0026♦◆■=]/g; function parsePersonInfo(raw){ const parser = new DOMParser(); const doc = parser.parseFromString(raw, 'text/html'); const col = $("#columnInSubjectA", doc).eq(0); return $("> div.light_odd", col).get().flatMap((el) => { return $("div div.prsn_info p span.badge_job", el).map((_,role) =>{ return { "name": $("div h2 a",el).contents().filter((_, el) => el.nodeType === 3).text().trim(), "name_cn": $("div h2 a span.tip",el).text(), "relation": $(role).text(), "eps": $(role).next().text(), "id": $("div.rr a",el).attr("href").split("/").pop(), "comments": parseInt($("div.rr small",el).text().slice(2, -1)), }; }).get(); }); } async function getPersonInfo(subjectId) { const [epRsp, rsp] = await Promise.all([ fetch(`https://api.bgm.tv/v0/episodes?subject_id=${subjectId}`, { method: 'GET' }), fetch(`${site}subject/${subjectId}/persons`, { method: 'GET' }) ]); if (!epRsp.ok || !rsp.ok) { console.error('无法获取条目或人物信息'); return; } let epRaw = await epRsp.json(); let raw = await rsp.text(); //处理章节desc let r = new DefaultDict(() => new DefaultDict(() => [])); for(var ep of epRaw.data){ let { id, desc } = ep; desc = desc.replaceAll("\r","").replaceAll(regex_sym,"、"); let regexes = Object.entries(regexes_per).sort((a,b) => { const posA = desc.search(a[1]); const posB = desc.search(b[1]); return posA < 0 ? 1 : posB < 0 ? -1 : posA - posB; }); regexes.forEach(([role, regex]) => { const matches = desc.match(regex); for(var ro in r[id]){ r[id][ro].forEach(x => { const replaced = trimCommas( x.name.replaceAll( regexes_role_per[role] ,"" )); if( replaced[0] != x.name[0] ) x.name = trimCommas( x.name.replaceAll( regexes_role[role] ,"" )); else x.name = replaced; }); } r[id][role] = []; if (matches) { r[id][role] = matches.map(x => ({ href: [], name: trimCommas(x) })); } }); regexes.forEach(([role, regex]) => { const matches = desc.match(regex); for(var ro in r[id]){ r[id][ro].forEach(x => { const replaced = trimCommas( x.name.replaceAll( regexes_role_per[role] ,"")); if( replaced[0] != x.name[0] ) x.name = trimCommas( x.name.replaceAll( regexes_role[role] ,"")); else x.name = replaced; }); } }); //过滤重复元素 for(var ro in r[id]){ const simplified = r[id][ro].sort((a,b) => b.name - a.name).reduce((p,n) => { if( !p.includes(n.name) && n.name != ' ' ) return p + n.name + '^^^'; else return p; },'').split("^^^").map( x => ({ href: [], name: trimCommas(x) })); if(simplified[simplified.length-1].name == "") simplified.pop(); r[id][ro] = simplified; } } //处理章节参与 const doc = $.grep(parsePersonInfo(raw), function(r) { return r.eps!=""; }); for(var person of doc){ const eps = person.eps ? parsePageno(person.eps) : [-1]; eps.forEach(ep => { ["SP", "OP", "ED"].forEach((key, index) => { if (ep[key]) { ep[key].forEach(sort => { const fEp = epRaw.data.find(function(item) { return item.sort == sort && item.type == index + 1; }); if (fEp) { const i = r[fEp.id][person.relation].findIndex((x) => { if(person.name_cn) x.name = x.name.replace(person.name_cn,person.name); if(x.name.includes(person.name)) return true; else{ x.name = replaceSpace(x.name,person.name); if(x.name.includes(person.name)) return true; } }); const [href,name] =[`/person/${person.id}`, `<a href="/person/${person.id}" class="l">${person.name}</a>`]; if(i == -1) r[fEp.id][person.relation].push({href,name}); else r[fEp.id][person.relation][i].name=r[fEp.id][person.relation][i].name.replace(person.name,name); } }); } }); if (!ep.SP && !ep.OP && !ep.ED) { const fEp = epRaw.data.find(function(item) { return item.sort == ep && item.type == 0; }); if (fEp) { const i = r[fEp.id][person.relation].findIndex((x) => { if(person.name_cn) x.name = x.name.replace(person.name_cn,person.name); if(x.name.includes(person.name)) return true; else{ x.name = replaceSpace(x.name,person.name); if(x.name.includes(person.name)) return true; } }); const [href,name] =[`/person/${person.id}`, `<a href="/person/${person.id}" class="l">${person.name}</a>`]; if(i == -1) r[fEp.id][person.relation].push({href,name}); else r[fEp.id][person.relation][i].name=r[fEp.id][person.relation][i].name.replace(person.name,name); } } }); } return Object.entries(r).sort((a, b) => a[0] - b[0]); } function formatEpInfo_0(epinfo,epid) { return `<div class="epinfo">` + epinfo.map(([role, persons]) => { const p = `${persons.map(({ href, name }) => name).join('、')}`; return p.replaceAll(/<(\w|\W)*?>/g,"").length>30 ? `<span class="epinfo-${epid} epinfo-hidden">`+`<span class="tip">${role}</span>:` + p +`<br /></span>`: `<span class="epinfo-${epid} ">`+`<span class="tip">${role}</span>:` + p +`<br /></span>`; }).join('') + `</div>` + `<hr class="board" />`+ `<a class="toggle-${epid} l toggle-pointer" hidden>更多制作人员</a>`+ `<hr class="toggle-${epid} board" hidden/>`; } function formatEpInfo_1(epinfo,epid) { return `<div class="epinfo">` + epinfo.map(([role, persons]) => { const p = `${persons.map(({ href, name }) => name).join('、')}`; return p.replaceAll(/<(\w|\W)*?>/g,"").length>30 ? `<small class="epinfo-${epid} epinfo-hidden grey ps">`+`<small class="grey">${role}</small>:` + p +`<br /></small>`: `<small class="epinfo-${epid} grey ps">`+`<small class="grey">${role}</small>:` + p +`<br /></small>`; }).join('') + `</div>`; } function formatEpInfo_2(epinfo,epid) { return `<div class="epinfo"><hr class="board" /><h2 class="subtitle">参与制作人员</h2><div class="ep-personinfo">` + epinfo.map(([role, persons]) => { const p = `${persons.map(({ href, name }) => { const namematch = name.match(/<a[\w\W]+?<\/a>/g); if(namematch) return namematch.join("、"); else return ''; }).filter(x => x!='').join('、')}`; if(p) return `<span class="epinfo-${epid} tip">`+`<span class="tip">${role}</span>:` + p +`<br /></span>`; else return ''; }).join('') + `</div></div>`; } async function initEpInfo_0(epEl,sid) { let epinfo = sessionStorage.getItem(`epinfo-${sid}`); if (!epinfo) { epinfo = await getPersonInfo(sid); sessionStorage.setItem(`epinfo-${sid}`, JSON.stringify(epinfo)); } else { epinfo = JSON.parse(epinfo); } if (epinfo){ if (epinfo.length < 1) return; //没有任何参与信息 for (const [ep, roles] of epinfo) { if(Object.entries(roles).map( x => x[1]).filter((arr) => arr.length > 0).length == 0) continue; const html = formatEpInfo_0(mergeKeys(Object.entries(roles), (ps) => ps.map(p => p.name).join('、')),ep); const prgInfoEl = $(`#prginfo_${ep} span.tip`); prgInfoEl.find('span.cmt.clearit').before(html); if( $( `.epinfo-${ep}.epinfo-hidden` ).get().length > 0 ) $(`#prginfo_${ep} span.tip .toggle-${ep}`).removeAttr("hidden"); prgInfoEl.find(`a.toggle-${ep}`).click(() => { $(`.epinfo-${ep}`).fadeIn(); $(`#prginfo_${ep} span.tip .toggle-${ep}`).attr('hidden', true); }); } } } let isEpInit = false; async function initEpInfo_1(epEl,sid) { if (isEpInit) { return; } isEpInit = true; let epinfo = sessionStorage.getItem(`epinfo-${sid}`); if (!epinfo) { epinfo = await getPersonInfo(sid); sessionStorage.setItem(`epinfo-${sid}`, JSON.stringify(epinfo)); } else { epinfo = JSON.parse(epinfo); } if (epinfo){ if (epinfo.length < 1) return; //没有任何参与信息 for (const [ep, roles] of epinfo) { if(Object.entries(roles).map( x => x[1]).filter((arr) => arr.length > 0).length == 0) continue; const html = formatEpInfo_1(mergeKeys(Object.entries(roles), (ps) => ps.map(p => p.name).join('、')),ep); const prgInfoEl = epEl.find(el => $(`h6 a`,el).attr('href') == '/ep/' + ep); if(prgInfoEl!=null) $(html).appendTo(prgInfoEl); } } } async function initEpInfo_2(sid,epid) { let epinfo = sessionStorage.getItem(`epinfo-${sid}`); if (!epinfo) { epinfo = await getPersonInfo(sid); sessionStorage.setItem(`epinfo-${sid}`, JSON.stringify(epinfo)); } else { epinfo = JSON.parse(epinfo); } if (epinfo){ if (epinfo.length < 1) return; //没有任何参与信息 const ep = epinfo.find(x => x[0] == epid); const html = formatEpInfo_2(mergeKeys(Object.entries(ep[1]), (ps) => ps.map(p => p.name).join('、')),ep[0]); $('.singleCommentList').before(html); } } function initToggle_0() { async function update(el) { if(el.firstElementChild){ let sid = el.firstElementChild.firstElementChild.getAttribute("subject_id"); let els = $(`a[subject_id='${sid}']`,el).get(); if(sid == null){ sid = url.split('/').pop(); els = epEl; } await initEpInfo_0(els,sid); } } //鼠标悬停时拉取参与信息 for(const el of epUl){ el.addEventListener("mouseenter",()=>{ if(!el.hasAttribute("isShown")){ el.setAttribute("isShown",true); update(el); } }); } } function initToggle_1() { let isShown = { [null]: true, 'true': true, 'false': false }[localStorage.getItem('bgm-epinfo-shown')]; async function update() { toggleEl.html(isShown ? svgCollapse : svgExpand); if (isShown) { const [sid,ep] = url.split('/').slice(-2); await initEpInfo_1(epEl,sid); } $(".epinfo").toggleClass('epinfo-hidden', !isShown); } $(".line_detail").addClass("subject_ep_section"); $(".subject_ep_section").append(`<div class="epinfo-toggle"></div>`) const toggleEl = $(".epinfo-toggle"); let longPressTimer; toggleEl.click(() => { isShown = !isShown; update(); }).on('mousedown touchstart', (e) => { e.preventDefault(); longPressTimer = setTimeout(() => { isShown = !isShown; if (!confirm(`是否切换默认为${isShown ? '展开' : '收起'}章节参与信息?`)) { return; } localStorage.setItem('bgm-epinfo-shown', isShown); update(); }, 750); }).on('mouseleave mouseup touchend', (e) => { e.preventDefault(); clearTimeout(longPressTimer); }); update(); } function main() { if(mode == 0) initToggle_0(); else if(mode == 1) initToggle_1(); else if(mode == 2){ const [sid] = $('.nameSingle a').attr('href').split('/').slice(-1); const epid = url.split('/').pop(); initEpInfo_2(sid,epid); } } main(); })(); class DefaultDict { constructor(defaultInit) { return new Proxy({}, { get: (target, name) => name in target ? target[name] : (target[name] = defaultInit()) }) } } /// Merge keys with the same value function mergeKeys(objEntries, keyFn) { const d = objEntries.reduce((acc, [k, v]) => { const sk = keyFn(v); if(sk!="") acc[sk] = [sk in acc ? acc[sk][0] + '、' + k : k, v]; return acc; }, {}); return Object.entries(d).map(([sk, kv]) => kv); } /// e.g. `1,3-5,7` => [1, 3, 4, 5, 7] `OP1-2` => {OP:[1,2]} `8.5` => {SP:[8.5]} function parsePageno(pn) { return pn.split(',').flatMap(x => { for ( var key of ["SP", "OP", "ED"] ) { if( x.startsWith(key) ){ x = x.replaceAll(key,""); const [start, end] = x.split('-').map(x => parseFloat(x)); return {[key]:Array.from({ length: (end || start) - start + 1 }, (_, i) => start + i)}; } } if (/[.]/.test(x) && !/[-]/.test(x)) { return { "SP":[parseFloat(x)] }; } const [start, end] = x.split('-').map(x => parseFloat(x)); return Array.from({ length: (end || start) - start + 1 }, (_, i) => start + i); }); } //去除首尾、和空格 function trimCommas(x){ if (typeof x === 'string'){ x = x.trim(); while(x.startsWith("、")){ x = x.replace("、","").trimStart(); } while(x.endsWith("、")) { x = x.split("").reverse().join("").replace("、","").trimStart().split("").reverse().join(""); } } return x; } //匹配姓名中间带空格的人物名称,并进行替换 function replaceSpace(ori_str, per_str){ if(typeof ori_str === 'string' && typeof per_str === 'string'){ for(var i = 1; i < per_str.length ; i++){ const per_withSpace = `${per_str.slice(0, i)} ${per_str.slice(i)}`; if(ori_str.includes(per_withSpace)){ return ori_str.replace(per_withSpace,per_str); } const per_withFullSpace = `${per_str.slice(0, i)} ${per_str.slice(i)}`; if(ori_str.includes(per_withFullSpace)){ return ori_str.replace(per_withFullSpace,per_str); } } } return ori_str; }