// ==UserScript== // @name 常看VIP视频全网解析 (Beta版) // @namespace https://scriptcat.org/zh-CN/script-show-page/6457 // @version 2.2.8 // @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 *://*/* // @grant GM_addStyle // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @require https://cdn.jsdelivr.net/gh/Tampermonkey/utils@3b32b826e84ccc99a0a3e3d8d6e5ce0fa9834f23/requires/gh_2215_make_GM_xhr_more_parallel_again.js // @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 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 // @antifeature piracy // @license MIT // ==/UserScript== (function () { if (window.hasInitVipScript) return; window.hasInitVipScript = true; 'use strict'; const DNS_OPT = { dohServers: [ { id: 'ali', name: '阿里DNS', url: 'https://dns.alidns.com/dns-query', region: 'cn' }, { id: 'tx', name: '腾讯DNS', url: 'https://doh.pub/dns-query', region: 'cn' }, { id: 'bd', name: '百度DNS', url: 'https://doh.baidu.com/dns-query', region: 'cn' }, { id: '114', name: '114DNS', url: 'https://doh.pub/dns-query', region: 'cn' }, { id: '360', name: '360DNS', url: 'https://doh.360.cn/dns-query', region: 'cn' }, { id: 'cf', name: 'Cloudflare', url: 'https://cloudflare-dns.com/dns-query', region: 'intl' }, { id: 'gg', name: 'GoogleDNS', url: 'https://dns.google/resolve', region: 'intl' }, ], _domainRank: new Map(), _dnsCache: new Map(), _ready: false, _getCachedDns(domain) { const cached = this._dnsCache.get(domain); if (cached && Date.now() - cached.ts < 300000) return cached.ip; this._dnsCache.delete(domain); return null; }, _setCachedDns(domain, ip) { this._dnsCache.set(domain, { ip, ts: Date.now() }); }, detectRegion() { if (this._region) return this._region; const tz = Intl.DateTimeFormat().resolvedOptions().timeZone || ''; const lang = navigator.language || ''; this._region = (tz.includes('Asia/Shanghai') || tz.includes('Asia/Chongqing') || tz.includes('Asia/Harbin') || tz.includes('Asia/Urumqi') || lang.startsWith('zh')) ? 'cn' : 'intl'; return this._region; }, async _dohResolve(domain, server) { const cached = this._getCachedDns(domain); if (cached) return cached; const url = server.url + '?name=' + encodeURIComponent(domain) + '&type=A'; const startTime = Date.now(); try { const data = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: url, headers: { 'Accept': 'application/dns-json' }, timeout: 3000, onload(response) { if (response.status === 200) { try { resolve(JSON.parse(response.responseText)); } catch (e) { reject(new Error('JSON parse error')); } } else { reject(new Error('HTTP ' + response.status)); } }, onerror() { reject(new Error('Network error')); }, ontimeout() { reject(new Error('Timeout')); } }); }); const latency = Date.now() - startTime; const a = (data.Answer || []).filter(r => r.type === 1); const ip = a.length ? a[a.length - 1].data : null; if (ip) { this._setCachedDns(domain, ip); if (!this._serverLatency) this._serverLatency = new Map(); const prev = this._serverLatency.get(server.id) || { total: 0, count: 0 }; this._serverLatency.set(server.id, { total: prev.total + latency, count: prev.count + 1 }); } return ip; } catch { return null; } }, _probeDomain(domain) { return new Promise(resolve => { const start = Date.now(); const timer = setTimeout(() => { try { xhr.abort(); } catch {} resolve({ domain, latency: Infinity, ok: false }); }, 3000); const xhr = GM_xmlhttpRequest({ method: 'HEAD', url: 'https://' + domain + '/', timeout: 3000, onload() { clearTimeout(timer); resolve({ domain, latency: Date.now() - start, ok: true }); }, onerror() { clearTimeout(timer); resolve({ domain, latency: Date.now() - start, ok: false }); }, ontimeout() { clearTimeout(timer); resolve({ domain, latency: Infinity, ok: false }); } }); }); }, async _probeAll(domainList, concurrency = 12) { const executing = new Set(); const results = []; for (const domain of domainList) { const p = this._probeDomain(domain).then(r => { this._domainRank.set(domain, { latency: r.latency, ok: r.ok, ts: Date.now() }); results.push(r); }); executing.add(p); p.then(() => executing.delete(p)).catch(() => executing.delete(p)); if (executing.size >= concurrency) await Promise.race(executing); } await Promise.allSettled(executing); return results; }, async _dohBenchmark(domainList) { const region = this.detectRegion(); const sorted = [...this.dohServers].sort((a, b) => { const am = a.region === region ? 1 : 0; const bm = b.region === region ? 1 : 0; return bm - am; }); const testDomains = domainList.slice(0, 3); const benchResults = []; for (const server of sorted.slice(0, 4)) { let totalLatency = 0, ok = 0; for (const domain of testDomains) { const start = performance.now(); const ip = await this._dohResolve(domain, server); if (ip) { totalLatency += performance.now() - start; ok++; } } if (ok) benchResults.push({ name: server.name, avgLatency: totalLatency / ok, ok }); } benchResults.sort((a, b) => a.avgLatency - b.avgLatency); if (benchResults.length) this._bestDoh = benchResults[0].name; }, getLatency(domain) { const r = this._domainRank.get(domain); return r ? r.latency : Infinity; }, isDomainOk(domain) { const r = this._domainRank.get(domain); return r && r.ok; }, injectHints(domainList) { const frag = document.createDocumentFragment(); const dnsSelf = ['doh.pub', 'dns.alidns.com', 'doh.360.cn', 'doh.baidu.com', 'cloudflare-dns.com', 'dns.google', 'dns.quad9.net']; [...new Set([...dnsSelf, ...domainList])].forEach(d => { const link = document.createElement('link'); link.rel = 'dns-prefetch'; link.href = '//' + d; frag.appendChild(link); }); domainList.forEach(d => { const link = document.createElement('link'); link.rel = 'preconnect'; link.href = 'https://' + d; link.crossOrigin = 'anonymous'; frag.appendChild(link); }); ['fastly.jsdelivr.net', 'cdn.jsdelivr.net'].forEach(d => { const link = document.createElement('link'); link.rel = 'preconnect'; link.href = 'https://' + d; link.crossOrigin = 'anonymous'; frag.appendChild(link); }); document.head.appendChild(frag); }, async boot(domainList) { this.detectRegion(); this.injectHints(domainList); this._probeAll(domainList, 12).then(() => { this._ready = true; }); this._dohBenchmark(domainList).catch(() => {}); } }; const CONFIG = { API_TIMEOUT: 2500, STUCK_CHECK_TIMEOUT: 6000, SEARCH_CONCURRENCY: 20, 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]_/, /iq\.com\/play\//, /youku\.com\/v_show\/id_/, /v\.youku\.com\/v_show\/id_/, /v\.qq\.com\/(x\/cover|x\/page|tv|play)\//, /mgtv\.com\/b\//, /mgtv\.com\/s\//, /bilibili\.com\/(video|bangumi\/play)\//, /b23\.tv\//, /le\.com\/ptv\/vplay\//, /tv\.sohu\.com\/v\//, /film\.sohu\.com\/album\//, /pptv\.com\/show\//, /acfun\.cn\/v\/ac/, /1905\.com\/play\//, /ixigua\.com\/video\//, /ixigua\.com\/play\//, /tudou\.com\/(listplay|albumplay|programs\/view)\//, /fun\.tv\/vod-play\//, /baofeng\.com\/(play\/|play-|player\/)/, /bfeng\.cn\//, /stormsfy\.com\//, /fun\.tv\/(vod-play|player)\//, /funshion\.com\/(play|player)\//, /migumovie\.hcs\.cmvideo\.cn\/movie/, /miguvideo\.com\/detail/, /miguvideo\.com\/(v|n|p)\/play/, /miguvideo\.com\/play\//, /cmvideo\.cn\/(v|n|p)\/play/, /cmvideo\.cn\/detail/, /douyin\.com\/video\//, /kuaishou\.com\/short-video\//, /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', '.container-player', '#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', '.artplayer-app', '#bf-player', '.bf-player', '#baofeng-player', '.bf-video-player', '#player-container', '#fun-player', '.fun-player', '#FunPlayer', '#h5player' ], 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', '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"]' } }, 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 = 'v5'; 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, _fetch(url, label) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: url, timeout: 30000, onload(r) { if (r.status >= 200 && r.status < 400 && r.responseText) { resolve(r.responseText); } else { reject(new Error(label + ' fetch status ' + r.status)); } }, onerror(e) { reject(new Error(label + ' network error')); }, ontimeout() { reject(new Error(label + ' timeout')); } }); }); }, preload() { if (!this._hlsPromise) { this._hlsPromise = this._fetch( 'https://fastly.jsdelivr.net/npm/hls.js@1.6.11/dist/hls.min.js', 'hls.js' ).then(code => { this._hls = code; return code; }).catch(() => null); } if (!this._artPromise) { this._artPromise = this._fetch( 'https://fastly.jsdelivr.net/npm/artplayer@5.4.0/dist/artplayer.min.js', 'artplayer.js' ).then(code => { this._art = code; return code; }).catch(() => null); } }, async getHls() { if (this._hls) return this._hls; if (this._hlsPromise) return await this._hlsPromise; return await this._fetch('https://fastly.jsdelivr.net/npm/hls.js@1.6.11/dist/hls.min.js', 'hls.js').catch(() => null); }, async getArt() { if (this._art) return this._art; if (this._artPromise) return await this._artPromise; return await this._fetch('https://fastly.jsdelivr.net/npm/artplayer@5.4.0/dist/artplayer.min.js', 'artplayer.js').catch(() => null); } }; LibCache.preload(); const ApiStats = { _p: {}, _t: null, // 将公网抗 PNA 拦截能力最强、大厂 CDN 节点最稳的线路(ikun、红牛、360等)置顶 _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: "CK", u: atob("aHR0cHM6Ly9ja3p5Lm1lL2FwaS5waHAvcHJvdmlkZS92b2Qv") }, { 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, switching: false, lastSwitchTime: 0, isManualSelect: false, isAutoSwitch: false }; 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 onVideoPage = () => { if (CONFIG.VIDEO_URL_PATTERNS.some(p => p.test(location.href))) return true; const hn = location.hostname; if ((hn.includes('miguvideo.com') || hn.includes('cmvideo.cn')) && (document.querySelector('#wPlayer') || document.querySelector('[class*="migu-player"]') || document.querySelector('[class*="commonPlayer"]'))) { return true; } if ((hn.includes('baofeng.com') || hn.includes('fun.tv') || hn.includes('funshion.com') || hn.includes('bfeng.cn')) && (document.querySelector('#player-container') || document.querySelector('[class*="bf-player"]') || document.querySelector('[class*="fun-player"]') || document.querySelector('[id*="FunPlayer"]'))) { return true; } return false; }; const UI = { init() { 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 = '