// ==UserScript== // @name 常看VIP视频全网解析 (Beta版) // @namespace https://scriptcat.org/zh-CN/script-show-page/6457 // @version 2.4.4 // @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_listValues // @grant GM_deleteValue // @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 gcore.jsdelivr.net // @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'; 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; 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', 'gcore.jsdelivr.net']; [...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 _UA_MOBILE = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); const isMobileView = () => _UA_MOBILE || window.innerWidth <= 768; 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\//, /mgtv\.com\/(b|s|video)\//, /m\.mgtv\.com\/(b|s|video)\//, /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\.com\/show\//, /m\.pptv\.com\/show\//, /pptv\.com\/play\//, /m\.pptv\.com\/play\//, /acfun\.cn\/v\/ac/, /m\.acfun\.cn\/v\/ac/, /acfun\.cn\/play\//, /m\.acfun\.cn\/play\//, /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', LIB_CORRUPT: 'tm_lib_corrupt', PLAYER_READY: 'tm_player_ready', SWITCH_URL: 'tm_switch_url', SWITCH_ACK: 'tm_switch_ack' }, 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"]', '#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, [class*="episode"][class*="on"] [class*="text"], [class*="pick-list"] [class*="on"]', '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': '[data-mvp-identifier="intro"][title], .intro-title[title], .video-title[title], .player-title, [class*="video-title"], [class*="videoTitle"], [class*="mod_title"], h1[class*="title"]', 'v.qq.com': '[data-mvp-identifier="intro"][title], .intro-title[title], [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': '[data-mvp-identifier="intro"][title], .intro-title[title], [class*="video-title"], [class*="videoTitle"], [class*="mod_title"], h1', 'v.qq.com': '[data-mvp-identifier="intro"][title], .intro-title[title], [class*="video-title"], [class*="videoTitle"], [class*="mod_title"], h1', '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 _ROMAN_MAP = { 'Ⅰ': '1', 'Ⅱ': '2', 'Ⅲ': '3', 'Ⅳ': '4', 'Ⅴ': '5', 'Ⅵ': '6', 'Ⅶ': '7', 'Ⅷ': '8', 'Ⅸ': '9', 'Ⅹ': '10', 'Ⅺ': '11', 'Ⅻ': '12' }; const _ROMAN_RE = /[ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ]/g; const _NOISE_RE = /[\s\u200b-\u200f\ufeff_·、,,!!??::;;'"“”‘’\-–—\[\]【】()()]/g; const normalizeName = s => (s || '') .replace(/[\u200b-\u200f\ufeff]/g, '') .replace(_ROMAN_RE, c => _ROMAN_MAP[c] || c) .toLowerCase() .replace(_NOISE_RE, ''); const nameSimilarity = (a, b) => { const x = normalizeName(a), y = normalizeName(b); if (!x || !y) return 0; if (x === y) return 1; if (x.includes(y) || y.includes(x)) return 0.75 + 0.25 * Math.min(x.length, y.length) / Math.max(x.length, y.length); if (x.length < 2 || y.length < 2) return 0; const ga = new Set(), gb = new Set(); for (let i = 0; i < x.length - 1; i++) ga.add(x.slice(i, i + 2)); for (let i = 0; i < y.length - 1; i++) gb.add(y.slice(i, i + 2)); let inter = 0; ga.forEach(g => { if (gb.has(g)) inter++; }); return (2 * inter) / (ga.size + gb.size); }; const titleQueryVariants = title => { const out = []; const push = s => { s = (s || '').trim(); if (s && s.length > 1 && !out.includes(s)) out.push(s); }; const toArabic = s => s.replace(_ROMAN_RE, c => _ROMAN_MAP[c] || c); push(title); push(toArabic(title)); push(title.replace(/\s+/g, '')); const short = title.split(_TITLE_SPLIT_RE)[0]; push(short); push(toArabic(short)); return out; }; 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) {} } }; function purgeSearchCache() { try { if (typeof GM_listValues !== 'function' || typeof GM_deleteValue !== 'function') return; const now = Date.now(); const prefix = 'cache_' + _CACHE_VERSION + '_'; GM_listValues().forEach(k => { if (k.indexOf('cache_') !== 0) return; if (k.indexOf(prefix) !== 0) { GM_deleteValue(k); return; } try { const c = JSON.parse(GM_getValue(k, 'null')); if (!c || now - c.ts >= _CACHE_EXPIRY) GM_deleteValue(k); } catch (e) { GM_deleteValue(k); } }); } catch (e) {} } const LibCache = { _hls: null, _art: null, _hlsPromise: null, _artPromise: null, _HLS_VER: '1.7.3', _ART_VER: '5.4.0', _CODE_KEYS: { hls: 'vp_lib_hls', art: 'vp_lib_art' }, _META_KEYS: { hls: 'vp_lib_meta_hls', art: 'vp_lib_meta_art' }, _MARKERS: { hls: 'Hls', art: 'Artplayer' }, _hlsUrls: [ 'https://fastly.jsdelivr.net/npm/hls.js@1.7.3/dist/hls.min.js', 'https://cdn.jsdelivr.net/npm/hls.js@1.7.3/dist/hls.min.js', 'https://unpkg.com/hls.js@1.7.3/dist/hls.min.js', 'https://gcore.jsdelivr.net/npm/hls.js@1.7.3/dist/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' ], _ver(kind) { return kind === 'hls' ? this._HLS_VER : this._ART_VER; }, _urls(kind) { return kind === 'hls' ? this._hlsUrls : this._artUrls; }, _sum(str) { let h = 0x811c9dc5; for (let i = 0; i < str.length; i++) { h ^= str.charCodeAt(i); h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0; } return h.toString(36); }, _valid(kind, code, meta) { if (!meta || typeof code !== 'string') return false; if (meta.v !== this._ver(kind)) return false; if (code.length < 20000 || meta.len !== code.length) return false; if (meta.sum !== this._sum(code)) return false; return code.indexOf(this._MARKERS[kind]) !== -1; }, _read(kind) { try { const code = GM_getValue(this._CODE_KEYS[kind], ''); if (!code) return null; let meta = null; try { meta = JSON.parse(GM_getValue(this._META_KEYS[kind], 'null')); } catch (e) {} if (this._valid(kind, code, meta)) return code; } catch (e) {} this._clearKind(kind); return null; }, _write(kind, code) { try { GM_setValue(this._CODE_KEYS[kind], code); GM_setValue(this._META_KEYS[kind], JSON.stringify({ v: this._ver(kind), len: code.length, sum: this._sum(code), ts: Date.now() })); } catch (e) { console.warn('[VIP] Persist player lib failed:', e && e.message); } }, _clearKind(kind) { try { GM_setValue(this._CODE_KEYS[kind], ''); GM_setValue(this._META_KEYS[kind], ''); } catch (e) {} }, purge() { this._hls = null; this._art = null; this._hlsPromise = null; this._artPromise = null; this._clearKind('hls'); this._clearKind('art'); }, _metaOk(kind) { try { const meta = JSON.parse(GM_getValue(this._META_KEYS[kind], 'null')); return !!(meta && meta.v === this._ver(kind) && meta.len > 20000); } catch (e) { return false; } }, _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(kind) { const urls = this._urls(kind); const marker = this._MARKERS[kind]; 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 => { if (code && code.length >= 20000 && code.indexOf(marker) !== -1) { resolve(code); } else { index++; tryNext(); } }) .catch(() => { index++; tryNext(); }); }; tryNext(); }); }, async _load(kind) { const cached = this._read(kind); if (cached) return cached; const code = await this._fetchWithFallback(kind); if (code) this._write(kind, code); return code; }, preload() { if (this._metaOk('hls') && this._metaOk('art')) return; this.getHls(); this.getArt(); }, async getHls() { if (this._hls) return this._hls; if (this._hlsPromise) return await this._hlsPromise; this._hlsPromise = this._load('hls').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._load('art').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, libReloadTried: false, lastPlayUrl: '', iframeReady: false, switchAckGen: 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' ]; 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 _EXACT_PLAYER_SEL = EXACT_PLAYER_SELECTORS.join(', '); const onVideoPage = (() => { let _cache = { url: '', result: false, ts: 0 }; return () => { const href = location.href; if (href === _cache.url && Date.now() - _cache.ts < 500) return _cache.result; const hn = location.hostname; const isMobile = isMobileView(); let _result; if (isMobile) { if (CONFIG.VIDEO_URL_PATTERNS.some(p => p.test(href))) { _cache = { url: href, result: true, ts: Date.now() }; return true; } if (VIDEO_URL_KEYWORDS.some(kw => href.toLowerCase().includes(kw))) { if (VIDEO_DOMAINS.some(d => hn.includes(d))) { _cache = { url: href, result: true, ts: Date.now() }; return true; } } if (VIDEO_DOMAINS.some(d => hn.includes(d))) { if (document.querySelector('video')) { _cache = { url: href, result: true, ts: Date.now() }; return true; } if (document.querySelector(_EXACT_PLAYER_SEL)) { _cache = { url: href, result: true, ts: Date.now() }; return true; } const title = document.title.toLowerCase(); if (title.includes('播放') || title.includes('play') || title.includes('watch')) { _cache = { url: href, result: true, ts: Date.now() }; return true; } } _cache = { url: href, result: false, ts: Date.now() }; return false; } const isVideoSite = VIDEO_DOMAINS.some(d => hn.includes(d)); if (!isVideoSite) { _cache = { url: href, result: false, ts: Date.now() }; return false; } const hasVideoUrl = CONFIG.VIDEO_URL_PATTERNS.some(p => p.test(href)) || VIDEO_URL_KEYWORDS.some(kw => href.toLowerCase().includes(kw)); if (!hasVideoUrl) { _cache = { url: href, result: false, ts: Date.now() }; return false; } const hasVideo = document.querySelector('video'); const hasPlayer = !!document.querySelector(_EXACT_PLAYER_SEL); if (hasVideo || hasPlayer) { _cache = { url: href, result: true, ts: Date.now() }; return true; } if (document.querySelector('video[src], video source[src]')) { _cache = { url: href, result: true, ts: Date.now() }; return true; } _cache = { url: href, result: false, ts: Date.now() }; return false; }; })(); const UI = { init() { const existingRoot = document.getElementById('vip-root'); if (existingRoot) existingRoot.remove(); const staleOv = document.getElementById('vip-overlay'); if (staleOv) staleOv.remove(); const staleToast = document.getElementById('vip-toast'); if (staleToast) staleToast.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 = '