// ==UserScript== // @name OpenCD 工具箱 // @namespace opencd-upload-helper // @version 9.76.18 // @description OpenCD 全站工具箱:发种/发布页自动填充 · 批量发种与批量打标 · 静默发布 · 自动标签(27 固定标签 / 别名归类 / MusicBrainz / 简介流派识别)· 发种查重 · 平台换源解析(Qobuz 等)· 悬浮球调度中心 · 17 套主题 // @icon data:image/x-icon;base64,AAABAAEAQEAAAAEAIAAoQgAAFgAAACgAAABAAAAAgAAAAAEAIAAAAAAAAAAAABILAAASCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOqjAQXpogBK6qUHsuqmDezqpQf/6qUG/+qlCvrqpg3e6aQFo+miAFfpogAQ6qMCA+mjAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOmiABvqowO/66wd//HJav/44q7/+uvJ//ruz//56L//9tua//HGZP/triH/66gO/+qkBaXpowEx6qMDBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOmiAAjpoQSr8L9O//789v////////////7////+//////////////////////////rryv/wwlj/66kU/+qjA4rpogATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADpnwAd7K8j//zx2v///////v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+//////////////rqxP/utzr/6aQEu+mhABsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6J4AI+yxKv/8+e7//v////7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v///////vrx//DBVP/poQSy6aEADQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOigABftrR30++vK///////+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+///////89ef/7rc5/+mfAHEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADpogAI6qIGo/PLbf////////////7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7///////jjsf/rpwro6KAAIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOifADHrpxHj9tiT/////v///////v////7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v//////////////8cRb/+ihApwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6Z8ATOqnC93yyGb/+ea5//7///////////////////////////////////////////////7////45rn/8sls//fdov/rqxj26qIAQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADpoQA56J4Al+yvI/fwwFP/8895//fcof/55rz/+urH//rqxf/45rr/99yh//PPef/xwVT/7K8i7OicAM/wvkz39M97/umhALzpogAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOmiAAzqogBA6J0Ab+mhAKPrqRTM7bEn7O2zLvvtsy767LEm6+ypE8rpoQCi6J0AbuqhADzpowEr6qQG2vbVjP/utTP56aIAaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADpogAV6aIAJOqkBDDqpAQ26qQGNeqkBC/qogAj6aIAFAAAAAAAAAAAAAAAAOmhAGPuszD/9tmW/+mfAM3pogAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADpogAI6qQEvfLHY/7xxWD76JwAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOmgADfusin/9NKE/+uqGerpoQA3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADpogAD6J8CifHCV//yzHH/6JwAmemiAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOmhAB/rqRLa88x0/++5P/3onwBIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOmiAAHpogED6qUIBemkBQbpogAX6aIAOemiAFjpogFw6aIBg+mjAo7powKa6qQDp+qkBqzqpAOm6J4C0fC/Tv/yyWr+6qYL4umiApbpogFo6aIAO+mjAw/qpgoF6aMDBOmiAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADpogAF6qQFDemiAB3pogBV6aMFk+uoEc/rqRT/6qcP/+qnDv/rqhn/7bMt/++5P//wvkz/8cNa//LIZv/yyWr/8sdm//HFYP/xwVP++OKu//fZl//wvk3/7bEm/+uoDv/qqBH/66oV5+qlCKXpogBn6aIALOqlBhDqowIK6aIAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOmjAw3qowIl6aIAcOupE8/srBr/7K0d/++8RP/zzXf/99ud//fgqf/23J//9daO/vPNdPrwwVj07rc57O2wKefsriHk66wa4+uqF+TsriHj7bIs5vDCWPj0znv/77xF/fPMc/733qf/+OGs//XXj//xxV3/7rMs/+ytH//sqxnx6qUHoumhAFXqpAQh6aMCEumiAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6aIAB+mhADDppAaY664f/+2yK//yx2X/9tiV//bYkv/yxF717K4h4OmfANrnmwDU6Z4AxemhAKzpoQCT6Z4AeOigAF/ooABT6aAATumhAEfpoQBF6aAATuigAFXomwC78stw/e63N/vpngDs6J0AyOebANfsrB3k8stv/PnmvP/66cL/9teU//LGY//ttjb/7K8j/+qmCrfpoABj6aEAKuqiABIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6qMAF+mhAHbrrR797rg6//POdv/0znj/7LAn4eicAMzongCy6aEAfumgAEzpogAlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6aMAM+qlCOL11oz/66oV4emiARzpogAP6qAAT+mgAJjnmgDE6aEA1/DBVfj54rD/+/HX//jktP/00X//77tD/+yvIv7qogCi6KEAUOqjAiYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6aAAHOqiAKbtsy3/8MNb//DAUfzpoADK6J4AmeihAE/pogALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADqoQCN8L5O/fLHZv/poQCy6aIAAwAAAAAAAAAAAAAAAOmhADTpoACC55kAuOqmCd3zyWr/+unB//z15P/56cL/88x0/+63Of/qpgvQ6J4Ab+qiADXpowIDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9M95COeZAJ7ttDL/8L9P/+umCdPpnwB/6aIAGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6aEAGOqkA9j104f/7LAo8emgAFkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADqogBE6J0AkeidAMbvukH/99yf//358P/++/T/9tiU//C+TP/rqRPg6J0AheqiADsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD36MkE+PDgMfTNc3nrpxn277tE/+mhALzqoQA0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADpogFn7rUz//TUiP/poQLK6aIAGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADpoQAi6Z4AceidALzuuDv/9tmW///+/f//////9+Cp//HCWf/rqBDg6J0AhumjAS4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+fDdUvfjuZ/utDDA771J/+yqFuPpoAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6aEAB+mkCsLyzHD/8MBS/uibAHsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADpowEd6J8AaemgAMfvvUv/+OS1///////+/vv/9+Gs//HBVP/poQDN6Z4Ab+miAQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v38G/37+9Lz0IK66qYJvfDAUv/rqRGlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADonwBE7rQt/vXVjf/srCLl6J8AMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADpogAp6J0AgOysGfX0znn//v37///////9+Ov/9tud/+2yLPnonQCw6aEANQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPz580n369Xz8s59q+2xKNjzymz/66oSsemhAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6aIABemgBZ3zy3D/88xy/+mgALDpogAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOmiAAPpoABG6aMBzvDAU//99+v////////////779P/88pr/+meANbpoQBnAAAAAAAAAAAAAAAAAAAAAAAAAAD47dak79Sg4/TShoLsqxPS99ye/+yuH+7onwATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADpoAAz66oV4/fcn//utzj66J0AWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOqjASTpnwCw8b9O//347P////////////368P/33aD/66gP7+ifAIgAAAAAAAAAAAAAAAAAAAAA9ufI0OzLjOj11Y5q6JoBt/npw//wvk7+6aAAhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6aIABOieAIPyxV//9teS/+qnEOHpoAAXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6aEAHOmeArLzyWv///////7////+/////v79//row//tsCj36J0AkemiAAEAAAAAAAAAAPXkw9fnv3H99tWNZOaRAIr21o//+uzJ/+qlCO7qogBbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADpogAf6aAIy/jgqv/yw1z/6qIBqwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADpoAA07KkT4vjluf///////v7+//7+////////+u7S/+6zL/jonQCEAAAAAAAAAAD148C0469L//v274HzyW1X6qQJ6fz04f/44a3/6qIA7+qjAXMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6aIAAemhAG/ttDD7+uzL/+yuH/PqogBoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6aIAA+ibAJL11If///////7+/v/+/v7//v7+/////v/67Mv/7K8k+OieAFkAAAAA+/XpYOa2Wv/szZHh881zNuiaAKHxxFz//v7+//nmuf7sqxf86aAAwumiAC0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADpogAC6aABv/XWjf/547L/6qIA4umiADcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOmiAATomwCV9dSI///////+/v7//v7+//7+/v/+/v7////+//fcn//rqA/p6aEAH////yLx2KX94q1F//39/nXuuUMM6Z4A1fTMcv///v3//fjt//LFYP3qpQb/6qQEv+miAEjpogEH6qMDBOmiAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOmhAEjrqA7p/PHa//TShP/poALI6aIAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6aIAAuqjBQXpoABr7LAl//rqx////////v7+//7+/v/+/v7//v7+//7+////////775L/+ieAHz79OcK9ubIltuaF//muWP/////Wu21NB7onQDa8cFS/P346////////PTh//LKb//sqhj/66gO+eqkB6XpogBX6aIAHuqlBg/powIK6aIABumiAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6qMCp/HFYf/9+/T/8cNb/+mfA7PpogAT6aIABumiAAfpogAI6aIACemkAwzqpgkQ6aIBIemiAGXqpAfQ660d//jjs/3//////v7+//7+/v/+/v7//v7+//7+/v/+/v7///////jfqf/rqhPXAAAAAP359CTy267h1IUD/+rHhP/26M1P67AmI+icALnsrRv1+OS1//768f////////////jktv/xxWH/7bAl/+urGf/rqxfm6qUHqumiAHnpoQBS6aIALOqkBh/qpQga6qMBFemiABDpogAN6aIACumiAAfpogAE6aIAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOmgAB7qpQfo+enE//368P/wvkz/6qAFwumhAHfpogB+6aMCjuqkBqDqqA7C66wa8eurGv/usiv/9NKC//357////////v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7////779P/7KoV8wAAAAAAAAAA////RPDTmezRgAD/7c2U//fpza3utjge6qkTd+eaANfwvUr7+ejA//z36f/////////////////8893/996k//PLcv/vukL/7bIu/+ywJv/sryP/7K0e6eupEsrqpQiq6aMDlumhAITpoABy6J8AY+igAFbooABL6KAAQemgADnpoAAz6qEAMeqiADLpogAL6aAAeO62N//+/Pb//vvz//PIaf/uuDz/8L1L//HCWP/zym3/9dSM//jisP/89eP////////////+/////v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+////+ee+/+upE+YAAAAAAAAAAAAAAAD///9a68Z67NSGAP/dnyj/+O/k6/bitDnvuElC6qQHleeXAM7vvEb799yf//rtz//9+O3////////////////////////////99+r/+uvI//nhrv/22Zj/9M98//LIaP/xwVb/8LxI/+65QP/uuDz/7rc4/+61Nf/ttDL/7bMw/+60MP/srR3/6aMCjemgAA7qpAvV9deT/////////////////////////////////////////////v////7+///+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v///////vz4//HFYf/powaaAAAAAAAAAAAAAAAAAAAAAP///2Dty4fl1YwB/9aLAP/u0Jb/+O/ex/XZlkLwvVlB6Z8AeOaUALbqpATg8cNb//fcnv/56sT//PPh//79+//////////////////////////////////////////////////////////////////+/Pj//fnw//336//99+v/+OS1/+yrGPnpoAAr6J0ATu2yKvr89eX//v////7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7//////////fnu//XUif/qpQjf6J8AJgAAAAAAAAAAAAAAAAAAAAAAAAAA+/brT+zMidXdoCr/0HoA/9mTCf/szZD/9u3atPXUhU3xxGEv8shmUOyvInzljwCu6qIA1++9SP/zznr/9tyf//nluf/78Nf//fnx/////////////////////////////v////7////+/////v////7////+/////v/////////yx2X/6KAApemhAAbonwCy8895///+/P/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7///7////////////////////9+e3/+OW3//HEXv/poQDP6Z8AQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///9G9+rOqua7af/ZlAv/0HsA/9ucHP/sz5b/89+6wv///3n+9uJQ8st/NvTOdTnpoQBd5ZEAiuaUAK/qpAXT77o/+vHEXv/0z3v/9deS//jgrP/66cT/+/HZ//357////////////////////////////////////////PTi/+ysG/npogE56J4AN+yrHuv77c/////////////////////////////////////////////////////////////++/b/+/DY//jjtP/11Yz/8cJY/+uoEN7onACX6aIALgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///x3///908tmq0eOwU//UhgD/0X8A/9ubGv/jsU//68iD//Piv7v///99+eSwX/XWkin21o4X77xIIOaUAErmlQBv5ZEAj+aVAK/qpw7K7bIp6e66Qf/wwFL/8shn//PPev/11o//99+m//nmu//67c3//PTh//7+/P/21Yz/6qMAuAAAAADonQCU8cNa//nluP/56MH/+ujC//rowf/55r3/+OW5//jisP/33aD/9daR//TQgP/zy3D/8cNa/+65P//sqxfa6J0AquidAHrqogA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///zT78uOI79Oa+t+lNv/TgwD/0oEA/9WIAP/bmhf/47FO/+nCd//w2qzN/v39mf/++Ib22JxS992kDwAAAAD01IsC55cAFuiaADDnmQBL5pQAX+WSAHvmkwCX6aEAsOuoEcjtryTk7rY0/O+6QP/vvk3/8LxJ/+miAf/qowFf6aIAIOqlCMbsryPr7bIp5+2yK+vtsirq7bAm4+yuId7rqxjS6qcMwemiAbHongCe55sAiOidAG7pogBX6qIAOemiABQAAAAAAAAAAAAA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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADktFYS//78xvr16Mz9+/Ya+/TpAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8B/////////AA////////4AA////////gAB///////+AAD///////4AAP///////gAAf///////AAA///////+AAD///////8AAH///////+Acf/////////4//////////h//////////H/////////8P/////////4/////////gA///////8AAAH/////+AAAAB/////AAf4AA////4D//xwAf//+B///D4Af//w///+P8AP//H///8f+AP/w////x/+Af+D////j/8Af4P///+H/8A/A////8f/4A8h////w//wByH////h//gDEP////H/+AOQP///8P/4AZgP///4f/gBjAH///g/4ADGAB///BgAAOGAAA/+AAAA8MAAAAIAAAD4OAAAAwAAAfwPAAABAAAD/gPgAAGAAAf/gH8AAIAAP//AA/4AwAP///AAH/7//////gAAH//////3gAAAf////+DwAAA/////4A4AAB/////AAOAAD////8eAD4AP////jwAAfwf///+eGEAD/////8BwwAP/////wPHgQ//////x8eZz//////nj5jP/////+ePnM//////4Y+cH//////wD5wf//////wPHD///////84I////////xAD////////gYf////////D/////////8//////8= // @match https://open.cd/* // @grant GM_getValue // @grant GM_setValue // @grant GM_xmlhttpRequest // @grant GM_cookie // @grant GM_download // @grant GM_notification // @grant GM_setClipboard // @grant unsafeWindow // @connect open.cd // @connect music.163.com // @connect m.music.163.com // @connect y.music.163.com // @connect interface.music.163.com // @connect c.y.qq.com // @connect u.y.qq.com // @connect mobiles.kugou.com // @connect mobilecdn.kugou.com // @connect songsearch.kugou.com // @connect mdata.kugou.com // @connect m.kugou.com // @connect www.kugou.com // @connect search.kuwo.cn // @connect kuwo.cn // @connect m.kuwo.cn // @connect mobi.kuwo.cn // @connect sou.kuwo.cn // @connect itunes.apple.com // @connect music.apple.com // @connect musicbrainz.org // @connect api.deezer.com // @connect www.deezer.com // @connect allmusic.com // @connect www.allmusic.com // @connect api.song.link // @connect api.odesli.co // @connect www.qobuz.com // @connect static.qobuz.com // @connect cdn-images.dzcdn.net // @connect tidal.com // @connect www.tidal.com // @connect openapi.tidal.com // @connect resources.tidal.com // @connect auth.tidal.com // @connect api.tidal.com // @connect music.amazon.com // @run-at document-idle // @license MIT // ==/UserScript== (function () { 'use strict'; if (window.top !== window.self) return; /* ================================================================ * §01 常量与配置 * ================================================================ */ const VERSION = '9.76.18'; const AUTO_FILL_PRESETS = true; const OCD_Z = { PROBE_PANEL: 999998, TOP_BAR: 999999, LAYER_1000001: 1000001, LAYER_1000010: 1000010, MODULE_PANEL: 9999990, BATCH_PANEL: 9999996, HOST: 9999997, FAB: 9999998, SHELL_MENU: 9999999, OPEN_ANIM: 10000010, TASK_DETAIL: 10000030, BATCH_STATS: 10000040, BATCH_MORE: 10000042, SILENT_OBSERVE: 10000045, SILENT_BAR: 10000046, BATCH_INPUT: 10000047, TAGPRESET: 10000048, TIP_TOAST: 10000050, SILENT_SESSION: 10000060, TASK_MENU: 10000068, COVER_FLOAT: 10000069, SH_POP: 10000070, ASK_POP: 10000072, MYREQ_POP: 10000076, DRAFT_POP: 10000077, EDIT_POP: 10000078, PUB_SUCCESS: 10000079, REQ_PREVIEW: 10000082, APPEND_POP: 10000085, CONFIRM: 10000095, APPEND_CONFIRM: 10000086, APPEND_SUCCESS: 10000087, TAG_POP: 10000090, CIRCUIT_POP: 10000100, RESPOND_POP: 10000110, RESP_CONFIRM: 10000120, RESP_SUCCESS: 10000130, TINFO_SUBPOP: 10000140, TINFO_POP: 10000150, SPEC_POP: 10000160, GRAB_POP: 10000170, UTIL_POP: 10000175, CMDK: 10000190, TOAST: 10000200, }; /* V9.73.18:使用手册线上链接(由 html2link 发布;更新手册后用同 slug 覆盖即可,无需改脚本) */ const MANUAL_URL = 'https://html2link.dev/x/F9g27lI8'; function openManual() { try { const u = String(MANUAL_URL || '').trim(); if (!u || u.indexOf('http') !== 0) { try { toastSafe('使用手册链接未配置(MANUAL_URL 为空)', 'err'); } catch (e) {} return; } try { if (typeof GM_openInTab === 'function') { GM_openInTab(u, { active: true, insert: true }); return; } } catch (e) {} try { window.open(u, '_blank'); } catch (e) {} } catch (e) {} } /* V9.73.21:把主题类同步到所有已打开的面板(此前只有壳菜单自己换肤) */ function syncAllPanelThemeClasses() { try { const cls = 'ocd-theme-' + store.get(THEME_KEY, 'dark'); const els = document.querySelectorAll('[data-ocd-panel], [id^="ocd-"][id$="-panel"], .ocd-util-mask, .ocd-pop'); els.forEach(function (el) { try { Array.from(el.classList).filter(function (c) { return c.indexOf('ocd-theme-') === 0; }).forEach(function (c) { el.classList.remove(c); }); el.classList.add(cls); } catch (e) {} }); } catch (e) {} } /* V9.73.21:对**所有**已打开的面板实时预览入场动画(此前硬编码 7 个 id,漏了通知中心/工具箱维护) */ function previewOpenAnimAllPanels(key) { let played = 0; try { const anim = (OPEN_ANIM_KEY[key] || 'ocd-dp-slide') + ' .45s ease-out'; const els = document.querySelectorAll('[data-ocd-panel], [id^="ocd-"][id$="-panel"], #ocd-silent-box'); els.forEach(function (el) { try { if (!el || el.style.display === 'none' || !el.isConnected) return; el.style.animation = 'none'; void el.offsetWidth; el.style.animation = anim; played++; } catch (e) {} }); } catch (e) {} return played; } /* ================================================================ * V9.73.39 §22 自动标签匹配(固定 27 标签 · 合并别名表 · 地区推断) * 用户确认语义:8 汽车=车库摇滚 ✓ · 15 天籁=Soul/R&B+美声/天籁人声 ✓ · 7 发烧=音质向(24-Bit/Hi-Res) ✓ * ================================================================ */ function ocdNormTag(s) { /* V9.74.2:先解 HTML 实体(& / " / ' 等),否则 Alternative & Indie 会归一成 alternativeampindie 而匹配不上别名表 alternativeindie */ const _nt = String(s || '').replace(/&/gi, '&').replace(/</gi, '<').replace(/>/gi, '>').replace(/"/gi, '\x22').replace(/?39;|'/gi, '\x27').replace(/ /gi, ' ').replace(/r\s*&\s*b/gi, 'rnb'); /* V9.74.6:R&B / R & B → rnb ✓ 让 contemporary r&b 等写法也能命中别名表 ✓ */ return _nt.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff]/g, ''); } /* 多对一合并:来源叫法 -> 固定标签 ID(可在设置里编辑;此处为默认表)*/ const TAG_ALIAS_DEFAULT = { "6": ["pop", "poprock", "vocal", "vocalpop", "teenpop", "citypop", "流行", "泡泡糖"], "11": ["rock", "rocknroll", "alternative", "alternativeindie", "altindie", "indie", "indierock", "grunge", "hardrock", "altrock", "progrock", "psychedelic", "摇滚", "独立摇滚"], "12": ["ost", "soundtrack", "score", "originalmotionpicture", "stagescreen", "filmmusic", "tv", "原声", "影视原声", "配乐"], "9": ["classical", "baroque", "romantic", "opera", "chamber", "orchestral", "symphony", "古典", "歌剧", "室内乐"], "18": ["jazz", "bebop", "swing", "fusion", "smoothjazz", "bossa", "bossanova", "jazzvocal", "爵士"], "19": ["metal", "heavymetal", "deathmetal", "blackmetal", "thrashmetal", "powermetal", "doom", "metalcore", "金属"], "21": ["electronic", "edm", "house", "techno", "trance", "dubstep", "downtempo", "idm", "synthpop", "electro", "ambienttechno", "电子", "电音"], "26": ["rap", "hiphop", "hiphoprap", "trap", "gfunk", "raphiphop", "说唱", "嘻哈"], "10": ["world", "worldmusic", "ethnic", "traditional", "national", "民族", "世界音乐"], "13": ["folk", "acoustic", "singersongwriter", "folksinger", "民间", "民谣"], "14": ["country", "bluegrass", "americana", "countryfolk", "乡村", "乡村民谣"], "23": ["gospel", "religious", "christian", "spiritual", "religion", "宗教", "赞美诗", "圣歌"], "15": ["soul", "randb", "rhythmandblues", "rb", "rbsoul", "soulrandb", "neosoul", "funk", "motown", "vocalist", "belcanto", "天籁", "灵魂", "美声", "人声"], "7": ["hires", "audiophile", "发烧"], "8": ["garage", "garagerock", "车库摇滚", "车库"], "17": ["blues", "rhythmandbluesblues", "bluesrock", "delta", "chicago", "蓝调", "布鲁斯"], "22": ["children", "childrens", "kids", "kidssongs", "nursery", "儿童", "儿歌"], "27": ["musical", "musicals", "showtunes", "castrecording", "broadway", "音乐剧", "歌舞剧"], "16": ["newage", "ambient", "relaxation", "easylistening", "meditation", "新世纪", "轻音乐", "冥想"], "24": ["reggae", "ska", "dancehall", "dub", "rocksteady", "雷鬼"], "25": ["drumbass", "dnb", "drumandbass", "jungle", "bass", "breakbeat", "贝斯", "鼓打贝斯"], "20": ["punk", "punkrock", "hardcore", "postpunk", "oi", "朋克"], }; /* V9.73.61:MusicBrainz 常见词补充表(与内置表合并使用 ✓ 不改动原有行 ✓)*/ const TAG_ALIAS_EXTRA = { "6": ["dancepop", "electropop", "synthpop", "artpop", "baroquepop", "chamberpop", "dreampop", "britpop", "jpop", "kpop", "cantopop", "mandopop"], "11": ["alternativerock", "noiserock", "artrock", "postrock", "shoegaze", "mathrock", "garagerock", "indiepop", "janglepop", "slackerrock", "stonerrock", "bluesrock", "folkrock", "countryrock", "southernrock", "glamrock", "punkrock", "poprock", "softrock", "classicrock"], "12": ["soundtrack", "original soundtrack", "filmscore", "televisionmusic", "videogamemusic"], "9": ["baroquemusic", "romanticclassical", "contemporaryclassical", "choral", "chambermusic", "symphonic"], "18": ["jazzfusion", "vocaljazz", "bigband", "swingmusic", "latinjazz", "acidjazz", "cooljazz", "hardbop"], "19": ["progressivemetal", "symphonicmetal", "folkmetal", "gothicmetal", "nu metal", "numetal", "groovemetal", "sludgemetal"], "21": ["electropop", "dance", "housedance", "deephouse", "techhouse", "electrohouse", "drumandbass", "breakcore", "triphop", "vaporwave", "synthwave", "chiptune", "glitch", "industrialtechno"], "26": ["hiphop", "eastcoasthiphop", "westcoasthiphop", "conscioushiphop", "gangstarap", "drill", "grime", "boombap", "lofihiphop"], "10": ["worldbeat", "afrobeat", "afropop", "latin", "flamenco", "celtic", "gamelan", "klezmer", "reggaeton"], "13": ["contemporaryfolk", "folktronica", "americana", "bluegrass", "countryfolk", "freakfolk", "singer songwriting"], "14": ["contemporarycountry", "countrypop", "outlawcountry", "altcountry", "honkytonk", "westernswing"], "23": ["contemporarychristian", "christianrock", "worship", "gospelmusic", "spirituals", "gregorianchant"], "15": ["contemporaryrnb", "alternativernb", "rnb", "randbsoul", "rnbsoul", "soulmusic", "northernsoul", "philadelphiasoul", "quietstorm", "doowop"], "8": ["garagerock", "sixtiesgarage", "fratrock", "surfrock"], "17": ["electricblues", "chicagoblues", "countryblues", "piedmontblues", "bluesrock", "jumpblues"], "22": ["childrensmusic", "childrens", "kidmusic", "lullaby", "storytelling"], "27": ["musicaltheatre", "westend", "broadway", "castalbum", "operetta"], "16": ["ambient", "drone", "fieldrecording", "neoclassical", "spacemusic", "newagemusic"], "24": ["rootsreggae", "dubmusic", "ragga", "loversrock", "rocksteadyreggae", "ska", "dancehall"], "25": ["drumandbass", "liquidfunk", "neurofunk", "jungle", "dubstep", "bassmusic", "footwork"], "20": ["posthardcore", "hardcorepunk", "skatepunk", "popunk", "pop punk", "streetpunk", "emocore", "grindcore", "screamo"], "7": ["hires", "audiophile"], }; /* 一对多展开:一个来源串同时命中多个固定标签(站点允许多选 ✓)*/ const TAG_SPLIT_DEFAULT = { "poprock": ["6", "11"], "rockpop": ["6", "11"], "funksoul": ["15", "17"], "soulfunk": ["15", "17"], "folkworld": ["13", "10"], "worldfolk": ["13", "10"], "hiphoprap": ["26"], "randb": ["15"], "alternativeindie": ["11"], "countryrock": ["11", "14"], "folkrock": ["11", "13"], "jazzfunk": ["18", "15"], "electronicpop": ["21", "6"], "synthpop": ["21", "6"], "metalcore": ["19", "20"], }; /* V9.74.1:站点「类别」(#source) 真实选项(真机取证 plugin_upload.php ✓ 17 项 ✓ 顺序按站点 ✓)*/ const GENRE_SITE_OPTS = ["流行(Pop)", "古典(Classical)", "器乐(Instrumental)", "原声(OST)", "摇滚(Rock)", "爵士(Jazz)", "新世纪(NewAge)", "舞曲(Dance)", "电子(Electronic)", "民谣(Folk)", "独立(Indie)", "嘻哈(Hip Hop)", "音乐剧(Musical)", "乡村(Country)", "另类(Alternative)", "世界音乐(World)", "其他(Others)"]; /* V9.74.1:固定标签 → 站点类别(内置 21 条 ✓ 可编辑/可关闭 ✓ 见 prefs.genreMap ✓) 未收录:7 发烧(音质向,站点无对应类别 ✗)· 1~5 地区(站点类别不含地区 ✗)→ 不映射,避免设错 ✓ */ const GENRE_MAP_DEFAULT = { "6": "流行(Pop)", "8": "摇滚(Rock)", "9": "古典(Classical)", "10": "世界音乐(World)", "11": "摇滚(Rock)", "12": "原声(OST)", "13": "民谣(Folk)", "14": "乡村(Country)", "15": "流行(Pop)", "16": "新世纪(NewAge)", "17": "爵士(Jazz)", "18": "爵士(Jazz)", "19": "摇滚(Rock)", "20": "摇滚(Rock)", "21": "电子(Electronic)", "22": "其他(Others)", "23": "其他(Others)", "24": "世界音乐(World)", "25": "电子(Electronic)", "26": "嘻哈(Hip Hop)", "27": "音乐剧(Musical)" }; /* 音质向 -> 7 发烧(HiFi):24-Bit / Hi-Res / DSD / 96k 以上 ✓ */ /* V9.73.45:解析规格串(位深/采样率/DSD/MQA/Hi-Res),档位=中(高于 CD 即算 ✓)*/ function tagSpecFrom(text) { const s2 = String(text || ""); const bit = (s2.match(/(\d{1,2})\s*[- ]?\s*bit/i) || [])[1]; const khz = (s2.match(/(\d{2,3}(?:\.\d)?)\s*k\s*hz/i) || [])[1]; return { bit: bit ? Number(bit) : null, khz: khz ? Number(khz) : null, dsd: /dsd|sacd/i.test(s2), mqa: /\bmqa\b/i.test(s2), hires: /hi[- ]?res|hires/i.test(s2) }; } /* 档位=中:位深≥24 或 采样率≥88.2 → 发烧(HiFi) ✓;DSD/MQA/Hi-Res 字样也放行 ✓ */ function tagHintQuality(quality, title, subtitle, tier) { const _tier = tier || "mid"; const head = tagSpecFrom(String(title || "") + " " + String(subtitle || "")); if (head.dsd || head.mqa || head.hires) return { on: true, from: "title", why: "标识(DSD/MQA/Hi-Res)" }; if (head.bit !== null || head.khz !== null) { const ok = hifiByTier(head, _tier); return { on: !!ok, from: "title", why: (head.bit || "?") + "bit/" + (head.khz || "?") + "kHz" }; } const s2 = (Array.isArray(quality) ? quality : [quality]).map(function (x) { return String(x || ""); }).join(" "); const body = tagSpecFrom(s2); if (body.dsd || body.mqa || body.hires) return { on: true, from: "quality", why: "标识(DSD/MQA/Hi-Res)" }; if (body.bit !== null || body.khz !== null) { const ok = hifiByTier(body, _tier); return { on: !!ok, from: "quality", why: (body.bit || "?") + "bit/" + (body.khz || "?") + "kHz" }; } return { on: false, from: "", why: "未识别规格" }; } /* 地区推断:国别码优先 ✓ 文字体系兜底 ✓ */ const TAG_REGION_CN = ["CN", "CHN", "CHINA", "中国大陆", "大陆"]; const TAG_REGION_HT = ["TW", "TWN", "TAIWAN", "HK", "HKG", "HONGKONG", "MO", "MACAU", "香港", "台湾", "港台"]; const TAG_REGION_JPKR = ["JP", "JPN", "JAPAN", "KR", "KOR", "KOREA", "南韩", "韩国", "日本", "日韩"]; const TAG_REGION_WEST = ["US", "USA", "GB", "UK", "DE", "FR", "IT", "ES", "CA", "AU", "SE", "NO", "NL", "IE", "RU", "欧美"]; function tagRegionOf(country, text) { const c = String(country || "").trim().toUpperCase(); if (c) { if (TAG_REGION_CN.indexOf(c) >= 0) return "1"; if (TAG_REGION_HT.indexOf(c) >= 0) return "3"; if (TAG_REGION_JPKR.indexOf(c) >= 0) return "4"; if (TAG_REGION_WEST.indexOf(c) >= 0) return "2"; } const tx = String(text || ""); if (/[\u3040-\u30ff]/.test(tx)) return "4"; /* 假名 -> 日韩 */ if (/[\uac00-\ud7af]/.test(tx)) return "4"; /* 韩文 -> 日韩 */ if (/[\u4e00-\u9fff]/.test(tx)) return "1"; /* 汉字无假名 -> 大陆(低置信 ✓)*/ return c ? "5" : ""; /* 有国别但不在表内 -> 其它地区 ✓ */ } /* 主函数:meta -> { ids:[...], reasons:[...], low:[...] } */ /* V9.73.46:自动标签设置(8 组 ✓ 默认均为"安全/当前行为" ✓)*/ const AUTOTAG_KEY = "ocd_helper_autotag_v1"; const AUTOTAG_DEFAULT = { autoOnParse: false, sources: ["title", "platform", "musicbrainz"], strictOnly: true, hintQuality: true, hintRegion: true, splitOneToMany: true, alias: null, conflict: "all", hifiTier: "mid", titleOnlyHifi: false, regionMode: "suggest", introMode: "intro+mb", genreMapOn: true, /* V9.74.1:标签→类别映射表开关(默认开 ✓)*/ genreMap: null /* V9.74.1:用户自定义覆盖(id → 类别文字;空串 = 该标签不映射 ✓)*/ }; function getAutoTagPrefs() { let p = {}; try { p = store.get(AUTOTAG_KEY, null) || {}; } catch (e) { p = {}; } const out = Object.assign({}, AUTOTAG_DEFAULT, p); if (!Array.isArray(out.sources) || !out.sources.length) out.sources = AUTOTAG_DEFAULT.sources.slice(); if (["all", "best"].indexOf(out.conflict) < 0) out.conflict = "all"; if (["strict", "mid", "wide"].indexOf(out.hifiTier) < 0) out.hifiTier = "mid"; if (["auto", "suggest", "off"].indexOf(out.regionMode) < 0) out.regionMode = "suggest"; if (["intro", "intro+mb", "mb"].indexOf(out.introMode) < 0) out.introMode = "intro+mb"; return out; } function setAutoTagPrefs(patch) { const cur = getAutoTagPrefs(); const nx = Object.assign({}, cur, patch || {}); try { store.set(AUTOTAG_KEY, nx); } catch (e) {} return nx; } function resetAutoTagPrefs() { try { store.set(AUTOTAG_KEY, Object.assign({}, AUTOTAG_DEFAULT)); } catch (e) {} return getAutoTagPrefs(); } /* ===================== V9.74.1:标签 → 站点「类别」映射(可编辑 ✓ 可关闭 ✓)===================== */ function genreMapUserOf() { try { const u = getAutoTagPrefs().genreMap; return (u && typeof u === "object") ? u : {}; } catch (e) { return {}; } } /* 合并:内置(开关开时)+ 用户覆盖;用户写空串 = 该标签不映射 ✓ */ function genreMapOf() { const out = {}; try { if (getAutoTagPrefs().genreMapOn !== false) { for (const k in GENRE_MAP_DEFAULT) out[k] = GENRE_MAP_DEFAULT[k]; } const u = genreMapUserOf(); for (const k in u) { const v = u[k]; if (v === "" || v === null || v === undefined) delete out[k]; else out[k] = String(v); } } catch (e) {} return out; } function genreMapSet(id, text) { try { const u = JSON.parse(JSON.stringify(genreMapUserOf())); if (text) u[id] = String(text); else u[id] = ""; setAutoTagPrefs({ genreMap: u }); } catch (e) {} return genreMapOf(); } function genreMapDel(id) { try { const u = JSON.parse(JSON.stringify(genreMapUserOf())); delete u[id]; setAutoTagPrefs({ genreMap: u }); } catch (e) {} return genreMapOf(); } /* 档位判定(strict / mid / wide)*/ function hifiByTier(spec, tier) { if (spec.dsd || spec.mqa || spec.hires) return true; if (tier === "wide") return (spec.bit !== null || spec.khz !== null); if (spec.bit === null && spec.khz === null) return false; if (tier === "strict") return (spec.bit !== null && spec.bit >= 24) && (spec.khz !== null && spec.khz >= 88.2); return (spec.bit !== null && spec.bit >= 24) || (spec.khz !== null && spec.khz >= 88.2); /* mid ✓ */ } /* V9.73.49:自动标签设置面板(自带 ✓ 8 组控件 + 别名表 JSON 编辑 + 试算 ✓)*/ function openAutoTagSettings() { const old = document.getElementById('ocd-autotag-cfg'); if (old) { old.remove(); return; } const p = getAutoTagPrefs(); const box = document.createElement('div'); box.id = 'ocd-autotag-cfg'; box.style.cssText = 'position:fixed;right:18px;bottom:18px;z-index:' + ocdTopZ(20) + ';width:430px;max-height:72vh;overflow:auto;border:1px solid var(--border,#444);border-radius:10px;padding:12px;background:var(--bg,#1b1b1b);color:var(--fg,#eee);font-size:12px;line-height:1.7;box-shadow:0 8px 28px rgba(0,0,0,.45);'; const H = (x) => '
(点试算看结果 ✓)' ].join(''); document.body.appendChild(box); /* V9.73.57:已删除重复的旧编辑器(V9.73.54 那份 ✗)*/ const save = (k, v) => { const np = setAutoTagPrefs({}); np[k] = v; store.set(AUTOTAG_KEY, np); }; box.querySelectorAll('[data-at]').forEach(function (el) { el.addEventListener('change', function () { const k = el.getAttribute('data-at'); const v = el.type === 'checkbox' ? !!el.checked : el.value; const np = Object.assign({}, getAutoTagPrefs()); np[k] = v; try { store.set(AUTOTAG_KEY, np); } catch (e) {} try { toastSafe('✅ 已保存:' + k + ' = ' + v, 'ok'); } catch (e) {} }); }); const on = (id, fn) => { const el = box.querySelector('#' + id); if (el) el.addEventListener('click', fn); }; let _atWrap = null; /* V9.73.88:别名版块延迟挂载 ✓ 移到面板最下方 ✓ */ /* V9.73.55:傻瓜化别名编辑(DOM 追加 ✓ 唯一锚点 ✓ 不写 JSON ✓)*/ try { const _tx = box.querySelector('#ocd-at-alias'); if (_tx) _tx.remove(); /* 去掉 JSON 文本域 ✓ */ const _sb = box.querySelector('#ocd-at-save'); if (_sb && _sb.parentElement) _sb.parentElement.remove(); const wrap = document.createElement('div'); wrap.style.cssText = 'margin-top:8px;padding:6px;border:1px dashed var(--border,#444);border-radius:6px;'; const tip = document.createElement('div'); tip.style.cssText = 'opacity:.85;'; tip.textContent = '✏️ 别名归类:填别名 → 选固定标签 → 添加(例如 krautrock → 摇滚)'; const line = document.createElement('div'); line.style.cssText = 'display:flex;gap:6px;margin-top:4px;'; const inp2 = document.createElement('input'); inp2.id = 'ocd-at-newalias'; inp2.placeholder = '别名,如 krautrock / 德国摇滚'; inp2.style.cssText = 'flex:1 1 auto;min-width:0;background:var(--bg-soft,#262626);color:var(--fg,#eee);border:1px solid var(--border,#444);border-radius:6px;padding:3px 6px;'; const sel2 = document.createElement('select'); sel2.id = 'ocd-at-newtag'; sel2.style.cssText = 'background:var(--bg-soft,#262626);color:var(--fg,#eee);border:1px solid var(--border,#444);border-radius:6px;padding:3px 6px;'; try { TAG_GROUPS.flatMap(g => g.row).forEach(function (x) { const o = document.createElement('option'); o.value = x[0]; o.textContent = x[1]; sel2.appendChild(o); }); } catch (e) {} const btn2 = document.createElement('button'); btn2.id = 'ocd-at-addalias'; btn2.textContent = '➕ 添加'; btn2.style.cssText = 'cursor:pointer;white-space:nowrap;flex:0 0 auto;'; line.appendChild(inp2); line.appendChild(sel2); line.appendChild(btn2); const mine = document.createElement('div'); mine.id = 'ocd-at-mylist'; mine.style.cssText = 'margin-top:6px;opacity:.9;'; wrap.appendChild(tip); wrap.appendChild(line); wrap.appendChild(mine); const rowB = document.createElement('div'); rowB.style.cssText = 'display:flex;gap:6px;margin-top:6px;flex-wrap:wrap;'; const expBtn = document.createElement('button'); expBtn.textContent = '📋 导出别名表'; expBtn.style.cssText = 'cursor:pointer;white-space:nowrap;'; expBtn.addEventListener('click', function () { _atDiag("点击 📋导出"); const cur = getAutoTagPrefs().alias || TAG_ALIAS_DEFAULT; const tx3 = JSON.stringify(cur, null, 1); try { GM_setClipboard(tx3); try { toastSafe('📋 别名表已复制到剪贴板 ✓', 'ok'); } catch (e) {} } catch (e) { try { window.prompt('复制以下 JSON:', tx3); } catch (e2) {} } }); const clrBtn = document.createElement('button'); clrBtn.textContent = '↺ 用内置表'; clrBtn.style.cssText = 'cursor:pointer;white-space:nowrap;'; clrBtn.addEventListener('click', function () { _atDiag("点击 ↺用内置表"); setAutoTagPrefs({ alias: null }); refreshMine(); try { toastSafe('↺ 已切换为内置别名表 ✓', 'ok'); } catch (e) {} }); const allBtn = document.createElement('button'); allBtn.id = 'ocd-at-showall'; allBtn.textContent = '👁 显示内置全部'; allBtn.style.cssText = 'cursor:pointer;white-space:nowrap;'; allBtn.addEventListener('click', function () { _atDiag("点击 👁显示内置全部"); _atShowAll = !_atShowAll; allBtn.textContent = _atShowAll ? '🙈 只看新增' : '👁 显示内置全部'; refreshMine(); }); rowB.appendChild(expBtn); rowB.appendChild(clrBtn); rowB.appendChild(allBtn); wrap.appendChild(rowB); _atWrap = wrap; /* V9.73.88:先不挂载 ✓ 见下方统一挂到最末 ✓ */ const cnOf = (id) => { try { const f = TAG_GROUPS.flatMap(g => g.row).find(x => x[0] === id); return f ? f[1] : id; } catch (e) { return id; } }; /* V9.73.58:默认只看"用户新增"的别名 ✓ 内置的用开关展开 ✓ */ let _atShowAll = false; let _atNewCount = 0; let _atAllCount = 0; const _atDiag = (what) => { try { console.log("[OCD Helper] 别名编辑诊断:" + what, JSON.stringify({ showAll: _atShowAll, newCnt: _atNewCount, allCnt: _atAllCount, aliasIsNull: !getAutoTagPrefs().alias, aliasKeys: Object.keys(getAutoTagPrefs().alias || {}).length })); } catch (e) {} }; _atDiag("面板初始化"); const refreshMine = () => { const cur = getAutoTagPrefs().alias; mine.innerHTML = ''; _atNewCount = 0; _atAllCount = 0; /* V9.73.58:重绘前清零 ✓ */ if (!cur) { mine.textContent = '(当前使用内置表 ✓ 添加后自动切换为自定义表 ✓)'; return; } let cnt = 0; Object.keys(cur).forEach(function (id) { (cur[id] || []).forEach(function (a) { /* V9.73.58:与内置表比对 ✓ 判断是否为"用户新增" ✓ */ let _isNew = true; try { const _b = (TAG_ALIAS_DEFAULT[id] || []); _isNew = !_b.some(function (bb) { return ocdNormTag(bb) === ocdNormTag(a); }); } catch (e) {} _atAllCount++; if (_isNew) _atNewCount++; if (!_isNew && !_atShowAll) return; cnt++; const chip = document.createElement('span'); chip.style.cssText = 'display:inline-block;margin:2px 4px 2px 0;padding:1px 6px;border:1px solid var(--border,#444);border-radius:10px;'; chip.textContent = a + ' → ' + cnOf(id) + ' '; /* V9.76.5:标出这条别名是不是第三方模块带来的 ✓(含门控 / 否决状态) —— 免得"我删了它怎么又回来了 / 我明明没加过它"找不到出处 ✓ */ try { const _src = (typeof ecomodTagSource === 'function') ? ecomodTagSource(a) : null; if (_src) { const mk = document.createElement('span'); const st = !_src.enabled ? '(模块已停用)' : (_src.excluded ? '(这一条已被你在模块面板里否决)' : (_src.gated ? '(参数条件不满足,暂不生效)' : '(生效中)')); mk.style.cssText = 'font-size:10px;opacity:.9;color:var(--accent,#5b8cff);margin-right:3px;'; mk.title = '来自第三方模块「' + _src.name + '」' + st + ' —— 在设置中心的「🧩 第三方模块生态」里可以停用 / 否决它'; mk.textContent = '🧩' + (!_src.enabled ? '✕' : (_src.excluded ? '🚫' : (_src.gated ? '⏸' : ''))); chip.insertBefore(mk, chip.firstChild); } } catch (e) {} const del = document.createElement('b'); del.textContent = '✕'; del.style.cssText = 'cursor:pointer;color:#ff6b6b;'; try { del.title = '删掉这条别名(若它来自第三方模块,模块还装着时会被重新写回 —— 要永久去掉请到「🧩 第三方模块生态」里否决它)'; } catch (e) {} del.addEventListener('click', function () { const np = JSON.parse(JSON.stringify(getAutoTagPrefs().alias || TAG_ALIAS_DEFAULT)); np[id] = (np[id] || []).filter(function (x) { return x !== a; }); if (!np[id].length) delete np[id]; setAutoTagPrefs({ alias: np }); refreshMine(); try { toastSafe('🗑 已删除别名:' + a, 'ok'); } catch (e) {} }); chip.appendChild(del); mine.appendChild(chip); }); }); try { const info = document.createElement('div'); info.style.cssText = 'margin-top:4px;opacity:.75;'; info.textContent = _atShowAll ? ('当前显示全部 ' + _atAllCount + ' 条(含内置 ✓)其中你新增 ' + _atNewCount + ' 条') : ('你新增 ' + _atNewCount + ' 条;内置 ' + Math.max(0, _atAllCount - _atNewCount) + ' 条未显示 ✓'); mine.appendChild(info); } catch (e) {} if (!cnt) { mine.textContent = _atShowAll ? '(内置表项为空 ✓)' : '(暂无自定义别名 ✓ 当前用内置表 ✓ 可点下面按钮查看内置条目)'; } }; btn2.addEventListener('click', function () { _atDiag("点击 ➕添加"); try { box.style.outline = '2px solid #ffd166'; setTimeout(function () { box.style.outline = ''; }, 400); } catch (e) {} /* 视觉反馈:确认点击确实到达 ✓ */ const name = String(inp2.value || '').trim(); if (!name) { try { toastSafe('请先填写别名 ✓ 例如 krautrock', 'err'); } catch (e) {} return; } const id = sel2.value; const base = JSON.parse(JSON.stringify(getAutoTagPrefs().alias || TAG_ALIAS_DEFAULT)); base[id] = base[id] || []; if (base[id].indexOf(name) < 0) base[id].push(name); setAutoTagPrefs({ alias: base }); inp2.value = ''; refreshMine(); _atDiag("添加后重绘"); try { toastSafe('✅ 已添加:' + name + ' → ' + cnOf(id) + '(立即生效 ✓ 可点试算核对 ✓)', 'ok'); } catch (e) {} }); refreshMine(); } catch (e) { try { console.warn('[OCD Helper] 别名编辑器初始化失败:', e && e.message); } catch (e2) {} } /* V9.73.63:数据源开关(可视化 ✓)*/ try { const ds = document.createElement('div'); ds.style.cssText = 'margin-top:8px;padding:6px;border:1px dashed var(--border,#444);border-radius:6px;'; const dst = document.createElement('div'); dst.style.cssText = 'opacity:.85;'; dst.textContent = '🗂 数据源开关(决定用哪些来源推断标签)'; ds.appendChild(dst); [ ['title', '标题/副标题规格(如 [24Bit-44.1kHz] → 发烧)'], ['platform', '平台流派(Qobuz / QQ 等页面流派)'], ['musicbrainz', 'MusicBrainz 兜底(流派 + 国别,缺流派时才联网)'] ].forEach(function (row) { const k = row[0], label = row[1]; const lb = document.createElement('label'); lb.style.cssText = 'display:flex;align-items:center;gap:6px;margin:3px 0;cursor:pointer;'; const cb = document.createElement('input'); cb.type = 'checkbox'; cb.checked = (getAutoTagPrefs().sources || []).indexOf(k) >= 0; cb.addEventListener('change', function () { const cur = (getAutoTagPrefs().sources || []).slice(); const i = cur.indexOf(k); if (cb.checked) { if (i < 0) cur.push(k); } else if (i >= 0) cur.splice(i, 1); setAutoTagPrefs({ sources: cur }); try { toastSafe('✅ 数据源「' + label + '」已' + (cb.checked ? '启用' : '关闭'), 'ok'); } catch (e) {} }); const sp = document.createElement('span'); sp.textContent = label; lb.appendChild(cb); lb.appendChild(sp); ds.appendChild(lb); }); box.appendChild(ds); } catch (e) { try { console.warn('[OCD Helper] 数据源开关初始化失败:', e && e.message); } catch (e2) {} } /* V9.73.71:简介/联网模式下拉 ✓ */ try { const rowI = document.createElement('div'); rowI.style.cssText = 'margin-top:8px;padding:6px;border:1px dashed var(--border,#444);border-radius:6px;'; const lb = document.createElement('div'); lb.style.cssText = 'opacity:.85;'; lb.textContent = '📄 标签来源模式(简介 vs 联网)'; const s3 = document.createElement('select'); s3.id = 'ocd-at-intromode'; s3.style.cssText = 'background:var(--bg-soft,#262626);color:var(--fg,#eee);border:1px solid var(--border,#444);border-radius:6px;padding:3px 6px;margin-top:4px;width:100%;'; [ ['intro', '① 只用简介 / 平台流派(不联网,最快)'], ['intro+mb', '② 简介优先,缺流派时才联网补充(默认 ✓)'], ['mb', '② 总是联网获取(MusicBrainz 补齐流派 + 国别)'] ].forEach(function (o) { const op = document.createElement('option'); op.value = o[0]; op.textContent = o[1]; if ((getAutoTagPrefs().introMode || 'intro+mb') === o[0]) op.selected = true; s3.appendChild(op); }); s3.addEventListener('change', function () { setAutoTagPrefs({ introMode: s3.value }); try { toastSafe('✅ 标签来源模式:' + s3.options[s3.selectedIndex].textContent, 'ok'); } catch (e) {} }); rowI.appendChild(lb); rowI.appendChild(s3); box.appendChild(rowI); /* V9.73.88:把「✏️ 别名归类」挂到面板**最下方** ✓ */ try { if (_atWrap) box.appendChild(_atWrap); } catch (e) {} } catch (e) {} /* ===== V9.74.1:⑧ 标签 → 站点「类别」映射表(可编辑 ✓ 可启停 ✓ 挂在 ⑦ 别名版块下方 ✓)===== */ try { const gm = document.createElement("div"); gm.id = "ocd-at-genre-wrap"; gm.style.cssText = "margin-top:8px;padding:6px;border:1px dashed var(--border,#444);border-radius:6px;"; const gt = document.createElement("div"); gt.style.cssText = "opacity:.85;"; gt.textContent = "🏷➡📂 标签 → 站点「类别」映射(供「自动设置类别」使用 ✓ 解析后按标签把类别设为对应项 ✓)"; gm.appendChild(gt); const onLb = document.createElement("label"); onLb.style.cssText = "display:flex;align-items:center;gap:6px;margin:3px 0;cursor:pointer;"; const onCb = document.createElement("input"); onCb.type = "checkbox"; onCb.id = "ocd-at-genre-on"; onCb.checked = getAutoTagPrefs().genreMapOn !== false; onCb.addEventListener("change", function () { setAutoTagPrefs({ genreMapOn: !!onCb.checked }); try { toastSafe("✅ 标签→类别映射表已" + (onCb.checked ? "启用" : "关闭(回退名称模糊匹配)"), "ok"); } catch (e) {} }); const onSp = document.createElement("span"); onSp.textContent = "启用映射表(关掉 = 用旧的名称模糊匹配 ✓)"; onLb.appendChild(onCb); onLb.appendChild(onSp); gm.appendChild(onLb); const addRow = document.createElement("div"); addRow.style.cssText = "display:flex;gap:6px;margin-top:4px;flex-wrap:wrap;"; const tagSel = document.createElement("select"); tagSel.id = "ocd-at-genre-tag"; tagSel.style.cssText = "flex:1 1 110px;min-width:0;background:var(--bg-soft,#262626);color:var(--fg,#eee);border:1px solid var(--border,#444);border-radius:6px;padding:3px 6px;"; try { TAG_GROUPS.flatMap(g => g.row).forEach(function (x) { const o = document.createElement("option"); o.value = x[0]; o.textContent = x[1]; tagSel.appendChild(o); }); } catch (e) {} const tgtSel = document.createElement("select"); tgtSel.id = "ocd-at-genre-target"; tgtSel.style.cssText = "flex:1 1 130px;min-width:0;background:var(--bg-soft,#262626);color:var(--fg,#eee);border:1px solid var(--border,#444);border-radius:6px;padding:3px 6px;"; (function () { const seen = {}; const push = function (x) { const v = String(x || "").trim(); if (!v || seen[v]) return; seen[v] = 1; const o = document.createElement("option"); o.value = v; o.textContent = v; tgtSel.appendChild(o); }; try { const sel = document.querySelector("#source"); if (sel) Array.from(sel.options).forEach(function (o) { if (String(o.value) !== "0") push(o.textContent); }); } catch (e) {} try { GENRE_SITE_OPTS.forEach(push); } catch (e) {} })(); const addBtn = document.createElement("button"); addBtn.id = "ocd-at-genre-add"; addBtn.textContent = "➕ 添加/覆盖"; addBtn.style.cssText = "cursor:pointer;white-space:nowrap;flex:0 0 auto;"; addRow.appendChild(tagSel); addRow.appendChild(tgtSel); addRow.appendChild(addBtn); gm.appendChild(addRow); const gList = document.createElement("div"); gList.id = "ocd-at-genre-list"; gList.style.cssText = "margin-top:6px;"; gm.appendChild(gList); const btnRow = document.createElement("div"); btnRow.style.cssText = "display:flex;gap:6px;margin-top:6px;flex-wrap:wrap;"; const showBtn = document.createElement("button"); showBtn.id = "ocd-at-genre-showall"; showBtn.textContent = "👁 显示内置全部"; showBtn.style.cssText = "cursor:pointer;white-space:nowrap;"; const resetBtn = document.createElement("button"); resetBtn.id = "ocd-at-genre-reset"; resetBtn.textContent = "↺ 恢复内置映射"; resetBtn.style.cssText = "cursor:pointer;white-space:nowrap;"; const expB = document.createElement("button"); expB.id = "ocd-at-genre-export"; expB.textContent = "📋 导出映射表"; expB.style.cssText = "cursor:pointer;white-space:nowrap;"; btnRow.appendChild(showBtn); btnRow.appendChild(resetBtn); btnRow.appendChild(expB); gm.appendChild(btnRow); let _gmAll = false, _gmUserN = 0, _gmAllN = 0; const cn2 = function (id) { try { const f = TAG_GROUPS.flatMap(g => g.row).find(x => x[0] === id); return f ? f[1] : id; } catch (e) { return id; } }; const refreshGm = function () { const user = genreMapUserOf(); const merged = genreMapOf(); gList.textContent = ""; _gmUserN = 0; _gmAllN = 0; const ids = Object.keys(merged).concat(Object.keys(user).filter(function (k) { return !(k in merged); })); let cnt = 0; ids.forEach(function (id) { const isUser = Object.prototype.hasOwnProperty.call(user, id); const off = isUser && !user[id]; _gmAllN++; if (isUser) _gmUserN++; if (!isUser && !_gmAll) return; cnt++; const chip = document.createElement("span"); chip.style.cssText = "display:inline-block;margin:2px 4px 2px 0;padding:1px 6px;border:1px solid var(--border,#444);border-radius:10px;" + (isUser ? "border-color:#ffd166;" : ""); chip.textContent = id + " " + cn2(id) + " → " + (off ? "(已关闭 ✓ 不映射)" : merged[id]) + " " + (isUser ? "✎ " : ""); const del = document.createElement("b"); del.textContent = "✕"; del.style.cssText = "cursor:pointer;color:#ff6b6b;"; del.title = isUser ? "删除自定义(恢复内置 ✓)" : "关闭该标签的映射(不设置类别 ✓)"; del.addEventListener("click", function () { if (isUser) genreMapDel(id); else genreMapSet(id, ""); refreshGm(); try { toastSafe(isUser ? ("🗑 已删除自定义映射:" + cn2(id)) : ("🚫 已关闭映射:" + cn2(id)), "ok"); } catch (e) {} }); chip.appendChild(del); gList.appendChild(chip); }); const info = document.createElement("div"); info.style.cssText = "margin-top:4px;opacity:.75;"; info.textContent = _gmAll ? ("当前显示全部 " + _gmAllN + " 条 · 其中自定义/关闭 " + _gmUserN + " 条") : ("自定义/关闭 " + _gmUserN + " 条 · 内置 " + Math.max(0, _gmAllN - _gmUserN) + " 条未显示 ✓(点上方按钮展开)"); gList.appendChild(info); if (!cnt) { gList.textContent = "(无自定义映射 ✓ 当前用内置表 ✓)"; } }; addBtn.addEventListener("click", function () { const id = tagSel.value, tx = String(tgtSel.value || "").trim(); if (!id || !tx) { try { toastSafe("请选择标签与目标类别 ✓", "err"); } catch (e) {} return; } genreMapSet(id, tx); refreshGm(); try { toastSafe("✅ 已保存:" + cn2(id) + " → " + tx + "(立即生效 ✓)", "ok"); } catch (e) {} }); showBtn.addEventListener("click", function () { _gmAll = !_gmAll; showBtn.textContent = _gmAll ? "🙈 只看自定义" : "👁 显示内置全部"; refreshGm(); }); resetBtn.addEventListener("click", function () { setAutoTagPrefs({ genreMap: null }); refreshGm(); try { toastSafe("↺ 已恢复内置映射表 ✓", "ok"); } catch (e) {} }); expB.addEventListener("click", function () { const tx = JSON.stringify(genreMapOf(), null, 1); try { GM_setClipboard(tx); try { toastSafe("📋 映射表已复制到剪贴板 ✓", "ok"); } catch (e) {} } catch (e) { try { window.prompt("复制以下 JSON:", tx); } catch (e2) {} } }); refreshGm(); box.appendChild(gm); } catch (e) { try { console.warn("[OCD Helper] 标签→类别映射表初始化失败:", e && e.message); } catch (e2) {} } on('ocd-at-close', () => box.remove()); on('ocd-at-save', () => { const _txe = box.querySelector('#ocd-at-alias'); const tx = _txe ? String(_txe.value || '').trim() : ''; if (!tx) { setAutoTagPrefs({ alias: null }); try { toastSafe('✅ 已改为使用内置别名表 ✓', 'ok'); } catch (e) {} return; } try { const o = JSON.parse(tx); setAutoTagPrefs({ alias: o }); try { toastSafe('✅ 别名表已保存并生效 ✓', 'ok'); } catch (e) {} } catch (e) { try { toastSafe('❌ JSON 解析失败:' + (e.message || ''), 'err'); } catch (e2) {} } }); on('ocd-at-clear', () => { const _txc = box.querySelector('#ocd-at-alias'); if (_txc) _txc.value = ''; setAutoTagPrefs({ alias: null }); try { toastSafe('↺ 已切换为内置别名表 ✓', 'ok'); } catch (e) {} }); on('ocd-at-export', () => { const cur = getAutoTagPrefs().alias || TAG_ALIAS_DEFAULT; const tx = JSON.stringify(cur, null, 1); try { GM_setClipboard(tx); try { toastSafe('📋 别名表已复制到剪贴板 ✓', 'ok'); } catch (e) {} } catch (e) { window.prompt('复制以下 JSON:', tx); } }); on('ocd-at-try', () => { const m = (function () { try { return window.__ocdLastParsedMeta || null; } catch (e) { return null; } })(); const r = autoTags(m || {}); const nameOf = (id) => { try { const f = TAG_GROUPS.flatMap(g => g.row).find(x => x[0] === id); return f ? f[1] : id; } catch (e) { return id; } }; const o = box.querySelector('#ocd-at-out'); o.textContent = [ '元数据:' + JSON.stringify(m || {}), '将自动勾选:' + (r.ids.length ? r.ids.map(nameOf).join('、') : '(无)'), '仅建议(不勾):' + (r.low.length ? r.low.map(nameOf).join('、') : '(无)'), '依据:' + r.reasons.join(' | ') ].join('\n'); }); on('ocd-at-reset', () => { resetAutoTagPrefs(); try { toastSafe('↺ 已恢复默认设置 ✓ 请重开面板查看 ✓', 'ok'); } catch (e) {} box.remove(); openAutoTagSettings(); }); return true; } /* V9.73.60:MusicBrainz 兜底(流派 + 国别)· 缓存 + 1 req/s 节流 ✓ */ const MB_CACHE = {}; let _mbLast = 0; async function mbFetchJson(url) { for (let i = 0; i < 3; i++) { const wait = Math.max(0, 1000 - (Date.now() - _mbLast)); if (wait) await new Promise(function (r) { setTimeout(r, wait); }); _mbLast = Date.now(); try { const d = await gmFetchJSON(url, { Accept: "application/json", "User-Agent": "OpenCD-Helper/1.0 (userscript)" }); return d || null; } catch (e) { if (i === 2) { try { console.warn("[OCD Helper] MusicBrainz 失败:", e && e.message); } catch (e2) {} return null; } } } return null; } async function mbLookup(artist, album) { const a = String(artist || "").trim(), b = String(album || "").trim(); if (!a || !b) return null; const key = (a + "|" + b).toLowerCase(); if (MB_CACHE[key]) return MB_CACHE[key]; const q = "https://musicbrainz.org/ws/2/release-group/?query=" + encodeURIComponent('release:"' + b + '" AND artist:"' + a + '"') + "&fmt=json&limit=3"; const d = await mbFetchJson(q); const rgs = (d && d["release-groups"]) || []; if (!rgs.length) { MB_CACHE[key] = null; return null; } const nb = normKey(b); rgs.sort(function (x, y) { const sx = (normKey(x.title).indexOf(nb) === 0 ? 100 : 0) + (Number(x.score) || 0); const sy = (normKey(y.title).indexOf(nb) === 0 ? 100 : 0) + (Number(y.score) || 0); return sy - sx; }); const pick = rgs[0]; const out = { mbid: pick.id, name: pick.title || "", genres: [], country: "" }; const det = await mbFetchJson("https://musicbrainz.org/ws/2/release-group/" + pick.id + "?inc=genres&fmt=json"); if (det && Array.isArray(det.genres)) out.genres = det.genres.map(function (g) { return g && g.name; }).filter(Boolean); let aid = ""; try { const ac = (det && det["artist-credit"]) || pick["artist-credit"] || []; aid = (ac[0] && ac[0].artist && ac[0].artist.id) || ""; } catch (e) {} if (aid) { const ad = await mbFetchJson("https://musicbrainz.org/ws/2/artist/" + aid + "?fmt=json"); try { if (ad && ad.country) out.country = ad.country; else if (ad && ad.area && ad.area["iso-3166-1-codes"] && ad.area["iso-3166-1-codes"][0]) out.country = ad.area["iso-3166-1-codes"][0]; } catch (e) {} } MB_CACHE[key] = out; try { console.log("[OCD Helper] MusicBrainz:", JSON.stringify({ name: out.name, genres: out.genres.slice(0, 5), country: out.country })); } catch (e) {} return out; } /* 异步版:平台 genre 缺失时用 MusicBrainz 补齐(流派 + 国别)*/ /* V9.73.68:从简介文本提取流派与音质规格(用户截图证据:Genre: … / Available in: 24-Bit/48 kHz ✓)*/ function tagIntroExtract(text) { const s2 = String(text || ""); const out = { genre: "", quality: [] }; try { /* V9.73.73:标签名支持多种写法 ✓(英文 Genre/Style(s)/Tags,中文 类型/流派/曲风/风格/标签/类别/分类)*/ const _labelRe = /(?:^|[\s|\[((])(?:genres?|styles?|tags?|类型|流派|曲风|风格|标签|类别|分类)\s*[::]\s*([^\n\r]+)/i; const m1 = s2.match(_labelRe) || s2.match(/Genre\s*[::]\s*([^\n\r]+)/i); if (m1) { /* V9.73.73:值按多值拆分 ✓(Pop/Rock Rock Alternative & Indie → pop / rock / alternative / indie)*/ const _raw = String(m1[1]).replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim(); const _parts = _raw.split(/[\/、,,;;|·]+|\s*&\s*|\s+and\s+/i).map(function (x) { return String(x || "").trim(); }).filter(Boolean); const _flat = []; _parts.forEach(function (p) { if (/\s/.test(p) && p.split(/\s+/).length > 1) { p.split(/\s+/).forEach(function (w) { if (w) _flat.push(w); }); } else _flat.push(p); }); out.genre = (_flat.length ? _flat : [_raw]).join(" / "); } const m2 = s2.match(/(?:available in|quality|音质|规格|格式)\s*[::]\s*([^\n\r]+)/i) || s2.match(/Available in\s*[::]\s*([^\n\r]+)/i); if (m2) out.quality = String(m2[1]).replace(/<[^>]*>/g, " ").split(/[,,、;;]|\s{2,}/).map(function (x) { return x.trim(); }).filter(Boolean); if (!out.quality.length) { const m3 = s2.match(/(\d{1,2}\s*[- ]?\s*Bit\s*\/\s*[\d.]+\s*k?Hz)/i); if (m3) out.quality = [m3[1]]; } } catch (e) {} return out; } async function autoTagsAsync(meta) { const m = Object.assign({}, meta || {}); const p = getAutoTagPrefs(); let _im = p.introMode || "intro+mb"; /* V9.73.72:提升到函数作用域 ✓ */ let _mbAllowed = (p.sources || []).indexOf("musicbrainz") >= 0 && _im !== "intro"; /* V9.73.68:**优先**用简介里的流派/音质(用户的待发布简介通常带 Genre / Available in ✓)*/ try { /* V9.73.71:简介是对象 { st, content, introFrom } ✓ 需兼容对象/字符串 ✓ */ const _introRaw = m.intro || m.introText || m.desc || ""; const _intro = (typeof _introRaw === "string") ? _introRaw : ((_introRaw && (_introRaw.content || _introRaw.text)) || ""); if (_intro) { const ie = tagIntroExtract(_intro); if (ie.genre && !String(m.genre || "").trim()) { m.genre = ie.genre; m._fromIntro = 1; } if (ie.quality && ie.quality.length) { m.quality = (Array.isArray(m.quality) && m.quality.length) ? m.quality : ie.quality; m._fromIntro = 1; } } } catch (e) {} const hasGenre = !!(m.genre && String(m.genre).trim()); try { _im = p.introMode || "intro+mb"; if (_im === "mb") { try { m._forceMb = 1; } catch (e) {} } /* mb 模式:总是联网补齐 ✓(已在 try 内 ✓)*/ _mbAllowed = (p.sources || []).indexOf("musicbrainz") >= 0 && _im !== "intro"; /* intro 模式不联网 ✓ */ if ((!hasGenre || m._forceMb) && _mbAllowed) { const mb = await mbLookup(m.artist, m.title || m.album); if (mb) { if (mb.genres && mb.genres.length) { m.genre = mb.genres.join(" / "); m._mb = 1; } if (!m.country && mb.country) { m.country = mb.country; m._mb = 1; } } } } catch (e) {} const r = autoTags(m); if (m._mb) r.reasons = ["MusicBrainz 补齐:genre=" + (m.genre || "-") + " country=" + (m.country || "-")].concat(r.reasons || []); if (m._fromIntro && !m._mb) r.reasons = ["来自简介:genre=" + (m.genre || "-") + " quality=" + [].concat(m.quality || []).slice(0, 1).join("")].concat(r.reasons || []); return r; } /* V9.73.66:兜底 —— 「更多设置」里点 🏷️ 自动标签设置 时打开设置面板 ✓ */ try { document.addEventListener("click", function (ev) { try { const el = ev.target && ev.target.closest ? ev.target.closest("[data-more=\"ocd-batch-autotag\"], #ocd-batch-autotag, [data-key=\"ocd-batch-autotag\"]") : null; if (!el) return; ev.preventDefault(); ev.stopPropagation(); openAutoTagSettings(); /* 批量更多设置·自动标签 ✓ */ } catch (e) {} }, true); } catch (e) {} function autoTags(meta) { const m = meta || {}; const p = getAutoTagPrefs(); /* V9.73.46:按设置生效 ✓ */ const hit = {}; const low = {}; const why = []; let alias = TAG_ALIAS_DEFAULT, split = TAG_SPLIT_DEFAULT; /* V9.73.56:优先使用设置面板里的自定义表(prefs.alias ✓)—— 此前只读旧键 ✗ 导致不生效 ✗ */ try { if (p && p.alias && typeof p.alias === "object" && Object.keys(p.alias).length) alias = p.alias; } catch (e) {} /* V9.73.61:把 MB 补充词合并进别名表(用户自定义优先 ✓ 其后补充 ✓ 最后内置 ✓)*/ try { const _merged2 = {}; for (const k in TAG_ALIAS_DEFAULT) _merged2[k] = (TAG_ALIAS_DEFAULT[k] || []).slice(); for (const k in TAG_ALIAS_EXTRA) { _merged2[k] = (_merged2[k] || []).concat(TAG_ALIAS_EXTRA[k] || []); } if (alias === TAG_ALIAS_DEFAULT || (p && p.alias && Object.keys(p.alias).length)) alias = Object.assign({}, _merged2, alias === TAG_ALIAS_DEFAULT ? {} : alias); else alias = _merged2; if (alias === TAG_ALIAS_DEFAULT) alias = _merged2; } catch (e) {} /* 兜底:无论如何都保证补充词可用 ✓ */ try { const _has = (id, w) => (alias[id] || []).some(function (x) { return normKey(x) === normKey(w); }); for (const k in TAG_ALIAS_EXTRA) { (TAG_ALIAS_EXTRA[k] || []).forEach(function (w) { if (!_has(k, w)) { alias[k] = (alias[k] || []).concat([w]); } }); } } catch (e) {} try { const ov = store.get("ocd_helper_tagalias_v1", null); if (ov && typeof ov === "object") { if (ov.alias && typeof ov.alias === "object") alias = ov.alias; if (ov.split && typeof ov.split === "object") split = ov.split; } } catch (e) {} /* V9.73.63:平台流派受 sources.platform 控制(关掉则只用标题规格 + 地区 ✓)*/ const _usePlatform = !p.sources || p.sources.indexOf("platform") >= 0; const genres = _usePlatform ? (Array.isArray(m.genre) ? m.genre : String(m.genre || "").split(/[,\/;|]+/)) : []; genres.map(function (g) { return ocdNormTag(g); }).filter(Boolean).forEach(function (g) { let done = false; if (p.splitOneToMany && split[g]) { split[g].forEach(function (id) { hit[id] = 1; }); why.push("genre:" + g + " -> split " + split[g].join("+")); done = true; } if (!done) { for (const id in alias) { const arr = alias[id] || []; if (arr.some(function (a) { return ocdNormTag(a) === g; })) { hit[id] = 1; why.push("genre:" + g + " -> " + id); done = true; } } } if (!done) { for (const id in alias) { const arr = alias[id] || []; if (arr.some(function (a) { const na = ocdNormTag(a); return na.length >= 4 && g.length >= 4 && (g.indexOf(na) >= 0 || na.indexOf(g) >= 0); })) { if (p.strictOnly) { low[id] = 1; why.push("genre~" + g + " ≈ " + id + "(低置信 ✓ 仅建议)"); } else { hit[id] = 1; why.push("genre~" + g + " ≈ " + id + "(阈值放宽 → 勾选 ✓)"); } done = true; } } } if (!done) why.push("genre:" + g + " 未识别 ✓ 仅提示"); }); if (p.hintQuality) { var _q = tagHintQuality(m.quality, m.title, m.subtitle, p.hifiTier); if (_q.on) { hit["7"] = 1; why.push("发烧(HiFi)<-" + (_q.from || "?") + ":" + _q.why + "(档位=" + p.hifiTier + ")"); } else if (_q.why && _q.why !== "未识别规格") { why.push("发烧(HiFi):不满足档位(" + p.hifiTier + "):" + _q.why); } } const reg = p.hintRegion && p.regionMode !== "off" ? tagRegionOf(m.country, (m.artist || "") + " " + (m.title || "") + " " + (m.subtitle || "")) : ""; if (reg && p.regionMode === "suggest") { low[reg] = 1; why.push("region:" + (m.country || "(文字体系)") + " -> " + reg + "(低置信建议 ✓ 未自动勾选 ✓)"); } else if (reg) { hit[reg] = 1; why.push("region:" + (m.country || "(文字体系)") + " -> " + reg + "(regionMode=auto ✓ 自动勾选 ✓)"); } /* V9.73.48:补回 auto 路径 ✓ */ /* V9.73.47:删除遗留的重复赋值(suggest 模式不得自动勾地区 ✗)*/ /* V9.73.47:删除遗留的重复赋值(suggest 模式不得自动勾地区 ✗)*/ let _hitIds = Object.keys(hit).sort(function (a, b) { return Number(a) - Number(b); }); if (p.conflict === "best" && _hitIds.length > 1) { const _genre = ["6","11","12","9","18","19","21","26","10","13","14","23","15","8","17","22","27","16","24","25","20"]; const _g = _hitIds.filter(function (id) { return _genre.indexOf(id) >= 0; }); if (_g.length > 1) { _hitIds = _hitIds.filter(function (id) { return _genre.indexOf(id) < 0 || id === _g[0]; }); why.push("conflict=best → 流派只保留 " + _g[0]); } } const ids = _hitIds; const lowIds = Object.keys(low).filter(function (id) { return !hit[id]; }).sort(function (a, b) { return Number(a) - Number(b); }); return { ids: ids, low: lowIds, reasons: why }; } /* V9.73.41:把自动匹配结果应用到「🏷️分类标签」面板(只补勾 ✓ 绝不取消已勾 ✓ 宁缺勿错 ✓)*/ /* V9.73.43:文档级事件委托 —— 按钮何时渲染都能生效 ✓(首次失败原因:绑定点早于渲染 ✗)*/ try { document.addEventListener("click", function (ev) { try { const el = ev.target && ev.target.closest ? ev.target.closest("#ocd-autotag-btn") : null; if (!el) return; ev.preventDefault(); ev.stopPropagation(); console.log("[OCD Helper] 自动标签:按钮被点击 ✓ meta=", JSON.stringify(window.__ocdLastParsedMeta || null)); Promise.resolve(applyAutoTags(null)).catch(function (e) { console.warn("[OCD Helper] 自动标签异常:", e && e.message); }); } catch (e) {} }, true); } catch (e) {} let _autoTagLast = null; /* V9.73.44:上一次本按钮自动勾选的 id(仅会话内 ✓ 不落盘 ✓)*/ async function applyAutoTags(meta) { const pop = document.getElementById("ocd-tag-pop"); let m = meta || null; if (!m) { try { m = window.__ocdLastParsedMeta || null; } catch (e) {} } if (!m) { try { toastSafe("✨ 自动匹配需要平台元数据:请先完成解析(或用 __ocd.applyAutoTags(meta) 传入)", "err"); } catch (e) {} return { ok: false, added: 0, msg: "no-meta" }; } if (!pop) { /* V9.73.43:自动打开 🏷️分类标签 面板后再应用(真正一键 ✓)*/ try { const openBtn = document.getElementById("ocd-tag-btn"); if (openBtn) openBtn.click(); } catch (e) {} let waited = 0; while (waited < 2000 && !document.getElementById("ocd-tag-pop")) { await new Promise(function (res) { setTimeout(res, 100); }); waited += 100; } const pop2 = document.getElementById("ocd-tag-pop"); if (!pop2) { try { toastSafe("✨ 未能打开 🏷️分类标签 面板(请手动点一下再试)", "err"); } catch (e) {} return { ok: false, added: 0, msg: "no-panel" }; } return applyAutoTags(m); } const r = await autoTagsAsync(m); /* V9.73.44:若上次自动勾选的那批标签**现在仍然都勾着** → 视为"再点一次撤销" ✓ */ const _isOn = (el) => /var\(--ok/.test(String(el.getAttribute("style") || "")); try { if (_autoTagLast && _autoTagLast.length) { const still = _autoTagLast.filter(function (id) { const el = pop.querySelector("[data-tag=\"" + id + "\"]"); return el && _isOn(el); }); if (still.length === _autoTagLast.length) { let off = 0; still.forEach(function (id) { const el = pop.querySelector("[data-tag=\"" + id + "\"]"); if (el) { try { el.click(); off++; } catch (e) {} } }); const nameOf2 = (id) => { try { const f = TAG_GROUPS.flatMap(g => g.row).find(x => x[0] === id); return f ? f[1] : id; } catch (e) { return id; } }; _autoTagLast = null; try { toastSafe("↩️ 已撤销上一次自动勾选(" + off + " 个):" + still.map(nameOf2).join("、") + " · 你的手动选择未受影响", "ok"); } catch (e) {} try { console.log("[OCD Helper] 自动标签:已撤销", JSON.stringify(still)); } catch (e) {} return { ok: true, toggledOff: off, ids: still }; } } } catch (e) {} const isOn = (el) => /var\(--ok/.test(String(el.getAttribute("style") || "")); let added = 0; let already = 0; const missing = []; r.ids.forEach(function (id) { const el = pop.querySelector("[data-tag=\"" + id + "\"]"); if (!el) { missing.push(id); return; } if (isOn(el)) { already++; return; } try { el.click(); added++; } catch (e) {} }); const nameOf = (id) => { try { const f = TAG_GROUPS.flatMap(g => g.row).find(x => x[0] === id); return f ? f[1] : id; } catch (e) { return id; } }; const addNames = r.ids.map(nameOf); const lowNames = r.low.map(nameOf); let msg = "✨ 已自动勾选 " + added + " 个" + (already ? "(另有 " + already + " 个已勾选)" : "") + ":" + addNames.join("、"); if (lowNames.length) msg += " | 建议(低置信度,未勾选):" + lowNames.join("、"); if (missing.length) msg += " | 面板未找到:" + missing.join("、"); _batchTagProgressShow(msg + "\n(每条任务的打标结果已写入其任务卡备注 ✓ · 同一批再点一次可一次性撤销 ✓)", undo.length ? "ok" : "warn"); try { toastSafe(msg, "ok"); } catch (e) {} try { console.log("[OCD Helper] 自动标签:", JSON.stringify({ ids: r.ids, low: r.low, reasons: r.reasons })); } catch (e) {} try { _autoTagLast = added ? r.ids.slice() : null; } catch (e) {} /* V9.73.44:记住本次自动勾选的 id ✓ 便于再点撤销 ✓ */ if (added) { try { console.log("[OCD Helper] 自动标签:已勾选 " + added + " 个(再点一次可撤销 ✓)"); } catch (e) {} } return { ok: true, added: added, already: already, ids: r.ids, low: r.low, missing: missing, reasons: r.reasons }; } const CHANGELOG = [ { v: '9.76.18', d: '2026-10-06', items: [ '起因:用户 m14220–m14222 真机报障(截图 + 原话)—— 搜索求助助手(专辑「满江红」)点「🔍 开始搜索」后,① 那行显示「🟡 ① 站内查重 · 命中 7 条 ▸」却**点不动**、面板上**没有继续下一环节的搜索选项**、点「🧹 清空」只弹出「正在搜索中…请稍候再清空」✗ 三项现象同时出现 ✓', '取证(两条路径混用 ✓):CDP 探针挂在**主世界**给 Element.prototype.innerHTML setter 打钩(TM 5.x 下脚本与页面同域 ✓)→ 抓到唯一一条「innerHTML 清空含结果块的父节点」:tgt=`sh_result` / len=0 / 栈 `at clearSearchResults (