// ==UserScript== // @name 常看VIP视频全网解析 (Beta版) // @namespace https://scriptcat.org/zh-CN/script-show-page/6457 // @version 2.3.6 // @description 💎VIP视频解析 | 🎬爱奇艺解析 | 全面支持移动端 | 🔍腾讯VIP去广 | 📺优酷独播解锁 | ⚡芒果TV大会员 | 🎥B站番剧解析 | 🔗搜狐视频直连 | ✦乐视超清播放 | ◆PPTV聚力解析 | ●咪咕体育直播 | ★西瓜VIP解锁 | 🔍抖音短剧解析 | 🎬快手短视频解析 | 📺1905电影网 | ⚡AcFun弹幕解析 | 🔗土豆/风行/暴风兼容 | ✦全网影视通解 | ◆4K蓝光画质 | ●HDR杜比音效 | ★智能线路优选 | 💎免登录免费看 | 🎞️热播剧/最新电影/独家综艺/国漫日番 | ⚡极速评估 | 🔄智能缓存 | 🚀DNS预解析 | 🚀24并发加速 | 全面支持移动端 // @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI1MCIgZmlsbD0idXJsKCNncmFkKSIvPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iZ3JhZCIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzAwMzM5OSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzAwNjZjYyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjx0ZXh0IHg9IjUwIiB5PSI3MCIgZm9udC1zaXplPSI1MCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0id2hpdGUiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXdlaWdodD0ibjkwMCI+VklQPC90ZXh0Pjwvc3ZnPg== // @author lsym // @noframes // @match *://*/* // @match *://m.*/* // @match *://*.m.*/* // @match *://*.youku.com/* // @match *://*.iqiyi.com/* // @match *://*.qq.com/* // @match *://*.bilibili.com/* // @match *://*.mgtv.com/* // @match *://*.le.com/* // @match *://*.sohu.com/* // @match *://*.pptv.com/* // @match *://*.acfun.cn/* // @match *://*.ixigua.com/* // @match *://*.douyin.com/* // @match *://*.kuaishou.com/* // @grant GM_addStyle // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @run-at document-end // @connect 360zy.com // @connect jsdelivr.com // @connect api.1080zyku.com // @connect api.apibdzy.com // @connect api.ffzyapi.com // @connect api.guangsuapi.com // @connect api.maoyanapi.top // @connect api.niuniuzy.me // @connect api.okzyw.net // @connect api.tiankongapi.com // @connect api.ukuapi.com // @connect api.ukuapi88.com // @connect api.wujinapi.me // @connect api.xinlangapi.com // @connect api.yparse.com // @connect api.yzzy-api.com // @connect api.zuidapi.com // @connect caiji.dyttzyapi.com // @connect caiji.kczyapi.com // @connect caiji.kuaichezy.org // @connect caiji.moduapi.cc // @connect cj.ffzyapi.com // @connect cj.lziapi.com // @connect cj.rycjapi.com // @connect cj.vodimg.top // @connect cj.yayazy.net // @connect ckzy.me // @connect ddmf.net // @connect hhzyapi.com // @connect ikunzyapi.com // @connect iqiyizyapi.com // @connect jszyapi.com // @connect jyzyapi.com // @connect lz.118318.xyz // @connect m3u8.apiyhzy.com // @connect p2100.net // @connect savviuux.hk3.345888.xyz.cdn.cloudflare.net // @connect sdzyapi.com // @connect slapibf.com // @connect subocaiji.com // @connect suboziyuan.net // @connect suoniapi.com // @connect taopianapi.com // @connect tyyszyapi.com // @connect www.39kan.com // @connect www.hongniuzy2.com // @connect www.huyaapi.com // @connect www.lovedan.net // @connect www.lzzy.tv // @connect www.mdzyapi.com // @connect www.qilinzyz.com // @connect www.ryzyw.com // @connect www.seacms.org // @connect www.wyvod.com // @connect xkanzy.com // @connect xsd.sdzyapi.com // @connect fastly.jsdelivr.net // @connect cdn.jsdelivr.net // @connect unpkg.com // @connect cdnjs.cloudflare.com // @connect doh.pub // @connect dns.alidns.com // @connect doh.360.cn // @connect doh.baidu.com // @connect cloudflare-dns.com // @connect dns.google // @connect dns.quad9.net // @connect * // @antifeature piracy // @license MIT // ==/UserScript== (function () { if (window.hasInitVipScript) return; window.hasInitVipScript = true; 'use strict'; // ===== 内嵌 GM_xmlhttpRequest 并行请求补丁 ===== const HAS_GM = typeof GM !== 'undefined'; const NEW_GM = ((scope, GM) => { if (typeof GM_info === 'undefined' || GM_info.scriptHandler !== "Tampermonkey") return; try { if (compareVersions(GM_info.version, "5.3.2") < 0) return; } catch(e) { return; } const GM_xmlhttpRequestOrig = GM_xmlhttpRequest; const GM_xmlHttpRequestOrig = GM?.xmlHttpRequest; function compareVersions(v1, v2) { const parts1 = v1.split('.').map(Number); const parts2 = v2.split('.').map(Number); const length = Math.max(parts1.length, parts2.length); for (let i = 0; i < length; i++) { const num1 = parts1[i] || 0; const num2 = parts2[i] || 0; if (num1 > num2) return 1; if (num1 < num2) return -1; } return 0; } function GM_xmlhttpRequestWrapper(odetails) { if (odetails.redirect !== undefined) { return GM_xmlhttpRequestOrig(odetails); } const { onload, onloadend, onerror, onabort, ontimeout, ...details } = odetails; const handleRedirects = (initialDetails) => { return GM_xmlhttpRequestOrig({ ...initialDetails, redirect: 'manual', onload: function(response) { if (response.status >= 300 && response.status < 400) { const m = response.responseHeaders.match(/Location:\s*(\S+)/i); const redirectUrl = m && m[1]; if (redirectUrl) { try { const absoluteUrl = new URL(redirectUrl, initialDetails.url).href; handleRedirects({ ...initialDetails, url: absoluteUrl }); return; } catch(e) {} } } if (onload) onload.call(this, response); if (onloadend) onloadend.call(this, response); }, onerror: function(response) { if (onerror) onerror.call(this, response); if (onloadend) onloadend.call(this, response); }, onabort: function(response) { if (onabort) onabort.call(this, response); if (onloadend) onloadend.call(this, response); }, ontimeout: function(response) { if (ontimeout) ontimeout.call(this, response); if (onloadend) onloadend.call(this, response); } }); }; return handleRedirects(details); } function GM_xmlHttpRequestWrapper(odetails) { let abort; const p = new Promise((resolve, reject) => { const { onload, ontimeout, onerror, ...send } = odetails; send.onerror = function(r) { if (onerror) { resolve(r); onerror.call(this, r); } else reject(r); }; send.ontimeout = function(r) { if (ontimeout) { resolve(r); ontimeout.call(this, r); } else reject(r); }; send.onload = function(r) { resolve(r); if (onload) onload.call(this, r); }; const a = GM_xmlhttpRequestWrapper(send).abort; if (abort === true) a(); else abort = a; }); p.abort = () => { if (typeof abort === 'function') abort(); else abort = true; }; return p; } GM_xmlhttpRequest = GM_xmlhttpRequestWrapper; scope.GM_xmlhttpRequestOrig = GM_xmlhttpRequestOrig; if (GM?.xmlHttpRequest) { const gopd = Object.getOwnPropertyDescriptor(GM, 'xmlHttpRequest'); if (gopd && gopd.configurable === false) { return { __proto__: GM, xmlHttpRequest: GM_xmlHttpRequestWrapper, xmlHttpRequestOrig: GM_xmlHttpRequestOrig }; } else { GM.xmlHttpRequest = GM_xmlHttpRequestWrapper; GM.xmlHttpRequestOrig = GM_xmlHttpRequestOrig; } } return null; })(typeof window !== 'undefined' ? window : globalThis, HAS_GM ? GM : {}); if (HAS_GM && NEW_GM) GM = NEW_GM; // 轻量优化版 DNS_OPT:只注入 hint 提示,停止耗费 CPU 的大批量 HEAD/DoH 扫描 const DNS_OPT = { _domainRank: new Map(), getLatency(domain) { const r = this._domainRank.get(domain); return r ? r.latency : Infinity; }, injectHints(domainList) { try { const frag = document.createDocumentFragment(); const dnsSelf = ['fastly.jsdelivr.net', 'cdn.jsdelivr.net', 'unpkg.com', 'cdnjs.cloudflare.com']; [...new Set([...dnsSelf, ...domainList])].forEach(d => { const link = document.createElement('link'); link.rel = 'dns-prefetch'; link.href = '//' + d; frag.appendChild(link); }); if (document.head) document.head.appendChild(frag); } catch(e) {} }, boot(domainList) { this.injectHints(domainList); } }; const CONFIG = { API_TIMEOUT: 3500, // 适当放宽以应对代理延迟 STUCK_CHECK_TIMEOUT: 7000, SEARCH_CONCURRENCY: 16, FAST_EVAL_TIMEOUT: 700, SMART_SORTING: true, AUTOPLAY_NEXT_DELAY: 200, PANEL_LEAVE_CLOSE_DELAY: 1500, SPA_DEBOUNCE: 400, STORAGE_KEY_ICON_POSITION: 'vip_icon_pos_v8', VIDEO_URL_PATTERNS: [ // === 爱奇艺 === /iqiyi\.com\/[vwa]_/, /iqiyi\.com\/play\//, /m\.iqiyi\.com\/(v|a|play|video)\//, /iq\.com\/play\//, /m\.iq\.com\/play\//, // === 优酷 === /youku\.com\/v_show\/id_/, /v\.youku\.com\/v_show\/id_/, /m\.youku\.com\/(v_show|video|play)\//, /youku\.com\/video\//, /youku\.com\/play\//, // === 腾讯视频 === /v\.qq\.com\/(x\/cover|x\/page|tv|play)\//, /m\.v\.qq\.com\/(x\/cover|x\/page|tv|play)\//, /v\.qq\.com\/cover\//, /v\.qq\.com\/page\//, /m\.v\.qq\.com\/cover\//, /m\.v\.qq\.com\/page\//, // === 芒果TV === /mgtv\.com\/(b|s|video)\//, /m\.mgtv\.com\/(b|s|video)\//, // === B站 === /bilibili\.com\/(video|bangumi\/play|anime|play)\//, /m\.bilibili\.com\/(video|bangumi\/play|anime|play)\//, /b23\.tv\//, /m\.b23\.tv\//, // === 乐视 === /le\.com\/ptv\/vplay\//, /m\.le\.com\/ptv\/vplay\//, /le\.com\/(play|video)\//, /m\.le\.com\/(play|video)\//, // === 搜狐视频 === /tv\.sohu\.com\/v\//, /film\.sohu\.com\/album\//, /m\.sohu\.com\/(v|tv|film)\//, /sohu\.com\/play\//, // === PPTV === /pptv\.com\/show\//, /m\.pptv\.com\/show\//, /pptv\.com\/play\//, /m\.pptv\.com\/play\//, // === AcFun === /acfun\.cn\/v\/ac/, /m\.acfun\.cn\/v\/ac/, /acfun\.cn\/play\//, /m\.acfun\.cn\/play\//, // === 1905电影网 === /1905\.com\/play\//, /m\.1905\.com\/play\//, /1905\.com\/video\//, /m\.1905\.com\/video\//, // === 西瓜视频 === /ixigua\.com\/(video|play)\//, /m\.ixigua\.com\/(video|play)\//, // === 抖音 === /douyin\.com\/video\//, /m\.douyin\.com\/video\//, /douyin\.com\/discover\//, /m\.douyin\.com\/discover\//, // === 快手 === /kuaishou\.com\/(short-video|video|play)\//, /m\.kuaishou\.com\/(short-video|video|play)\//, // === 土豆 === /tudou\.com\/(listplay|albumplay|programs\/view|video)\//, /m\.tudou\.com\/(listplay|albumplay|programs\/view|video)\//, // === 风行/暴风 === /fun\.tv\/(vod-play|player|play)\//, /funshion\.com\/(play|player)\//, /m\.fun\.tv\/(vod-play|player|play)\//, /baofeng\.com\/(play\/|play-|player\/)/, /bfeng\.cn\//, /m\.baofeng\.com\/(play\/|play-|player\/)/, // === 咪咕视频 === /miguvideo\.com\/(detail|play|v|n|p)\//, /m\.miguvideo\.com\/(detail|play|v|n|p)\//, /cmvideo\.cn\/(detail|play|v|n|p)\//, /m\.cmvideo\.cn\/(detail|play|v|n|p)\//, /migumovie\.hcs\.cmvideo\.cn\/movie/, // === 其他 === /stormsfy\.com\//, /hanju\.koudaibaobao\.com\//, /maiduidui\.com\/play\//, /rrsp\.tv\/play\//, /vas\.hiaiabc\.com\/play\// ], MESSAGES: { VIDEO_ENDED: 'tm_video_ended', PLAY_SUCCESS: 'tm_play_success', PLAY_ERROR: 'tm_play_error', STREAM_ALIVE: 'tm_stream_alive' }, SELECTORS: { PLAYER_ELEMENTS: [ // 腾讯视频 '#tenvideo_player', '.txp_player_root', '#player-container', '#player', '[class*="txp_player"]', '[class*="mod_player"]', '.site-player', // 哔哩哔哩 '#bilibili-player', '.bpx-player-container', '[class*="player-wrap"]', // 优酷 '.container-player', '#youku-player', '[class*="youku-player"]', // 爱奇艺 '.iqp-player', '#iqp-player', '[class*="iqiyi-player"]', // 芒果TV '#mgtv-player-wrap', '[class*="mgtv-player"]', // 其他 '#sohuplayer', '#flashbox', '#le_player', '#player_swf', '#pp-player', '#ACPlayer', '#video-player', '#xigua-player', '.video-area', '.player-container', '.artplayer-app', '#bf-player', '.bf-player', '#baofeng-player', '.bf-video-player', '#fun-player', '.fun-player', '#FunPlayer', '#h5player', // 移动端通用选择器 '[class*="video-player"]', '[class*="videoPlayer"]', '[class*="play-container"]', '[class*="playContainer"]', '[id*="player"]', '[class*="player-box"]' ], QUICK_TITLE: ['meta[property="og:title"]', 'h1', '.video-title', '.title', '.vod_title', '.video-info-title'], PRECISE_TITLE: { 'iqiyi.com': '[class*="episodes_playingItem"] [class*="episodes_order"], .qy-episode-item[class*="is-active"] a, .album-list .is-active .title-content, [class*="selected"] .qy-episode-num, #text[style*="IQYHT-Bold"]', 'youku.com': '.box-anthology-item.active, .anthology-wrap li.active span, .anthology-item.current, .play-panel-item.active', 'v.qq.com': '.episode-item--select, .playlist-item--current, [class*="selected"] [class*="episode-item-text"], [class*="episode-item"][class*="selected"] .episode-item-text, [class*="episode-item"][class*="current"] .episode-item-text, [class*="episode-item"][class*="active"] .episode-item-text, [class*="numberListItem_select"] [class*="numberListItem_title"], [data-v-db0ab5fa].episode-item-text', 'bilibili.com': '[class*="EpisodeVirtualList_numberTitle"], [class*="numberListItem_select"] [class*="numberListItem_title"], .ep-list-item.on .ep-item-title, [class*="episode_list"] [class*="selected"]', 'mgtv.com': '[class*="mgtv-player-aside-number-selector__number"], .episode-list .current a, .episode-series .current', 'sohu.com': 'li.pane-item.vip.on a, li.pane-item.on a, .player-album-list .on a', 'le.com': '.js-episode-item.on', 'pptv.com': '.episode-list .current', 'acfun.cn': '.active .title-wenzi', 'miguvideo.com': '[data-v-50548e8b].on span[data-v-50548e8b], [data-v-50548e8b].on, [class*="episodeTitle"][class*="on"]', 'baofeng.com': '.media-title, .player-album .on, .play-list .on, .episode-list .current, [class*="episode"][class*="active"], [class*="episode"][class*="on"]' }, PRECISE_MAIN_TITLE: { 'qq.com': '.intro-title[title], .video-title[title], .player-title, [class*="video-title"], [class*="videoTitle"], [class*="mod_title"], h1[class*="title"]', 'v.qq.com': '[class*="video-title"], [class*="videoTitle"], [class*="mod_title"], .player-title, h1[class*="title"], [class*="site-title"]', 'iqiyi.com': '[data-ai-entity="视频名称、主标题"], [data-ai-entity*="主标题"], [data-ai-entity*="视频名称"], [class*="meta_title"], [class*="meta_titleNotCloud"], [class*="meta_titleNewLabel"], [class*="episodeTitle"], .album-head-title', 'iq.com': '[data-ai-entity="视频名称、主标题"], [data-ai-entity*="主标题"], [data-ai-entity*="视频名称"], [class*="meta_title"]', 'youku.com': '[data-spm-anchor-id*="introduction"] .title, .title[style*="max-width"], .video-title, a[data-pb-txid="pg_playlist_title"][title]', 'bilibili.com': '[class*="mediaTitle"][title], [class*="mediaTitle"], .media-info-title-t', 'b23.tv': '[class*="mediaTitle"][title], [class*="mediaTitle"]', 'sohu.com': 'a[data-pb-txid="pg_playlist_title"][title]', 'mgtv.com': 'h2[class*="mgtv-player-aside-info__title"][title]', 'miguvideo.com': '[data-v-50548e8b][title].episodeTitle', 'le.com': '.j_jujiName, .juji_bar, h1.title, .detail-title, .video-title, [class*="movieName"], [class*="videoName"]', 'baofeng.com': '.media-title, .video-info h1, .player-title, .detail-title, .movie-title, h1.title, [class*="videoTitle"], [class*="video-title"]' }, // 移动端专用标题选择器 MOBILE_TITLE_SELECTORS: { 'qq.com': '[class*="video-title"], [class*="videoTitle"], [class*="mod_title"], h1, .title', 'v.qq.com': '[class*="video-title"], [class*="videoTitle"], [class*="mod_title"], h1, .title', 'iqiyi.com': '[class*="title"], h1, .album-title, .video-title', 'youku.com': '[class*="title"], h1, .video-title, .show-title', 'bilibili.com': '[class*="title"], h1, .media-title, .video-title', 'mgtv.com': '[class*="title"], h1, .video-title', 'mgtv.com': '[class*="title"], h1, .video-title', 'sohu.com': '[class*="title"], h1, .video-title', 'le.com': '[class*="title"], h1, .video-title', 'douyin.com': '[class*="title"], h1, .video-title', 'kuaishou.com': '[class*="title"], h1, .video-title' } }, MOVIE_KEYWORDS: /^(HD|超清|高清|正片|国语|HD国语|720P|1080P|蓝光|4K|BD|TC|TS|DVD|抢先|高清版|HD高清|国语高清|HD中字)$/i, MOVIE_PRIORITY: ['蓝光', '4K', '1080P', '超清', 'HD国语', 'HD', '国语', '高清', '720P', 'BD', '正片', 'HD高清', '国语高清', 'HD中字', 'TC', 'TS', 'DVD', '抢先', '高清版'] }; const _MEDIA_EXT_RE = /\.m3u8|\.mp4|\.flv/i; const _TITLE_SPLIT_RE = /[-_\s((]/; const _EP_REMOVE_RE = /第.+[集季部]/; const _NUM_ONLY_RE = /^\d+$/; const _EP_PATTERN_RE = /(?:第|EP|Ep|ep|E)\s*(\d+)(?:集|话|期|部|季|章)?/i; const _EP_PREFIX_RE = /^(\d+)\s*(?:集|话|期|部|季|章)/; const _EP_SUFFIX_RE = /(\d+)\s*(?:集|话|期|部|季|章)(?:\D|$)/; const _EP_EP_RE = /(?:EP|Ep|ep)(\d+)/i; const _EP_SEASON_RE = /第\s*\d+\s*[季部]\s*第\s*(\d+)\s*[集话]/; const _EP_FALLBACK_RE = /(?:\D|^)(\d{1,4})(?:\D|$)/; const _CACHE_VERSION = 'v6'; const _CACHE_EXPIRY = 3600000; const SearchCache = { get(key) { try { const c = JSON.parse(GM_getValue('cache_' + _CACHE_VERSION + '_' + key, 'null')); if (c && Date.now() - c.ts < _CACHE_EXPIRY) return c.data; } catch (e) {} return null; }, set(key, data) { try { GM_setValue('cache_' + _CACHE_VERSION + '_' + key, JSON.stringify({ data: data, ts: Date.now() })); } catch (e) {} } }; const LibCache = { _hls: null, _art: null, _hlsPromise: null, _artPromise: null, _hlsUrls: [ 'https://fastly.jsdelivr.net/npm/hls.js@1.6.11/dist/hls.min.js', 'https://cdn.jsdelivr.net/npm/hls.js@1.6.11/dist/hls.min.js', 'https://unpkg.com/hls.js@1.6.11/dist/hls.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/hls.js/1.6.11/hls.min.js' ], _artUrls: [ 'https://fastly.jsdelivr.net/npm/artplayer@5.4.0/dist/artplayer.min.js', 'https://cdn.jsdelivr.net/npm/artplayer@5.4.0/dist/artplayer.min.js', 'https://unpkg.com/artplayer@5.4.0/dist/artplayer.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/artplayer/5.4.0/artplayer.min.js' ], _fetchSingle(url, timeout = 4000) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: url, timeout: timeout, onload(r) { if (r.status >= 200 && r.status < 400 && r.responseText) { resolve(r.responseText); } else { reject(new Error('HTTP ' + r.status)); } }, onerror() { reject(new Error('Network error')); }, ontimeout() { reject(new Error('Timeout')); } }); }); }, _fetchWithFallback(urls) { return new Promise((resolve) => { let index = 0; const tryNext = () => { if (index >= urls.length) { resolve(null); return; } const url = urls[index]; this._fetchSingle(url, 4000) .then(code => resolve(code)) .catch(() => { index++; tryNext(); }); }; tryNext(); }); }, preload() { if (!this._hlsPromise) { this._hlsPromise = this._fetchWithFallback(this._hlsUrls).then(code => { this._hls = code; return code; }); } if (!this._artPromise) { this._artPromise = this._fetchWithFallback(this._artUrls).then(code => { this._art = code; return code; }); } }, async getHls() { if (this._hls) return this._hls; if (this._hlsPromise) return await this._hlsPromise; this._hlsPromise = this._fetchWithFallback(this._hlsUrls).then(code => { this._hls = code; return code; }); return await this._hlsPromise; }, async getArt() { if (this._art) return this._art; if (this._artPromise) return await this._artPromise; this._artPromise = this._fetchWithFallback(this._artUrls).then(code => { this._art = code; return code; }); return await this._artPromise; } }; LibCache.preload(); const ApiStats = { _p: {}, _t: null, _priority: new Set(['ikun', '红牛', '360', '豪华', '非凡', '速播2', 'U酷', '1080', '天堂', '猫眼', '魔都']), _flush() { const p = this._p; this._p = {}; this._t = null; for (const [k, v] of Object.entries(p)) GM_setValue(`api_stats_${k}`, v); }, _schedule() { if (!this._t) this._t = setTimeout(() => this._flush(), 2000); }, get(n) { return this._p[n] || GM_getValue(`api_stats_${n}`) || { s: 0, f: 0, l: 0, r: 0 }; }, ok(n, lat) { const s = this.get(n); s.s++; s.l += lat; s.r++; this._p[n] = s; this._schedule(); }, fail(n) { const s = this.get(n); s.f++; s.r++; this._p[n] = s; this._schedule(); }, score(s, name) { if (name && this._priority.has(name)) return 99999; if (s.r < 3) return 1000; const sr = s.s / s.r; if (sr < 0.5) return -1000; return sr * 10000 - (s.s ? s.l / s.s : CONFIG.API_TIMEOUT); } }; const RAW_APIS = [ { n: "ikun", u: atob("aHR0cHM6Ly9pa3VuenlhcGkuY29tL2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "红牛", u: atob("aHR0cHM6Ly93d3cuaG9uZ25pdXp5Mi5jb20vYXBpLnBocC9wcm92aWRlL3ZvZC8=") }, { n: "360", u: atob("aHR0cHM6Ly8zNjB6eS5jb20vYXBpLnBocC9wcm92aWRlL3ZvZC8=") }, { n: "豪华", u: atob("aHR0cHM6Ly9oaHp5YXBpLmNvbS9hcGkucGhwL3Byb3ZpZGUvdm9kLw==") }, { n: "非凡", u: atob("aHR0cHM6Ly9hcGkuZmZ6eWFwaS5jb20vYXBpLnBocC9wcm92aWRlL3ZvZC8=") }, { n: "速播2", u: atob("aHR0cDovL3N1Ym96aXl1YW4ubmV0L2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "U酷", u: atob("aHR0cHM6Ly9hcGkudWt1YXBpLmNvbS9hcGkucGhwL3Byb3ZpZGUvdm9kLw==") }, { n: "1080", u: atob("aHR0cHM6Ly9hcGkuMTA4MHp5a3UuY29tL2luYy9hcGlqc29uLnBocC9wcm92aWRlL3ZvZC8=") }, { n: "iqiyi", u: atob("aHR0cHM6Ly9pcWl5aXp5YXBpLmNvbS9hcGkucGhwL3Byb3ZpZGUvdm9kLw==") }, { n: "百度", u: atob("aHR0cDovL2FwaS5hcGliZHp5LmNvbS9hcGkucGhwL3Byb3ZpZGUvdm9kLw==") }, { n: "艾旦", u: atob("aHR0cDovL3d3dy5sb3ZlZGFuLm5ldC9hcGkucGhwL3Byb3ZpZGUvdm9kLw==") }, { n: "蛋蛋", u: atob("aHR0cDovL2RkbWYubmV0L2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "新浪", u: atob("aHR0cHM6Ly9hcGkueGlubGFuZ2FwaS5jb20veGlubGFuZ2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "享看", u: atob("aHR0cHM6Ly94a2FuenkuY29tL2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "魔都", u: atob("aHR0cHM6Ly9jYWlqaS5tb2R1YXBpLmNjL2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "天堂2", u: atob("aHR0cDovL2NhaWppLmR5dHR6eWFwaS5jb20vYXBpLnBocC9wcm92aWRlL3ZvZC8=") }, { n: "快龙", u: atob("aHR0cHM6Ly9sei4xMTgzMTgueHl6L2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "猫眼", u: atob("aHR0cHM6Ly9hcGkubWFveWFuYXBpLnRvcC9hcGkucGhwL3Byb3ZpZGUvdm9kLw==") }, { n: "花旗", u: atob("aHR0cHM6Ly93d3cuc2VhY21zLm9yZy9hcGkucGhwL3Byb3ZpZGUvdm9kLw==") }, { n: "量子", u: atob("aHR0cHM6Ly9jai5semlhcGkuY29tL2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "量子2", u: atob("aHR0cDovL3d3dy5senp5LnR2L2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "索尼", u: atob("aHR0cHM6Ly9zdW9uaWFwaS5jb20vYXBpLnBocC9wcm92aWRlL3ZvZC8=") }, { n: "快车", u: atob("aHR0cHM6Ly9jYWlqaS5rdWFpY2hlenkub3JnL2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "最大", u: atob("aHR0cHM6Ly9hcGkuenVpZGFwaS5jb20vYXBpLnBocC9wcm92aWRlL3ZvZC8=") }, { n: "牛牛", u: atob("aHR0cHM6Ly9hcGkubml1bml1enkubWUvYXBpLnBocC9wcm92aWRlL3ZvZC8=") }, { n: "OK", u: atob("aHR0cHM6Ly9hcGkub2t6eXcubmV0L2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "闪电3", u: atob("aHR0cHM6Ly94c2Quc2R6eWFwaS5jb20vYXBpLnBocC9wcm92aWRlL3ZvZC8=") }, { n: "闪电", u: atob("aHR0cDovL3NkenlhcGkuY29tL2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "鸭鸭", u: atob("aHR0cHM6Ly9jai55YXlhenkubmV0L2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "无尽", u: atob("aHR0cHM6Ly9hcGkud3VqaW5hcGkubWUvYXBpLnBocC9wcm92aWRlL3ZvZC8=") }, { n: "光速", u: atob("aHR0cHM6Ly9hcGkuZ3VhbmdzdWFwaS5jb20vYXBpLnBocC9wcm92aWRlL3ZvZC8=") }, { n: "速播", u: atob("aHR0cHM6Ly9zdWJvY2FpamkuY29tL2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "金鹰", u: atob("aHR0cHM6Ly9qeXp5YXBpLmNvbS9hcGkucGhwL3Byb3ZpZGUvdm9kLw==") }, { n: "虎牙", u: atob("aHR0cHM6Ly93d3cuaHV5YWFwaS5jb20vYXBpLnBocC9wcm92aWRlL3ZvZC8=") }, { n: "极速", u: atob("aHR0cHM6Ly9qc3p5YXBpLmNvbS9hcGkucGhwL3Byb3ZpZGUvdm9kLw==") }, { n: "樱花", u: atob("aHR0cHM6Ly9tM3U4LmFwaXloenkuY29tL2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "非凡2", u: atob("aHR0cDovL2NqLmZmenlhcGkuY29tL2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "飘零2", u: atob("aHR0cHM6Ly9wMjEwMC5uZXQvYXBpLnBocC9wcm92aWRlL3ZvZC8=") }, { n: "一零", u: atob("aHR0cDovL2FwaS4xMDgwenlrdS5jb20vaW5jL2FwaV9tYWMxMC5waHAvcHJvdmlkZS92b2Qv") }, { n: "优质", u: atob("aHR0cHM6Ly9hcGkueXp6eS1hcGkuY29tL2luYy9hcGlqc29uLnBocC9wcm92aWRlL3ZvZC8=") }, { n: "魔都2", u: atob("aHR0cHM6Ly93d3cubWR6eWFwaS5jb20vYXBpLnBocC9wcm92aWRlL3ZvZC8=") }, { n: "天堂", u: atob("aHR0cHM6Ly9jYWlqaS5keXR0enlhcGkuY29tL2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "天涯", u: atob("aHR0cHM6Ly90eXlzenlhcGkuY29tL2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "U酷2", u: atob("aHR0cHM6Ly9hcGkudWt1YXBpODguY29tL2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "无忧", u: atob("aHR0cHM6Ly93d3cud3l2b2QuY29tL2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "天空", u: atob("aHR0cHM6Ly9hcGkudGlhbmtvbmdhcGkuY29tL2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "麒麟", u: atob("aHR0cHM6Ly93d3cucWlsaW56eXouY29tL2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "快鹰", u: atob("aHR0cDovL3NhdnZpdXV4LmhrMy4zNDU4ODgueHl6LmNkbi5jbG91ZGZsYXJlLm5ldC9hcGkucGhwL3Byb3ZpZGUvdm9kLw==") }, { n: "快车2", u: atob("aHR0cHM6Ly9jYWlqaS5rY3p5YXBpLmNvbS9hcGkucGhwL3Byb3ZpZGUvdm9kLw==") }, { n: "淘片", u: atob("aHR0cHM6Ly90YW9waWFuYXBpLmNvbS9jamFwaS9tYzEwL3ZvZC8=") }, { n: "影图", u: atob("aHR0cHM6Ly9jai52b2RpbWcudG9wL2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { n: "云解", u: atob("aHR0cHM6Ly9hcGkueXBhcnNlLmNvbS9hcGkvcHJvdmlkZS92b2Qv") }, { n: "39影", u: atob("aHR0cHM6Ly93d3cuMzlrYW4uY29tL2FwaS5waHAvcHJvdmlkZS92b2Qv") } ]; const _PRIORITY_ORDER = ['ikun', '红牛', '360', '豪华', '非凡', '速播2', 'U酷', '1080', '天堂', '猫眼', '魔都']; const _LOW_PRIORITY = new Set(['量子', '最大']); const processApis = raw => { const m = new Map(); raw.forEach(a => { if (!m.has(a.u)) m.set(a.u, { name: a.n, url: a.u, shortName: a.n.substring(0, 4) }); }); const apis = Array.from(m.values()); const priority = apis.filter(a => ApiStats._priority.has(a.shortName)); priority.sort((a, b) => { const ai = _PRIORITY_ORDER.indexOf(a.shortName); const bi = _PRIORITY_ORDER.indexOf(b.shortName); return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi); }); const low = apis.filter(a => _LOW_PRIORITY.has(a.shortName)); const others = apis.filter(a => !ApiStats._priority.has(a.shortName) && !_LOW_PRIORITY.has(a.shortName)); if (CONFIG.SMART_SORTING) { others.sort((a, b) => ApiStats.score(ApiStats.get(b.shortName), b.shortName) - ApiStats.score(ApiStats.get(a.shortName), a.shortName)); } return [...priority, ...others, ...low]; }; const UNIQUE_APIS = processApis(RAW_APIS); const State = { eps: [], curUrl: '', hiddenEl: null, panelOpen: false, curURL: location.href, dom: {}, timers: {}, cache: { key: null, results: [] }, activeName: null, firstAuto: false, curEp: null, failed: new Set(), failedSources: new Set(), closed: false, searchId: 0, playing: false, switchCount: 0, stuckPending: false, isPageLoad: true, lastSwitchTime: 0, isAutoSwitch: false, playGeneration: 0 }; const EP_MATCH_PATTERNS = [ { regex: /第\s*(\d+)\s*集/, group: 1 }, { regex: /第\s*(\d+)\s*话/, group: 1 }, { regex: /第\s*(\d+)\s*期/, group: 1 }, { regex: /EP\s*(\d+)/i, group: 1 }, { regex: /Ep\s*(\d+)/, group: 1 }, { regex: /ep\s*(\d+)/, group: 1 }, { regex: /E(\d+)/i, group: 1 }, { regex: /(\d+)\s*集/, group: 1 }, { regex: /(\d+)\s*话/, group: 1 }, { regex: /(\d+)\s*期/, group: 1 }, { regex: /\[(\d+)\]/, group: 1 }, { regex: /【(\d+)】/, group: 1 }, { regex: /((\d+))/, group: 1 }, { regex: /\((\d+)\)/, group: 1 } ]; function matchEpisodeEnhanced(epName, targetNum) { if (!epName || !targetNum) return false; const cleanName = epName.trim(); const num = parseInt(targetNum, 10); if (/^\d+$/.test(cleanName) && parseInt(cleanName, 10) === num) return true; for (const pattern of EP_MATCH_PATTERNS) { const match = cleanName.match(pattern.regex); if (match && match[pattern.group]) { if (parseInt(match[pattern.group], 10) === num) return true; } } if (cleanName.includes(targetNum)) { const idx = cleanName.indexOf(targetNum); const before = idx === 0 ? '' : cleanName.charAt(idx - 1); const after = idx + targetNum.length >= cleanName.length ? '' : cleanName.charAt(idx + targetNum.length); if ((/\D/.test(before) || idx === 0) && (/\D/.test(after) || idx + targetNum.length === cleanName.length)) { return true; } } return false; } // 主流视频网站域名列表 const VIDEO_DOMAINS = [ 'iqiyi.com', 'iq.com', 'youku.com', 'v.qq.com', 'm.v.qq.com', 'mgtv.com', 'bilibili.com', 'b23.tv', 'le.com', 'letv.com', 'sohu.com', 'pptv.com', 'acfun.cn', '1905.com', 'ixigua.com', 'douyin.com', 'kuaishou.com', 'miguvideo.com', 'cmvideo.cn', 'baofeng.com', 'fun.tv', 'funshion.com', 'bfeng.cn', 'stormsfy.com', 'tudou.com', 'hanju.koudaibaobao.com', 'maiduidui.com', 'rrsp.tv', 'vas.hiaiabc.com', 'mgtv.com' ]; // 视频页面URL关键词 const VIDEO_URL_KEYWORDS = [ '/video/', '/play/', '/v_show/', '/v_play/', '/episode/', '/bangumi/', '/show/', '/detail/', '/movie/', '/tv/', '/film/', '/anime/', '/variety/', '/documentary/', '/x/cover/', '/x/page/', '/x/web-interface/' ]; // 视频播放器选择器 const VIDEO_PLAYER_SELECTORS = [ 'video', 'iframe[src*="player"]', 'iframe[src*="video"]', '[class*="player"]', '[id*="player"]', '[class*="video"]', '[id*="video"]', '.artplayer-app', '#artplayer', '.xgplayer', '.dplayer', '.video-player', '[class*="migu-player"]', '[class*="commonPlayer"]', '#wPlayer', '#player-container', '#sohuplayer', '#flashbox', '.iqp-player', '#bilibili-player', '.bpx-player-container', '#mgtv-player-wrap', '#le_player', '#player_swf', '#pp-player', '#ACPlayer', '#video-player', '#xigua-player', '.video-area', '.player-container', '#bf-player', '.bf-player', '#baofeng-player', '.bf-video-player', '#fun-player', '.fun-player', '#FunPlayer', '#h5player', '[class*="txp_player"]', '[class*="tenvideo"]', '[class*="yvp-player"]', '[class*="youku-player"]', '[class*="iqiyi-player"]', '[class*="le-player"]', '[class*="sohu-player"]', '[class*="pptv-player"]', '[class*="ac-player"]', '[class*="mgtv-player"]' ]; // 精确的播放器选择器列表(只匹配真正的视频播放器) const EXACT_PLAYER_SELECTORS = [ '#tenvideo_player', '.txp_player_root', '#player-container', '#player', '[class*="txp_player"]', '[class*="mod_player"]', '.site-player', '#bilibili-player', '.bpx-player-container', '[class*="player-wrap"]', '.container-player', '#youku-player', '[class*="youku-player"]', '.iqp-player', '#iqp-player', '[class*="iqiyi-player"]', '#mgtv-player-wrap', '[class*="mgtv-player"]', '#sohuplayer', '#flashbox', '#le_player', '#player_swf', '#pp-player', '#ACPlayer', '#video-player', '#xigua-player', '.video-area', '.artplayer-app', '#bf-player', '.bf-player', '#baofeng-player', '#fun-player', '.fun-player', '#FunPlayer', '#h5player' ]; const onVideoPage = () => { const href = location.href; const hn = location.hostname; const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || window.innerWidth <= 768; // 移动端:使用宽松检测(保持原有行为) if (isMobile) { // 1. 检查URL模式 if (CONFIG.VIDEO_URL_PATTERNS.some(p => p.test(href))) return true; // 2. 检查URL中的视频关键词 if (VIDEO_URL_KEYWORDS.some(kw => href.toLowerCase().includes(kw))) { if (VIDEO_DOMAINS.some(d => hn.includes(d))) return true; } // 3. 视频网站且有播放器 if (VIDEO_DOMAINS.some(d => hn.includes(d))) { if (document.querySelector('video')) return true; if (EXACT_PLAYER_SELECTORS.some(sel => document.querySelector(sel))) return true; const title = document.title.toLowerCase(); if (title.includes('播放') || title.includes('play') || title.includes('watch')) return true; } return false; } // 电脑端:严格检测 // 必须是视频网站域名 const isVideoSite = VIDEO_DOMAINS.some(d => hn.includes(d)); if (!isVideoSite) return false; // URL必须包含视频相关路径(这是最关键的判断) const hasVideoUrl = CONFIG.VIDEO_URL_PATTERNS.some(p => p.test(href)) || VIDEO_URL_KEYWORDS.some(kw => href.toLowerCase().includes(kw)); // 如果URL不匹配视频页面,直接返回false if (!hasVideoUrl) return false; // URL匹配后,再检查是否有播放器元素 const hasVideo = document.querySelector('video'); const hasPlayer = EXACT_PLAYER_SELECTORS.some(sel => document.querySelector(sel)); // 有播放器元素才显示 if (hasVideo || hasPlayer) return true; // 如果有video标签且有src,也显示 if (document.querySelector('video[src], video source[src]')) return true; return false; }; const UI = { init() { const existingRoot = document.getElementById('vip-root'); if (existingRoot) existingRoot.remove(); this._css(); State.dom.c = this._el('div', { id: 'vip-root' }); State.dom.btn = this._el('div', { id: 'vip-btn', title: '点击展开/解析VIP视频 (可拖拽)' }); State.dom.btn.innerHTML = '