// ==UserScript== // @name 豆瓣电影下载大师 // @namespace https://github.com/Shshuaicc/douban-download-master // @version 5.2.1 // @description 豆瓣电影详情页下载/网盘/在线 三大类资源聚合搜索 v5.2.1 - 移除复制按钮·界面精简 // @author Shshuaicc // @match *://movie.douban.com/subject/* // @connect * // @grant GM_xmlhttpRequest // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_listValues // @grant GM_deleteValue // @grant GM_setClipboard // @grant GM_registerMenuCommand // @run-at document-start // @license MIT // ==/UserScript== /* * ============================================================ * 豆瓣电影下载大师 v5.2.1 * 作者: Shshuaicc * 更新日志: * v5.2.1 - 界面精简 * 【组件移除】删除「📋 复制全部可用链接」按钮(含 addCopyBtn 逻辑与亮/暗 CSS) * v5.2.0 - 纯白实底化·结果纯化·国际站全自动 * 【实底配色】面板/启动标记去毛玻璃改纯色底:亮色 #ffffff、暗色 #1c1c1e * - 分区卡片亮色 rgba(255,255,255,0.5)→#f6f6f7 浅灰分层;边框/阴影重配 * - backdrop-filter 全删(Toast/Tip 除外),饱和度问题从根源消除 * 【结果纯化】移除「显示全部 (N)」开关:无资源站点检测后直接隐藏,仅显示可用站点 * 【国际站全自动】移除「🌐 检测 N 个国际源」手动触发器:Tier3 与普通站点同样自动检测, * 仅按速度特点 1→2→3 顺序最后执行 * v5.1.0 - 配色可读性重构·去荧光·全量淡字强化 * 【配色体系】「可用绿」统一双模令牌:亮色 #1A7F37/#2DA44E,暗色 #4CC38A * - 毛玻璃饱和度 180%→150%,去除荧光泛光;图标饱和滤镜归 1.0 * - 修复整体 opacity 叠加导致的人眼不可读文字: * stats 0.35→0.75 / filter-toggle 0.4→0.75 / inaccessible 0.35→0.55 * - 可用站点字重 600→500,10-11px 小字对比度全部 ≥4.6:1 (WCAG AA) * v5.0.0 - 视觉降噪重构·信息提纯·双模适配 * 【配色体系】面板基底 rgba(255,255,255,0.82)/rgba(35,35,38,0.78) + blur(40px) saturate(180%) * - 边框统一 rgba(120,120,128,0.12);可用态去荧光实底,改极淡底+文字着色+微描边 * - 强调色收敛为 iOS Blue #007AFF/#0A84FF;文本三级层级(主0.9/次0.5) * 【信息密度】默认仅显示有效结果;「显示全部 (N)」低调文字链开关 * - Tier3 国际站完全折叠,仅显示「🌐 检测 N 个国际源」触发器 * - 空结果分区折叠为居中「暂未找到相关资源」,点击展开失败详情 * 【组件打磨】链接胶囊 8px/3px 10px/11px,可用态左侧3px绿竖条;检测中 4px 灰点呼吸 * - 进度条 2px 中性灰底+蓝填充,完成变绿无流动;统计栏 10px opacity 0.35 * v4.10.0 - 配色收敛与层次重构 * 【问题一·荧光绿收敛】 * - 可用站点改为深绿半透明底 rgba(48,209,88,0.18)+绿字#30D158+1px描边 * - 摘要数字、计数徽章统一半透明绿方案,font-weight 降 500 * - 进度条完成态改为半透明绿渐变,去除实色填充 * 【问题二·无资源折叠】 * - 默认开启「仅显示有资源」,unavailable/inaccessible/blocked 实时隐藏 * - 「显示全部」按钮始终可见,改为低调文字链 * - Tier3 待检测链接默认折叠,仅保留开始检测按钮;点击后逐个显示 * - 全无资源的梯队区块整体隐藏,不占空间 * 【问题三·层次重构】 * - 面板自适应背景:blur(40px) saturate(150%),深浅色各自半透明底 * - 链接胶囊紧凑化(圆角10px/padding 4px 11px/字号11.5px/gap 5px) * - 梯队标签去背景,改纯文字+左侧3px竖线色条 * - 分区间距6px、内部padding 12px;搜索框/统计栏弱化 * 【问题四·状态清晰】 * - checking 改静态灰底+尾随6px呼吸圆点 * - inaccessible 用 opacity 0.35+删除线,无背景 * - 无结果分区折叠为一行「未找到可用资源」,点击展开 * v4.9.0 - UI视觉与交互全面升级 * v4.8.0 - 检索命中率优化 * v4.7.0 - Bug修复与性能优化 * v4.6.1 - iOS毛玻璃界面·站点核验 * v4.6.0 - 配色与分类调整 * v4.5.0 - 分类合并与UI重构 * v4.4.4 - 渲染与计数统一 * v4.4.3 - 折叠交互优化 * v4.4.2 - 按检索加载速度分层顺序加载 * v4.4.1 - 新增FMHY国际站点集成 * ============================================================ */ (function() { // ============================================================ // 【0号优先级】立即注入可见标记 // ============================================================ try { var _ddm_mark = document.createElement('div'); _ddm_mark.id = 'ddm-boot-mark'; _ddm_mark.innerHTML = 'DDM 5.2.1'; _ddm_mark.style.cssText = 'position:fixed;top:14px;left:14px;z-index:2147483647;background:#ffffff;color:#1c1c1e;font-size:12px;font-weight:600;padding:8px 16px;border-radius:18px;box-shadow:0 4px 16px rgba(0,0,0,0.08),inset 0 0 0 1px rgba(120,120,128,0.16);cursor:pointer;font-family:-apple-system,BlinkMacSystemFont,"SF Pro Text","PingFang SC",sans-serif;transition:all .35s cubic-bezier(0.34,1.56,0.64,1);letter-spacing:0.2px;display:flex;align-items:center;'; _ddm_mark.title = '豆瓣电影下载大师 v5.2.1 已加载 - 点击关闭'; _ddm_mark.onclick = function() { if (_ddm_mark.parentNode) _ddm_mark.parentNode.removeChild(_ddm_mark); }; function _ddm_addMark() { if (document.getElementById('ddm-boot-mark')) return; if (document.body) { document.body.appendChild(_ddm_mark); // v4.9.0: 加载完成后圆点变绿实心 setTimeout(function() { if (_ddm_mark.parentNode) { var dot = _ddm_mark.querySelector('.ddm-boot-dot'); if (dot) dot.className = 'ddm-boot-dot done'; _ddm_mark.style.background = 'rgba(26,127,55,0.07)'; _ddm_mark.innerHTML = 'DDM 5.2.1 ✓'; } }, 1500); } else { setTimeout(_ddm_addMark, 50); } } _ddm_addMark(); console.log('%c[DDM5.2.1] 豆瓣电影下载大师 v5.2.1 已启动', 'color:#1c1c1e;font-size:14px;font-weight:bold;'); } catch(e) { try { alert('DDM v5.2.1启动: ' + e.message); } catch(e2) {} } // ============================================================ // 工具函数 (纯ES5) // ============================================================ function log() { try { var a=['[DDM5.2.1]']; for(var i=0;i/g,'>').replace(/"/g,'"').replace(/'/g,'''); } function randomInt(a,b) { return Math.floor(Math.random()*(b-a+1))+a; } function randomChoice(arr) { return arr[Math.floor(Math.random()*arr.length)]; } function addCSS(css) { try { if (typeof GM_addStyle !== 'undefined') { GM_addStyle(css); return; } } catch(e){} try { var s=document.createElement('style'); s.type='text/css'; s.textContent=css; (document.head||document.documentElement).appendChild(s); } catch(e){} } function copyText(text) { try { if (typeof GM_setClipboard !== 'undefined') { GM_setClipboard(text); return true; } } catch(e){} try { var ta=document.createElement('textarea'); ta.value=text; ta.style.position='fixed'; ta.style.left='-9999px'; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); return true; } catch(e){ return false; } } // ============================================================ // v4.9.0 UI辅助: 展开动画 / Toast / Tooltip // ============================================================ function animateBodyIn(body) { if (!body) return; body.classList.remove('ddm-body-in'); void body.offsetWidth; body.classList.add('ddm-body-in'); } function showToast(msg) { try { var old = document.getElementById('ddm-toast'); if (old && old.parentNode) old.parentNode.removeChild(old); var t = document.createElement('div'); t.id = 'ddm-toast'; t.className = 'ddm-toast'; t.textContent = msg; document.body.appendChild(t); setTimeout(function() { if (t.parentNode) t.classList.add('ddm-toast-hide'); }, 2700); setTimeout(function() { if (t.parentNode) t.parentNode.removeChild(t); }, 3150); } catch(e) { logErr('showToast error', e); } } function showTooltipFor(a, ev) { hideTooltip(); try { var tip = document.createElement('div'); tip.id = 'ddm-tip'; tip.className = 'ddm-tip'; var nm = a.textContent || ''; var desc = a.getAttribute('data-desc') || ''; var tier = a.getAttribute('data-tier') || ''; var html = '
' + esc(nm) + '
'; if (desc) html += '
' + esc(desc) + '
'; if (tier) html += '' + esc(tier) + ''; tip.innerHTML = html; document.body.appendChild(tip); var x = ev.clientX, y = ev.clientY; var tw = tip.offsetWidth || 100, th = tip.offsetHeight || 40; var nx = Math.max(8, Math.min(x - Math.round(tw / 2), window.innerWidth - tw - 8)); var ny = y - th - 14; if (ny < 8) ny = y + 20; tip.style.left = nx + 'px'; tip.style.top = ny + 'px'; } catch(e) { logErr('showTooltip error', e); } } function hideTooltip() { try { var tip = document.getElementById('ddm-tip'); if (tip && tip.parentNode) tip.parentNode.removeChild(tip); } catch(e) {} } // ============================================================ // 反爬模块 // ============================================================ var UA_POOL = [ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' ]; var REFERER_POOL = ['https://www.baidu.com/','https://www.google.com/','https://www.bing.com/','https://movie.douban.com/','']; var ACCEPT_LANG_POOL = ['zh-CN,zh;q=0.9,en;q=0.8','zh-CN,zh;q=0.9']; function getRandomHeaders() { var h = { 'User-Agent':randomChoice(UA_POOL), 'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language':randomChoice(ACCEPT_LANG_POOL), 'Accept-Encoding':'gzip, deflate', 'Cache-Control':'max-age=0', 'DNT':'1' }; var r = randomChoice(REFERER_POOL); if (r) h['Referer']=r; return h; } // ============================================================ // XHR封装 // ============================================================ var _xhrCache = {}; var _requestLastTime = {}; var MIN_REQUEST_INTERVAL = 200; function gmXhr(opts, cb) { var method = opts.method || 'GET'; var url = opts.url; var data = opts.data || null; var headers = opts.headers || {}; var timeout = opts.timeout || 8000; var maxRetries = opts.retries || 1; var retryCount = 0; var cacheKey = method + '|' + url + '|' + (data || ''); if (!opts.noCache && _xhrCache[cacheKey] && (Date.now()-_xhrCache[cacheKey].t < 300000)) { setTimeout(function(){cb(null,_xhrCache[cacheKey].r);},0); return; } function doRequest() { try { var fh = getRandomHeaders(); for (var k in headers) { if (hasOwn(headers,k)) fh[k]=headers[k]; } var host = url.replace(/^https?:\/\//,'').split('/')[0]; var now = Date.now(); var lt = _requestLastTime[host] || 0; var wt = 0; // v4.7.0: 同域名保留 200ms 最小间隔,去掉随机等待;跨域名零等待 if (now-lt < MIN_REQUEST_INTERVAL) wt = MIN_REQUEST_INTERVAL-(now-lt); _requestLastTime[host] = now+wt; function exec() { if (typeof GM_xmlhttpRequest !== 'undefined') { GM_xmlhttpRequest({ method:method, url:url, data:data, headers:fh, timeout:timeout, onload: function(res) { if(!opts.noCache) _xhrCache[cacheKey]={r:res,t:Date.now()}; cb(null,res); }, onerror: function() { if(retryCount 0 ? setTimeout(exec,wt) : exec(); } catch(e) { cb(e); } } doRequest(); } // ============================================================ // GBK编码 // ============================================================ var _gbkTable = null; function getGBKTable() { if (_gbkTable) return _gbkTable; _gbkTable = {}; try { var dec=new TextDecoder('gbk'); for(var l=0x81;l<=0xFE;l++){for(var t=0x40;t<=0xFE;t++){if(t===0x7F)continue; var b=new Uint8Array([l,t]); var c=dec.decode(b); if(c&&c.length===1&&!_gbkTable[c])_gbkTable[c]=[l,t];}} } catch(e){} return _gbkTable; } function encodeGBK(str) { var tb=getGBKTable(), out=[]; for(var i=0;i= 0xac00 && code <= 0xd7af); var isJapanese = (code >= 0x3040 && code <= 0x30ff); var isCyrillic = (code >= 0x0400 && code <= 0x04ff); var isThai = (code >= 0x0e00 && code <= 0x0e7f); if (isKorean || isJapanese || isCyrillic || isThai) { hitForeign = true; break; } cnTitle += ch; } if (hitForeign && cnTitle.trim().length > 0) { info.cnTitle = cnTitle.trim(); } else { var t1 = rawTitle.match(/^([\u4e00-\u9fa5][\s\S]*?)\s+([A-Za-z][\s\S]*)$/); if (t1) { info.cnTitle=t1[1].trim(); info.enTitle=t1[2].trim(); } else { info.cnTitle=rawTitle; } } if (!info.enTitle) { var enMatch = rawTitle.match(/\s+([A-Za-z][A-Za-z0-9\s\-\.:,'&]*)$/); if (enMatch) info.enTitle = enMatch[1].trim(); } var infoEl = document.querySelector('#info'); if (infoEl) { var infoText = infoEl.textContent||''; var im = infoText.match(/IMDb:\s*(tt\d+)/i); if(im)info.imdbId=im[1]; if (/集数|首播|电视台/.test(infoText)) info.isTV = true; if (!info.enTitle) { var ak=infoText.match(/又名:\s*([^\n]+)/); if(ak){var akas=ak[1].split(/[\/,,]/); for(var ai=0;ai 0)) return; _respTimes.push(elapsed); if (_respTimes.length > 30) _respTimes.shift(); if (_respTimes.length < 8) return; var sum = 0; for (var i = 0; i < _respTimes.length; i++) sum += _respTimes[i]; var avg = sum / _respTimes.length; var prev = CONCUR.current; if (avg < 1200) { CONCUR.current = Math.min(CONCUR.max, CONCUR.current + 2); } else if (avg > 3500) { CONCUR.current = Math.max(CONCUR.min, CONCUR.current - 1); } if (CONCUR.current !== prev) log('concurrency adjusted: ' + prev + ' -> ' + CONCUR.current + ' (avg=' + Math.round(avg) + 'ms)'); } function pool(items, limit, worker, progressCb, done) { var results = new Array(items.length); var cursor = 0, running = 0, finished = 0; function getLim() { return (limit > 0) ? limit : getConcurrency(); } function next() { if (finished >= items.length) { if (done) try { done(results); } catch(e) { logErr('pool done error', e); } return; } while (running < getLim() && cursor < items.length) { var idx = cursor++; running++; (function(i) { try { worker(items[i], i, function(err, r) { results[i] = err ? null : r; running--; finished++; if (progressCb) try { progressCb(finished, items.length); } catch(e) {} setTimeout(next, randomInt(10, 50)); }); } catch(e) { logErr('pool worker error', e); running--; finished++; results[i] = null; if (progressCb) try { progressCb(finished, items.length); } catch(e2) {} setTimeout(next, 10); } })(idx); } } if (progressCb) try { progressCb(0, items.length); } catch(e) {} next(); } // ============================================================ // 网盘API搜索 // ============================================================ function searchPanApi(kw, cb) { var apiUrl = PAN_APIS[0].url + encodeURIComponent(kw); gmXhr({url:apiUrl, timeout:10000, retries:2}, function(err, res) { if (err || !res || !res.responseText) { cb([]); return; } try { var json = JSON.parse(res.responseText); var items = []; var seen = {}; if (json.code === 0 || json.code === '0') { var data = json.data || {}; var groups = data.merged_by_type || data.by_type || data; var tagMap = {ali:'ali',aliyun:'ali',quark:'quark',baidu:'baidu',baidupan:'baidu',xunlei:'xunlei',thunder:'xunlei'}; var tagName = {ali:'阿里',quark:'夸克',baidu:'百度',xunlei:'迅雷',other:'网盘'}; var totalAdded = 0; var MAX_TOTAL = 20; var PER_GROUP = 6; for (var type in groups) { if (!hasOwn(groups, type)) continue; if (totalAdded >= MAX_TOTAL) break; var arr = groups[type]; var t = tagMap[type.toLowerCase()] || 'other'; if (Object.prototype.toString.call(arr) === '[object Array]') { var groupCount = 0; for (var i = 0; i < arr.length && groupCount < PER_GROUP && totalAdded < MAX_TOTAL; i++) { var it = arr[i]; var name = it.note||it.name||it.title||it.filename||'资源'; var url = it.url||it.link||it.share_url||''; var pwd = it.password||it.pwd||it.code||it.extract_code||''; if (url) { var normUrl = url.replace(/[?&#].*$/, '').toLowerCase(); if (seen[normUrl]) continue; seen[normUrl] = true; items.push({name:name, url:url, pwd:pwd, tag:t, tagLabel:tagName[t]}); groupCount++; totalAdded++; } } } } } cb(items); } catch(e) { logErr('pan api parse error', e); cb([]); } }); } // ============================================================ // 站点检测 // ============================================================ // v4.7.0: 站点健康度持久化缓存 (GM_setValue/GM_getValue) var HEALTH_KEY_PREFIX = 'ddm_health_'; var HEALTH_TTL = 24 * 60 * 60 * 1000; // 24小时 function getHealth(name) { try { if (typeof GM_getValue === 'undefined') return null; var v = GM_getValue(HEALTH_KEY_PREFIX + name, ''); if (!v) return null; var o = JSON.parse(v); if (o && typeof o.a !== 'undefined' && typeof o.t !== 'undefined') return o; } catch(e) {} return null; } function setHealth(name, available) { try { if (typeof GM_setValue === 'undefined') return; GM_setValue(HEALTH_KEY_PREFIX + name, JSON.stringify({ a: available ? 1 : 0, t: Date.now() })); } catch(e) {} } // 24h 内标记为不可用的站点 → 直接标灰跳过请求 function isHealthSkipped(name) { var h = getHealth(name); return !!(h && h.a === 0 && (Date.now() - h.t) < HEALTH_TTL); } // ============================================================ // v4.8.0 站点类型标记 (智能关键词选择用) // ============================================================ function tagSiteCategories() { var i; for (i = 0; i < DOWNLOAD_SITES.length; i++) { var s = DOWNLOAD_SITES[i]; s._cat = (s.tier === 3) ? 'bt_en' : ((s.tier === 1) ? 'bt_zh' : 'dl_zh'); } for (i = 0; i < PAN_SEARCH_SITES.length; i++) PAN_SEARCH_SITES[i]._cat = 'pan'; for (i = 0; i < ONLINE_SITES.length; i++) ONLINE_SITES[i]._cat = 'online'; } // ============================================================ // v4.8.0 站点专用解析器 (优先于通用评分) // ============================================================ var SITE_PARSERS = { '两个BT': function(text) { return /\.btbox|class="[^"]*btbox/i.test(text); }, '海盗湾': function(text) { return /id="searchResult"/i.test(text); }, '海盗湾备用': function(text) { return /id="searchResult"/i.test(text); }, '1337x': function(text) { return /class="[^"]*table-list/i.test(text); }, '1377x': function(text) { return /class="[^"]*table-list/i.test(text); }, 'YTS官方': function(text) { return /browse-movies-wrap|browse-movies/i.test(text); }, 'YTS': function(text) { return /browse-movies/i.test(text); }, 'Nyaa动漫': function(text) { return /class="[^"]*torrent-list/i.test(text); }, 'BT天堂': function(text) { return /class="[^"]*mlist/i.test(text); }, '小马盘搜': function(text) { return /(pan\.baidu\.com|aliyundrive|alipan\.com|quark\.cn)/i.test(text); } }; // ============================================================ // v4.8.0 反拦截检测 (Cloudflare 挑战页) // ============================================================ function isBlocked(text) { if (!text) return false; var low = text.toLowerCase(); if (low.indexOf('cf-browser-verification') !== -1) return true; if (low.indexOf('challenge-platform') !== -1) return true; if (low.indexOf('attention required') !== -1) return true; if (text.length < 3000 && (low.indexOf('just a moment') !== -1 || low.indexOf('checking your browser') !== -1)) return true; return false; } // ============================================================ // v4.8.0 加权评分算法 // ============================================================ var NEGATIVE_KW = ['没有找到','暂无相关','未找到','搜索不到','无结果','没有搜索到','验证码','captcha','404','页面不存在','page not found','no results','nothing found','0 results','0个结果','0 条结果','该关键词没有找到']; function scoreResult(text, movie) { var score = 0; var low = text.toLowerCase(); var i; // 负面信号 -3 for (i = 0; i < NEGATIVE_KW.length; i++) { if (low.indexOf(NEGATIVE_KW[i].toLowerCase()) !== -1) { score -= 3; break; } } // 页面过短 -5 if (text.length < 800) score -= 5; // 强信号 +3 if (low.indexOf('magnet:?xt=') !== -1) score += 3; if (/\.torrent/i.test(low)) score += 3; if (/(pan\.baidu\.com|aliyundrive|alipan\.com|quark\.cn|cloud\.189\.cn|tianyi|weiyun|pan\.quark\.cn)/i.test(low)) score += 3; // 标题强匹配 +4 if (movie.cnTitle && text.indexOf(movie.cnTitle) !== -1) score += 4; if (movie.enTitle && low.indexOf(movie.enTitle.toLowerCase()) !== -1) score += 4; // 标题前4字部分匹配 +2 var pre4 = (movie.cnTitle || '').substring(0, 4); if (pre4.length >= 2 && text.indexOf(pre4) !== -1) score += 2; var pre4en = (movie.enTitle || '').substring(0, 4).toLowerCase(); if (pre4en.length >= 3 && low.indexOf(pre4en) !== -1) score += 2; // 年份出现 +1 if (movie.year && low.indexOf(movie.year) !== -1) score += 1; // 结果列表HTML结构 +1~2 if (/]+href="[^"]+\.(html|htm|mp4|mkv|torrent|ram|iso|m3u8|rmvb)/i.test(low)) score += 1; if (/class="[^"]*(item|result|torrent|list|post|article|entry|search-item|movie-item|resource|share|search-list|box-list|video-list)/i.test(low)) score += 1; if (text.length > 15000 && /]*>.*<\/li>/i.test(text)) score += 1; // 下载/播放词汇 +1 if (/(下载|磁力|迅雷|thunder|bt下载|在线观看|播放|watch|play|stream|ed2k|bt种子)/i.test(low)) score += 1; return score; } // ============================================================ // v4.8.0 智能关键词选择 (按站点类型选最优候选, 最多3个) // ============================================================ function pickCandidates(site, movie) { if (site.useImdb) return movie.imdbId ? [movie.imdbId] : []; var base = movie.kwCandidates || []; var list = []; function push(s) { if (s && list.indexOf(s) === -1 && base.indexOf(s) !== -1) list.push(s); } var cat = site._cat || 'online'; if (site.useEn || cat === 'bt_en') { // 磁力/BT国际站:优先英文 push(movie.enTitle); push(movie.enTitle ? (movie.enTitle + (movie.year ? ' ' + movie.year : '')) : ''); push(movie.cnTitle); push(movie.cnTitle ? (movie.cnTitle + (movie.year ? ' ' + movie.year : '')) : ''); } else if (cat === 'dl_zh') { // 中文下载站:中文标题+年份优先 push(movie.cnTitle ? (movie.cnTitle + (movie.year ? ' ' + movie.year : '')) : ''); push(movie.cnTitle); var mp = movie.cnTitle ? movie.cnTitle.split(/[::]/)[0].trim() : ''; if (mp && mp !== movie.cnTitle) push(mp); } else if (cat === 'pan') { // 网盘搜索站:纯中文标题不带年份 push(movie.cnTitle); var mp2 = movie.cnTitle ? movie.cnTitle.split(/[::]/)[0].trim() : ''; if (mp2 && mp2 !== movie.cnTitle) push(mp2); push(movie.cnTitle ? (movie.cnTitle + (movie.year ? ' ' + movie.year : '')) : ''); } else { // 在线影视站 / 默认:中文标题 push(movie.cnTitle); var mp3 = movie.cnTitle ? movie.cnTitle.split(/[::]/)[0].trim() : ''; if (mp3 && mp3 !== movie.cnTitle) push(mp3); push(movie.cnTitle ? (movie.cnTitle + (movie.year ? ' ' + movie.year : '')) : ''); } // 剧集季数拼接补充 if (movie.season && movie.cnTitle) push(movie.cnTitle + ' ' + movie.season); var out = []; for (var i = 0; i < list.length && out.length < 3; i++) { if (list[i]) out.push(list[i]); } return out; } // ============================================================ // v4.8.0 多关键词检测 (无结果自动切换, 最多3个) // ============================================================ function testSite(site, movie, cb) { // v4.7.0: 健康度缓存命中(24h 内不可用)直接跳过 if (isHealthSkipped(site.name)) { cb({accessible:false, time:0, available:false, cachedSkip:true}); return; } var candidates = pickCandidates(site, movie); if (!candidates || candidates.length === 0) { cb({accessible:false, time:999999, available:false, skip:true}); return; } tryKeywords(site, movie, candidates, 0, cb); } function tryKeywords(site, movie, cands, idx, cb) { var kw = cands[idx]; var startTime = Date.now(); var timeout = 8000; function finish(extra) { extra = extra || {}; var r = { accessible:true, time: Date.now() - startTime, available:false }; for (var k in extra) { if (hasOwn(extra, k)) r[k] = extra[k]; } cb(r); } function nextOrFail() { if (idx + 1 < cands.length) { setTimeout(function() { tryKeywords(site, movie, cands, idx + 1, cb); }, 20); } else { setHealth(site.name, false); finish(); } } if (site.method === 'POST') { var body = site.postData(kw); gmXhr({ url:site.url, method:'POST', data:body, timeout:timeout, retries:1, noCache:true, headers:{'Content-Type':'application/x-www-form-urlencoded','Referer':site.url,'Origin':site.url.replace(/(https?:\/\/[^\/]+).*/,'$1')} }, function(err, res) { if (err || !res) { setHealth(site.name, false); finish({accessible:false, time:999999, error:true}); return; } var fu = res.finalUrl || site.url; var available = site.checkUrl ? site.checkUrl(fu) : true; if (!available) { nextOrFail(); return; } setHealth(site.name, true); finish({ available:true }); }); } else { var url = site.url + encodeURIComponent(kw) + (site.suffix || ''); gmXhr({url:url, timeout:timeout, retries:1, noCache:true}, function(err, res) { if (err || !res || !res.responseText) { setHealth(site.name, false); finish({accessible:false, time:999999, error:true}); return; } var text = res.responseText; try { // v4.8.0: 反拦截 if (isBlocked(text)) { finish({ blocked:true }); return; } // v4.8.0: 站点专用解析器优先 var parser = SITE_PARSERS[site.name]; if (parser) { var pr = parser(text); var pAvail = (typeof pr === 'object' && pr !== null && typeof pr.available !== 'undefined') ? pr.available : !!pr; if (pAvail) { setHealth(site.name, true); finish({ available:true }); return; } nextOrFail(); return; } if (!site.detect) { // 非内容解析站(如网盘跳转站):仅做可访问性判定 if (text.length < 500 || (res.status && res.status >= 400)) { nextOrFail(); return; } setHealth(site.name, true); finish({ available:true }); return; } // v4.8.0: 通用加权评分 var score = scoreResult(text, movie); if (score >= 3) { setHealth(site.name, true); finish({ available:true }); return; } nextOrFail(); } catch(e) { logErr('tryKeywords parse error', e, site.name); setHealth(site.name, true); finish({ available:true, error:true }); } }); } } // ============================================================ // v4.7.0 分区渲染 (支持子区域定位);v5.2.0 Tier3 全自动检测 // ============================================================ function renderSectionTiered(opts) { var sitesGroups = opts.sitesGroups; var movie = opts.movie; var sectionId = opts.sectionId; var section = document.getElementById(sectionId); if (!section) return; var totalSites = (sitesGroups[1]||[]).length + (sitesGroups[2]||[]).length + (sitesGroups[3]||[]).length; var totalFound = 0; var totalDone = 0; var allResults = {}; var sectionExpanded = false; var toastShown = false; // v4.7.0: 支持通过 bodyId/arrowId 精确定位子区域(如「更多网盘搜索站」) var sectionBody = opts.bodyId ? document.getElementById(opts.bodyId) : section.querySelector('.ddm-section-body'); var sectionArrow = opts.arrowId ? document.getElementById(opts.arrowId) : section.querySelector('.ddm-section-header .ddm-arrow'); // v5.2.0: 无资源站点检测完成后直接隐藏,仅显示可用站点 function applyFilter() { for (var t in allResults) { if (!hasOwn(allResults, t)) continue; var arr = allResults[t]; for (var i = 0; i < arr.length; i++) { var item = arr[i]; if (item.result && !item.result.available) item.a.classList.add('ddm-filtered'); } } updateTierVisibility(); } // 全无资源的梯队整体隐藏 function updateTierVisibility() { var t; for (t = 1; t <= 3; t++) { var linksCont = document.getElementById(opts.containerPrefix + '-t' + t + '-links'); if (!linksCont) continue; var wrap = linksCont.parentNode; if (!wrap || wrap.className.indexOf('ddm-tier-block') === -1) continue; var tag = wrap.querySelector('.ddm-tier-tag'); if (tag) tag.style.display = ''; var hasPendingCheck = false; var hasAvail = false; var arr = allResults[t] || []; for (var i = 0; i < arr.length; i++) { if (!arr[i].result) { hasPendingCheck = true; break; } if (arr[i].result.available) hasAvail = true; } // 检测中(结果未定)或存在可用站点 → 显示;全部完成且无可用 → 隐藏 wrap.style.display = (hasPendingCheck || hasAvail) ? '' : 'none'; } } function autoExpandSection() { if (sectionExpanded) return; sectionExpanded = true; if (sectionBody) { sectionBody.style.display = 'block'; // v4.9.0: 展开内容区淡入动画 animateBodyIn(sectionBody); } if (sectionArrow) sectionArrow.textContent = '▾'; updateSummaryCount(); } function updateSummaryCount() { var summaryEl = document.getElementById('ddm-summary-' + opts.summaryKey); if (summaryEl) { summaryEl.textContent = totalFound; if (totalFound > 0) summaryEl.className = 'ddm-summary-num has-val'; } } function updateProgress() { if (opts.barEl) opts.barEl.style.width = Math.round((totalDone/totalSites)*100) + '%'; if (opts.textEl) opts.textEl.textContent = totalDone + '/' + totalSites; if (opts.countEl) { opts.countEl.textContent = totalFound + '/' + totalSites; if (totalFound > 0) opts.countEl.className = 'ddm-section-count has-result'; } updateSummaryCount(); } function finalizeSection() { if (opts.barEl) { opts.barEl.style.width = '100%'; opts.barEl.classList.add('done'); } if (opts.textEl) opts.textEl.textContent = totalDone + '/' + totalSites; if (opts.badgeEl) { opts.badgeEl.className = 'ddm-progress-badge ' + (totalFound > 0 ? 'done' : 'done-none'); opts.badgeEl.textContent = totalFound > 0 ? ('✓ ' + totalFound + '可用') : '无资源'; } if (opts.countEl) { opts.countEl.textContent = totalFound + '/' + totalSites; if (totalFound > 0) opts.countEl.className = 'ddm-section-count has-result'; } if (section) { section.classList.add('ddm-section-completed'); if (totalFound > 0) section.classList.add('ddm-section-has-result'); } // v5.0.0: 无结果 → 折叠分区为一行居中次级文案「暂未找到相关资源」 var emptyTag = opts.emptyElId ? document.getElementById(opts.emptyElId) : null; if (totalFound === 0) { if (sectionBody) sectionBody.style.display = 'none'; if (sectionArrow) sectionArrow.textContent = '▸'; if (emptyTag) { emptyTag.style.display = 'inline'; var hdrTitle = section ? section.querySelector('.ddm-section-title') : null; var hdrCount = section ? section.querySelector('.ddm-section-count') : null; if (hdrTitle) hdrTitle.style.display = 'none'; if (hdrCount) hdrCount.style.display = 'none'; } } else { if (emptyTag) emptyTag.style.display = 'none'; } applyFilter(); updateSummaryCount(); // v4.9.0: 完成有结果 → Toast 通知 if (totalFound > 0 && !toastShown && opts.toastMsg) { toastShown = true; showToast(opts.toastMsg + ':找到 ' + totalFound + ' 个可用站点'); } for (var t in allResults) { if (!hasOwn(allResults, t)) continue; var arr = allResults[t]; arr.sort(function(a, b) { var ra = a.result || {}, rb = b.result || {}; if (ra.available && !rb.available) return -1; if (!ra.available && rb.available) return 1; if (ra.accessible && !rb.accessible) return -1; if (!ra.accessible && rb.accessible) return 1; return ra.time - rb.time; }); var cont = document.getElementById(opts.containerPrefix + '-t' + t + '-links'); if (cont) { for (var i = 0; i < arr.length; i++) cont.appendChild(arr[i].a); } } // v5.2.1: 「复制全部可用链接」按钮已移除 } function runTier(tier) { var sites = sitesGroups[tier] || []; var containerId = opts.containerPrefix + '-t' + tier + '-links'; var container = document.getElementById(containerId); if (!container || sites.length === 0) { if (tier < 3) runTier(tier + 1); else finalizeSection(); return; } container.innerHTML = ''; var tierResults = []; allResults[tier] = tierResults; var linkItems = []; for (var i = 0; i < sites.length; i++) { var site = sites[i]; var kw = site.useImdb ? movie.imdbId : (site.useEn ? movie.kwEn : movie.kw); var url = kw ? (site.url + encodeURIComponent(kw) + (site.suffix || '')) : 'javascript:void(0)'; if (site.method === 'POST') url = site.url; var a = document.createElement('a'); a.href = url; a.target = '_blank'; a.rel = 'noopener'; a.textContent = site.name; if (site.desc) a.title = site.desc; else if (!kw || site.method === 'POST') a.title = site.method === 'POST' ? site.name + ' (点击后搜索)' : site.name; a.className = 'checking'; // v4.9.0: 逐个弹入瀑布动画 (30ms 递增延迟) a.style.animation = 'ddm-link-pop .3s cubic-bezier(0.34,1.56,0.64,1) both'; a.style.animationDelay = (i * 30) + 'ms'; // v4.9.0: Tooltip 数据 if (site.desc) a.setAttribute('data-desc', site.desc); a.setAttribute('data-tier', TIER_CONFIG[site.tier || 2].label); (function(link) { link.addEventListener('mouseenter', function(ev) { showTooltipFor(link, ev); }, false); link.addEventListener('mouseleave', hideTooltip, false); link.addEventListener('click', hideTooltip, false); })(a); container.appendChild(a); linkItems.push({ site:site, a:a, kw:kw, result:null }); } // v5.2.0: Tier3 国际站与普通站点同样自动检测,仅按速度顺位最后执行 var _token = container._ddmToken || (container._ddmToken = '_ddm_token_' + Math.random().toString(36).slice(2, 9)); var tierFound = 0; pool(linkItems, 0, function(item, idx, workerDone) { setTimeout(function() { testSite(item.site, movie, function(r) { if (container._ddmToken !== _token) { workerDone(); return; } item.result = r; item.a.textContent = item.site.name; // v4.7.0: 记录响应时间用于动态并发调节 if (r.time > 0) updateConcurrency(r.time); if (r.available) { item.a.className = 'available'; item.a.title = item.site.name + ' ✅ 有资源 (' + r.time + 'ms)'; if (item.site.desc) item.a.title += ' - ' + item.site.desc; tierFound++; totalFound++; autoExpandSection(); if (opts.countEl) { opts.countEl.textContent = totalFound + '/' + totalSites; opts.countEl.className = 'ddm-section-count has-result'; } } else if (r.cachedSkip) { item.a.className = 'skipped'; item.a.title = item.site.name + ' ⏭ 24h内检测不可用,已跳过'; } else if (r.blocked) { item.a.className = 'blocked'; item.a.title = item.site.name + ' 🚧 被拦截 (CF挑战/验证码)'; } else if (r.accessible) { if (r.error) { item.a.className = 'error'; item.a.title = item.site.name + ' ⚠ 解析异常 (' + r.time + 'ms)'; } else { item.a.className = 'unavailable'; item.a.title = item.site.name + ' ❌ 无资源 (' + r.time + 'ms)'; } } else { item.a.className = 'inaccessible'; item.a.title = item.site.name + ' 🚫 无法访问'; } tierResults.push(item); // v4.10.0: 实时过滤无资源站点 + 梯队可见性 applyFilter(); workerDone(); }); }, randomInt(20, 150)); }, function(finished, total) { if (container._ddmToken !== _token) return; var done = 0; for (var t = 1; t <= 3; t++) { if (t < tier) done += (sitesGroups[t]||[]).length; else if (t === tier) done += finished; } totalDone = done; updateProgress(); }, function() { if (container._ddmToken !== _token) return; totalDone = 0; for (var t = 1; t <= tier; t++) totalDone += (sitesGroups[t]||[]).length; updateProgress(); log('tier ' + tier + ' done, found=' + tierFound + ', section=' + sectionId); if (tier < 3) { setTimeout(function() { runTier(tier + 1); }, 80); } else { finalizeSection(); } }); } runTier(1); } // ============================================================ // v5.0.0 构建面板 — 三大类顺序:下载→网盘→在线 // ============================================================ function buildPanel(movie) { log('buildPanel v5.2.1'); var target = document.querySelector('.aside'); if (!target) target = document.body; if (document.getElementById('ddm-panel')) return document.getElementById('ddm-panel'); var dlN = DOWNLOAD_SITES.length; var panN = PAN_SEARCH_SITES.length; var onlineN = ONLINE_SITES.length; var tDl = countTier(DOWNLOAD_SITES); var tPan = countTier(PAN_SEARCH_SITES); var tOnline = countTier(ONLINE_SITES); var t1Total = tPan[1] + tOnline[1] + tDl[1]; var t2Total = tPan[2] + tOnline[2] + tDl[2]; var t3Total = tPan[3] + tOnline[3] + tDl[3]; var totalAll = t1Total + t2Total + t3Total; function tierBlock(prefix, tier, count) { if (count === 0) return ''; return '
' + '' + TIER_CONFIG[tier].label + ' · ' + TIER_CONFIG[tier].desc + '' + '' + '
'; } var panel = document.createElement('div'); panel.id = 'ddm-panel'; panel.className = 'ddm-panel'; panel.innerHTML = '
' + '

⬇ 资源下载大师 v5.2.1

' + '' + '
' + '
' + '
0下载
' + '
0网盘直链
' + '
0网盘搜索
' + '
0在线
' + '
' + // v4.9.0: 实时过滤搜索框 '' + // 1. 下载资源(首位) '
' + '
' + '▸' + '🧲' + '下载资源' + '' + '0/' + dlN + '' + '
' + '
' + '
' + '0/' + dlN + '' + '
' + '' + '
' + // 2. 网盘资源(含API+搜索站) '
' + '
' + '▾' + '☁️' + '网盘资源' + '加载中' + '
' + '
搜索中
' + '
' + '
正在搜索网盘资源...
' + '
' + '
' + '▸' + '🔍' + '更多网盘搜索站' + '' + '0/' + panN + '' + '
' + '
' + '
' + '0/' + panN + '' + '
' + '' + '
' + // 3. 在线观看(末位) '
' + '
' + '▸' + '📺' + '在线观看' + '' + '0/' + onlineN + '' + '
' + '
' + '
' + '0/' + onlineN + '' + '
' + '' + '
' + '
' + '总计 ' + totalAll + ' 站点' + '快 ' + t1Total + '' + '中 ' + t2Total + '' + '慢 ' + t3Total + '' + '
'; try { if (target === document.body) { var fw = document.createElement('div'); fw.id = 'ddm-float-wrap'; fw.style.cssText = 'position:fixed;right:18px;top:78px;width:384px;max-height:86vh;overflow-y:auto;z-index:99998;background:transparent;border-radius:20px;'; fw.appendChild(panel); document.body.appendChild(fw); } else { target.insertBefore(panel, target.firstChild); } log('panel injected v5.2.1'); } catch(e) { logErr('panel inject failed', e); try { document.body.appendChild(panel); } catch(e2) {} } var headers = panel.querySelectorAll('.ddm-section-header[data-sec]'); for (var hi = 0; hi < headers.length; hi++) { headers[hi].addEventListener('click', function(e) { var key = this.getAttribute('data-sec'); var body = document.getElementById('ddm-' + key + '-body'); var arrow = this.querySelector('.ddm-arrow'); if (!body || !arrow) return; var hid = body.style.display === 'none'; body.style.display = hid ? 'block' : 'none'; arrow.textContent = hid ? '▾' : '▸'; // v4.9.0: 展开时内容区淡入动画 if (hid) { animateBodyIn(body); // v5.0.0: 展开后隐藏空结果提示并恢复标题/计数 var et = document.getElementById('ddm-' + key + '-empty'); if (et) et.style.display = 'none'; var ht = this.querySelector('.ddm-section-title'); if (ht) ht.style.display = ''; var hc = this.querySelector('.ddm-section-count'); if (hc) hc.style.display = ''; } }, false); } // v4.9.0: 实时过滤搜索框 var searchInput = document.getElementById('ddm-search'); if (searchInput) { searchInput.addEventListener('input', function() { var q = (this.value || '').trim().toLowerCase(); var links = panel.querySelectorAll('.ddm-links a'); for (var li = 0; li < links.length; li++) { var nm = (links[li].textContent || '').toLowerCase(); var show = !q || nm.indexOf(q) !== -1; if (show) links[li].classList.remove('ddm-search-hide'); else links[li].classList.add('ddm-search-hide'); } }, false); } var reloadBtn = document.getElementById('ddm-reload'); if (reloadBtn) { reloadBtn.addEventListener('click', function(e) { e.preventDefault(); var op = document.getElementById('ddm-panel'); if (op && op.parentNode) { var p=op.parentNode; if(p.id==='ddm-float-wrap')p.parentNode.removeChild(p); else p.removeChild(op); } _xhrCache = {}; _requestLastTime = {}; setTimeout(function(){ initAll(movie); }, 100); }, false); } return panel; } // ============================================================ // v4.7.0 DNS预解析 (Tier1 站点域名, 最多6个) // ============================================================ function addDnsPrefetch() { try { var domains = []; var seen = {}; var lists = [DOWNLOAD_SITES, PAN_SEARCH_SITES, ONLINE_SITES]; for (var i = 0; i < lists.length && domains.length < 6; i++) { var arr = lists[i]; for (var j = 0; j < arr.length && domains.length < 6; j++) { var s = arr[j]; if (s.tier !== 1) continue; var host = (s.url || '').replace(/^https?:\/\//, '').split('/')[0]; if (!host || seen[host]) continue; seen[host] = true; domains.push(host); } } var head = document.head || document.documentElement; if (!head) return; for (var k = 0; k < domains.length; k++) { try { var link = document.createElement('link'); link.rel = 'dns-prefetch'; link.href = '//' + domains[k]; head.appendChild(link); } catch(e) {} } if (domains.length > 0) log('dns-prefetch: ' + domains.join(', ')); } catch(e) { logErr('addDnsPrefetch error', e); } } // ============================================================ // 初始化 // ============================================================ function initAll(movie) { if (window._ddm_initialized) return; if (document.getElementById('ddm-panel')) return; window._ddm_initialized = true; try { addCSS(CSS); addDnsPrefetch(); tagSiteCategories(); var panel = buildPanel(movie); if (!panel) { window._ddm_initialized = false; return; } // 1. 下载资源(首位) var dlGroups = groupByTier(DOWNLOAD_SITES); renderSectionTiered({ sitesGroups: dlGroups, movie: movie, sectionId: 'ddm-sec-dl', containerPrefix: 'ddm-dl', summaryKey: 'dl', toastMsg: '🧲 下载资源', emptyElId: 'ddm-dl-empty', barEl: document.getElementById('ddm-dl-bar'), textEl: document.getElementById('ddm-dl-prog'), badgeEl: null, countEl: document.getElementById('ddm-dl-cnt') }); // 2. 网盘API资源 var panBody = document.getElementById('ddm-pan-body'); var panBadge = document.getElementById('ddm-pan-badge'); var panCnt = document.getElementById('ddm-pan-cnt'); var panSec = document.getElementById('ddm-sec-pan'); if (panBody) { searchPanApi(movie.kw, function(items) { if (!panBody) return; if (items && items.length > 0) { var html = '
'; for (var i = 0; i < items.length; i++) { var it = items[i]; html += ''; html += '' + esc(it.tagLabel) + ''; html += '' + esc(it.name) + ''; if (it.pwd) html += '🔑' + esc(it.pwd) + ''; html += ''; } html += '
'; panBody.innerHTML = html; if (panCnt) { panCnt.textContent = items.length + '条'; panCnt.className = 'ddm-section-count has-result'; } if (panBadge) { panBadge.className='ddm-progress-badge done'; panBadge.textContent='✓ 找到' + items.length + '条'; } if (panSec) { panSec.classList.add('ddm-section-completed'); panSec.classList.add('ddm-section-has-result'); } // v4.9.0: 网盘直链 Toast showToast('☁️ 网盘直链:找到 ' + items.length + ' 条资源'); var summaryEl = document.getElementById('ddm-summary-pan'); if (summaryEl) { summaryEl.textContent = items.length; summaryEl.className = 'ddm-summary-num has-val'; } var pwds = panBody.querySelectorAll('.ddm-pan-pwd'); for (var p = 0; p < pwds.length; p++) { pwds[p].addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); var pwd = this.getAttribute('data-pwd'); var ok = copyText(pwd); var old = this.innerHTML; var self = this; this.className = 'ddm-pan-pwd copied'; this.innerHTML = ok ? '✅已复制' : '失败'; setTimeout(function(){ self.className='ddm-pan-pwd'; self.innerHTML = old; }, 1500); }, false); } } else { panBody.innerHTML = '
未找到网盘资源,可查看下方搜索站
'; if (panBadge) { panBadge.className='ddm-progress-badge done-none'; panBadge.textContent='无结果'; } if (panSec) panSec.classList.add('ddm-section-completed'); if (panCnt) panCnt.textContent = '0条'; } }); } // 2.1 网盘搜索站 var panSearchGroups = groupByTier(PAN_SEARCH_SITES); renderSectionTiered({ sitesGroups: panSearchGroups, movie: movie, sectionId: 'ddm-sec-pan', containerPrefix: 'ddm-pansearch', bodyId: 'ddm-pansearch-body', arrowId: 'ddm-pansearch-arrow', summaryKey: 'pansearch', toastMsg: '☁️ 网盘搜索', emptyElId: 'ddm-pansearch-empty', barEl: document.getElementById('ddm-pansearch-bar'), textEl: document.getElementById('ddm-pansearch-prog'), badgeEl: null, countEl: document.getElementById('ddm-pansearch-cnt') }); // 3. 在线观看 var onlineGroups = groupByTier(ONLINE_SITES); renderSectionTiered({ sitesGroups: onlineGroups, movie: movie, sectionId: 'ddm-sec-online', containerPrefix: 'ddm-online', summaryKey: 'online', toastMsg: '📺 在线观看', emptyElId: 'ddm-online-empty', barEl: document.getElementById('ddm-online-bar'), textEl: document.getElementById('ddm-online-prog'), badgeEl: null, countEl: document.getElementById('ddm-online-cnt') }); // IMDb评分注入 if (movie.imdbId) { try { gmXhr({url:'https://www.imdb.com/title/'+movie.imdbId+'/',timeout:12000,retries:2,headers:{'Accept-Language':'en-US,en;q=0.9','Referer':'https://www.google.com/'}}, function(err, res) { if (err || !res || !res.responseText) return; try { var m = res.responseText.match(/"ratingValue"\s*:\s*"?(\d+\.?\d*)"?/); if (m) { var rating = m[1]; var sectl = document.querySelector('#interest_sectl'); if (sectl && !document.getElementById('ddm-imdb')) { var wrap = document.createElement('div'); wrap.id = 'ddm-imdb'; wrap.className = 'rating_wrap ddm-imdb'; wrap.innerHTML = '
' + esc(rating) + '星
查看IMDb页面'; try { sectl.appendChild(wrap); } catch(e){} } } } catch(e) { logErr('imdb parse error', e); } }); } catch(e) {} } var bm = document.getElementById('ddm-boot-mark'); if (bm && bm.parentNode) setTimeout(function(){ if(bm.parentNode)bm.parentNode.removeChild(bm); }, 800); log('initAll done v5.2.1'); } catch(e) { logErr('initAll error', e); window._ddm_initialized = false; } } // ============================================================ // 主入口 // ============================================================ function main() { log('main() v5.2.1'); try { var movie = getMovieInfo(); if (!movie.cnTitle) { var bm = document.getElementById('ddm-boot-mark'); if(bm)bm.innerHTML='DDM - 非详情页?'; return; } initAll(movie); } catch(e) { logErr('main error', e); } } function startWhenReady() { if (document.body && document.querySelector('h1')) { if (document.querySelector('.aside') || document.readyState === 'complete') { setTimeout(main, 300); } else { setTimeout(startWhenReady, 200); } } else { setTimeout(startWhenReady, 200); } } try { if (typeof GM_registerMenuCommand !== 'undefined') { GM_registerMenuCommand('🔄 重新加载面板', function() { var p = document.getElementById('ddm-panel'); if (p && p.parentNode) { var pp=p.parentNode; if(pp.id==='ddm-float-wrap')pp.parentNode.removeChild(pp); else pp.removeChild(p); } _xhrCache={}; _requestLastTime={}; window._ddm_initialized=false; main(); }); GM_registerMenuCommand('🧹 清空缓存', function() { _xhrCache={}; _requestLastTime={}; try { if (typeof GM_listValues !== 'undefined') { var ks=GM_listValues(); for (var i=0;i