1+ <!doctype html>
2+ < html lang ="zh-CN ">
3+ < head >
4+ < meta charset ="utf-8 ">
5+ < meta name ="viewport " content ="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no ">
6+ < title > 观影 WebHome</ title >
7+ < style >
8+ : root {--text : # f7fbff ;--muted : rgba (247 , 251 , 255 , .68 );--line : rgba (255 , 255 , 255 , .16 );--soft : rgba (42 , 50 , 58 , .44 );--panel : rgba (42 , 50 , 58 , .62 );--accent : # ff6b35 ;--accent2 : # ffb86b ;--radius : 18px }* {box-sizing : border-box}html , body {margin : 0 ;min-height : 100% ;background : transparent;color : var (--text );font-family : -apple-system, BlinkMacSystemFont, "Segoe UI" , "PingFang SC" , "Microsoft YaHei" , Arial, sans-serif}html : not (.fm-native ), html : not (.fm-native ) body {background : # 222a32 }.app {min-height : 100vh ;padding : 10px 10px 34px }.bar {display : flex;align-items : center;gap : 8px }.brand {font-size : 21px ;font-weight : 900 ;white-space : nowrap}.brand b {color : var (--accent2 )}.search {flex : 1 ;display : flex;gap : 8px ;align-items : center;background : rgba (255 , 255 , 255 , .10 );border : 1px solid rgba (255 , 255 , 255 , .14 );border-radius : 999px ;padding : 6px 7px 6px 13px }.search input {flex : 1 ;min-width : 0 ;background : transparent;border : 0 ;outline : 0 ;color : var (--text );font-size : 15px }.search button , .btn {border : 1px solid var (--line );background : var (--soft );color : var (--text );border-radius : 999px ;padding : 8px 12px ;font-size : 14px ;cursor : pointer}.search button , .primary {border-color : rgba (255 , 107 , 53 , .45 );background : linear-gradient (135deg , rgba (255 , 107 , 53 , .9 ), rgba (255 , 184 , 107 , .72 ));color : # 17110d ;font-weight : 800 }.tabs {display : flex;gap : 8px ;overflow : auto;padding : 10px 2px 0 ;scrollbar-width : none}.tabs ::-webkit-scrollbar {display : none}.tab {white-space : nowrap;border-radius : 999px ;padding : 8px 14px ;background : rgba (255 , 255 , 255 , .08 );border : 1px solid rgba (255 , 255 , 255 , .12 );color : var (--muted )}.tab .active {background : linear-gradient (135deg , rgba (255 , 107 , 53 , .55 ), rgba (255 , 184 , 107 , .3 ));color : var (--text );border-color : rgba (255 , 184 , 107 , .58 );font-weight : 800 }.status {margin : 10px 2px ;color : var (--muted );font-size : 14px }.grid {display : grid;grid-template-columns : repeat (3 , minmax (0 , 1fr ));gap : 10px }.card {border-radius : 15px ;overflow : hidden;background : linear-gradient (180deg , rgba (255 , 255 , 255 , .09 ), rgba (255 , 255 , 255 , .045 ));border : 1px solid rgba (255 , 255 , 255 , .12 );cursor : pointer}.poster {aspect-ratio : 3 / 4 ;background : rgba (255 , 255 , 255 , .08 );position : relative}.poster img {width : 100% ;height : 100% ;object-fit : cover;display : block}.poster .noimg {height : 100% ;display : grid;place-items : center;color : var (--muted );font-size : 28px }.badge {position : absolute;right : 7px ;top : 7px ;padding : 3px 8px ;border-radius : 999px ;background : rgba (0 , 0 , 0 , .58 );font-size : 11px ;font-weight : 800 }.meta {padding : 8px 8px 10px }.name {font-weight : 800 ;font-size : 13px ;white-space : nowrap;overflow : hidden;text-overflow : ellipsis}.remark {margin-top : 4px ;color : var (--muted );font-size : 11px ;white-space : nowrap;overflow : hidden;text-overflow : ellipsis}.sheet {position : fixed;inset : 0 ;z-index : 40 ;display : none;background : transparent;overflow : auto;padding : 12px }.sheet .active {display : block}.panel {background : linear-gradient (135deg , rgba (64 , 35 , 30 , .72 ), rgba (30 , 24 , 24 , .58 ));border : 1px solid rgba (255 , 255 , 255 , .10 );border-radius : 22px ;padding : 16px 12px 24px ;margin-bottom : 42px }.head {display : grid;grid-template-columns : 38% 1fr ;gap : 14px }.pic {width : 100% ;aspect-ratio : 3 / 4 ;object-fit : cover;border-radius : 16px ;background : rgba (255 , 255 , 255 , .08 )}.title {font-size : 22px ;font-weight : 700 ;line-height : 1.25 ;margin : 0 0 8px }.info {font-size : 14px ;line-height : 1.65 ;color : rgba (255 , 255 , 255 , .75 );max-height : 230px ;overflow : hidden}.pill {display : inline-block;margin : 18px 0 10px ;padding : 8px 18px ;border-radius : 999px ;background : rgba (255 , 255 , 255 , .10 );border : 1px solid rgba (255 , 184 , 107 , .55 );font-size : 15px ;font-weight : 800 }.desc {font-size : 15px ;line-height : 1.75 ;color : rgba (255 , 255 , 255 , .72 )}.line-tabs {display : flex;gap : 8px ;overflow : auto;margin : 8px 0 10px }.line-tab {white-space : nowrap;border-radius : 999px ;padding : 8px 12px ;background : rgba (255 , 255 , 255 , .08 );border : 1px solid rgba (255 , 255 , 255 , .14 );color : rgba (255 , 255 , 255 , .82 )}.line-tab .active {background : rgba (255 , 107 , 53 , .36 );border-color : rgba (255 , 184 , 107 , .55 );font-weight : 800 }.eps {display : grid;grid-template-columns : repeat (3 , minmax (0 , 1fr ));gap : 9px }.ep {border-radius : 12px ;padding : 10px 5px ;background : rgba (255 , 255 , 255 , .09 );border : 1px solid rgba (255 , 255 , 255 , .14 );color : rgba (255 , 255 , 255 , .9 );font-size : 13px ;text-align : center;min-height : 42px }.empty {padding : 34px 18px ;text-align : center;color : var (--muted );background : var (--soft );border : 1px dashed var (--line );border-radius : var (--radius )}.pager {display : flex;gap : 8px ;justify-content : center;margin : 14px 0 }.pager button {padding : 8px 16px ;border-radius : 999px ;background : rgba (255 , 255 , 255 , .08 );border : 1px solid var (--line );color : var (--text )}.mask {position : fixed;inset : 0 ;z-index : 70 ;display : none;background : rgba (0 , 0 , 0 , .35 );padding : 18px }.mask .active {display : grid;place-items : center}.cfg {width : min (560px , 100% );background : rgba (35 , 42 , 50 , .95 );border : 1px solid var (--line );border-radius : 20px ;padding : 16px }.field {width : 100% ;min-height : 42px ;border-radius : 12px ;border : 1px solid var (--line );background : rgba (255 , 255 , 255 , .08 );color : var (--text );padding : 8px 10px ;outline : 0 }@media (min-width : 720px ){.grid {grid-template-columns : repeat (5 , minmax (0 , 1fr ))}.app {padding : 18px 28px 44px }.head {grid-template-columns : 180px 1fr }.pic {width : 180px }.eps {grid-template-columns : repeat (5 , minmax (0 , 1fr ))}}@media (min-width : 1100px ){.grid {grid-template-columns : repeat (7 , minmax (0 , 1fr ))}.app {max-width : 1380px ;margin : 0 auto}.sheet {max-width : 1180px ;margin : 0 auto}}
9+ </ style >
10+ </ head >
11+ < body >
12+ < div class ="app "> < div class ="bar "> < div class ="brand "> 观< b > 影</ b > </ div > < form class ="search " id ="searchForm "> < input id ="kw " placeholder ="搜索电影、剧集、动漫… "> < button > 搜索</ button > </ form > < button class ="btn " id ="cfgBtn "> 配置</ button > </ div > < div class ="tabs " id ="tabs "> </ div > < div class ="status " id ="status "> 准备加载…</ div > < main id ="content "> </ main > </ div >
13+ < div class ="sheet " id ="sheet "> < div class ="panel " id ="panel "> </ div > </ div >
14+ < div class ="mask " id ="cfgMask "> < div class ="cfg "> < h3 > 观影配置</ h3 > < p style ="color:var(--muted);font-size:13px "> 如站点需要验证,请填入浏览器 Cookie。</ p > < textarea class ="field " id ="cookieInput " rows ="5 " placeholder ="browser_verified=...; PHPSESSID=...; app_auth=... "> </ textarea > < div style ="display:flex;gap:8px;margin-top:12px "> < button class ="btn primary " id ="saveCfg "> 保存</ button > < button class ="btn " id ="closeCfg "> 关闭</ button > </ div > </ div > </ div >
15+ < script >
16+ ( function ( ) {
17+ 'use strict' ;
18+ if ( window . fongmiBridge || window . fm ) document . documentElement . classList . add ( 'fm-native' ) ;
19+ const HOST = 'https://www.xn--10vr61a3xc5x3b.com' , IMG = 'https://s.tutu.pm/img' , UA = 'Mozilla/5.0 (Linux; Android 16; Pixel 9 Pro Build/BP1A.250305.019) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.7743.101 Mobile Safari/537.36' ;
20+ const CLASSES = [ [ 'mv' , '电影' ] , [ 'tv' , '剧集' ] , [ 'ac' , '动漫' ] ] ;
21+ let state = { tid :'home' , page :1 , last :[ ] , pageCount :1 } , currentDetail = null , cookie = localStorage . getItem ( 'guanying_cookie' ) || '' ;
22+ const $ = s => document . querySelector ( s ) , content = $ ( '#content' ) , status = $ ( '#status' ) , tabs = $ ( '#tabs' ) ;
23+ function esc ( s ) { return String ( s || '' ) . replace ( / [ & < > ' " ] / g, c => ( { '&' :'&' , '<' :'<' , '>' :'>' , "'" :''' , '"' :'"' } [ c ] ) ) }
24+ function clean ( s ) { return String ( s || '' ) . replace ( / < [ ^ > ] + > / g, '' ) . replace ( / \s + / g, ' ' ) . trim ( ) }
25+ function abs ( u ) { try { return new URL ( u , HOST + '/' ) . href } catch ( e ) { return u || '' } }
26+ function b64e ( o ) { let s = typeof o === 'string' ?o :JSON . stringify ( o ) ; return btoa ( unescape ( encodeURIComponent ( s ) ) ) . replace ( / = + $ / , '' ) . replace ( / \+ / g, '-' ) . replace ( / \/ / g, '_' ) }
27+ function b64d ( s ) { try { return JSON . parse ( decodeURIComponent ( escape ( atob ( String ( s ) . replace ( / - / g, '+' ) . replace ( / _ / g, '/' ) + '=' . repeat ( ( 4 - String ( s ) . length % 4 ) % 4 ) ) ) ) ) } catch ( e ) { try { return decodeURIComponent ( escape ( atob ( String ( s ) . replace ( / - / g, '+' ) . replace ( / _ / g, '/' ) + '=' . repeat ( ( 4 - String ( s ) . length % 4 ) % 4 ) ) ) ) } catch ( x ) { return s } } }
28+ function pic ( d , i ) { return d && i ?`${ IMG } /${ d } /${ i } /256.webp` :'' }
29+ function headers ( ref ) { let h = { 'User-Agent' :UA , 'Accept' :'*/*' , 'Referer' :ref || HOST + '/' , 'X-Requested-With' :'XMLHttpRequest' , 'Accept-Language' :'zh-CN,zh;q=0.9' } ; if ( cookie ) h . Cookie = cookie ; return h }
30+ async function req ( u , ref ) { let url = u . startsWith ( 'http' ) ?u :HOST + u ; status . textContent = '加载:' + url ; if ( window . fm && fm . req ) { let r = await fm . req ( url , { method :'GET' , headers :headers ( ref ) , responseType :'text' , timeout :25 } ) ; if ( ! r . ok ) throw new Error ( r . error || ( 'HTTP ' + r . status ) ) ; let t = r . body || '' ; if ( / p o w S o l v e | " r e f r e s h " \s * : \s * 1 / . test ( t ) ) { await pow ( ) ; r = await fm . req ( url , { method :'GET' , headers :headers ( ref ) , responseType :'text' , timeout :25 } ) ; t = r . body || '' } return t } let r = await fetch ( url , { headers :headers ( ref ) } ) ; let t = await r . text ( ) ; return t }
31+ async function jsonReq ( u , ref ) { try { return JSON . parse ( await req ( u , ref ) || '{}' ) } catch ( e ) { return { } } }
32+ async function pow ( ) { try { let d = await jsonReq ( '/res/pow' ) ; if ( ! d . N ) return false ; let n = BigInt ( '0x' + d . N ) , y = BigInt ( '0x' + d . x ) , t = parseInt ( d . t || 0 ) ; for ( let i = 0 ; i < t ; i ++ ) y = ( y * y ) % n ; let body = 'y=' + y . toString ( 16 ) ; if ( window . fm && fm . req ) { let r = await fm . req ( HOST + '/res/pow' , { method :'POST' , headers :Object . assign ( headers ( HOST + '/' ) , { 'Content-Type' :'application/x-www-form-urlencoded' } ) , body, responseType :'text' , timeout :25 } ) ; return / s u c c e s s / . test ( r . body || '' ) } await fetch ( HOST + '/res/pow' , { method :'POST' , headers :Object . assign ( headers ( HOST + '/' ) , { 'Content-Type' :'application/x-www-form-urlencoded' } ) , body} ) ; return true } catch ( e ) { return false } }
33+ function obj ( h , k ) { let p = '_obj.' + k , i = h . indexOf ( p ) ; if ( i < 0 ) return null ; i = h . indexOf ( '=' , i ) ; if ( i < 0 ) return null ; i ++ ; while ( i < h . length && / \s / . test ( h [ i ] ) ) i ++ ; let q = h [ i ] ; if ( q !== '[' && q !== '{' ) return null ; let end = q === '{' ?'}' :']' , deep = 0 , ins = false , escp = false , out = '' ; for ( let j = i ; j < h . length ; j ++ ) { let c = h [ j ] ; out += c ; if ( escp ) { escp = false ; continue } if ( c === '\\' ) { escp = true ; continue } if ( c === '"' ) { ins = ! ins ; continue } if ( ins ) continue ; if ( c === q ) deep ++ ; else if ( c === end ) { deep -- ; if ( deep === 0 ) break } } out = out . replace ( / \\ \/ / g, '/' ) . replace ( / \\ ' / g, "'" ) . replace ( / , \s * ( [ } \] ] ) / g, '$1' ) ; try { return JSON . parse ( out ) } catch ( e ) { return null } }
34+ function vod ( name , i , d , remarks , extra ) { let it = Object . assign ( { title :name || '未知' , id :String ( i || '' ) , dir :String ( d || '' ) , remarks :String ( remarks || '' ) } , extra || { } ) ; return { vod_id :b64e ( it ) , vod_name :it . title , vod_pic :pic ( it . dir , it . id ) , vod_remarks :it . remarks , _raw :it } }
35+ function listFromInlist ( x ) { if ( ! x ) return [ ] ; let arr = [ ] ; if ( Array . isArray ( x ) ) { x . forEach ( b => arr = arr . concat ( listFromInlist ( b ) ) ) ; return arr } let ts = x . t || [ ] , ids = x . i || [ ] , ty = x . ty || '' , gs = x . g || [ ] , qs = x . q || [ ] ; ts . forEach ( ( name , n ) => { let q = Array . isArray ( qs [ n ] ) ?qs [ n ] . join ( ' ' ) :'' ; arr . push ( vod ( name , ids [ n ] || '' , ty , ( ( gs [ n ] || '' ) + ' ' + q ) . trim ( ) ) ) } ) ; return arr }
36+ function listFromSearch ( x ) { if ( ! x || typeof x !== 'object' ) return [ ] ; let l = x . l || { } , titles = l . title || l . t || [ ] , ids = l . i || l . id || [ ] , ds = l . d || l . dir || [ ] , years = l . year || [ ] ; return titles . map ( ( t , i ) => vod ( t , ids [ i ] || '' , ds [ i ] || 'mv' , years [ i ] || '' ) ) }
37+ async function suggest ( tid ) { let kws = { mv :[ '2025' , '电影' , '最新电影' , '人生' ] , tv :[ '剧集' , '2025' , '黑袍' , '庆余年' ] , ac :[ '动漫' , '星辰变' , '斗罗' , '瑞克' ] , home :[ '2025' , '星辰变' , '黑袍' , '人生' ] } [ tid ] || [ tid ] , arr = [ ] , seen = new Set ( ) ; for ( let kw of kws ) { let d = await jsonReq ( '/res/search_suggest?q=' + encodeURIComponent ( kw ) ) ; if ( ! Array . isArray ( d ) ) continue ; for ( let v of d ) { let dr = String ( v . dir || 'mv' ) ; if ( tid !== 'home' && tid && dr !== tid ) continue ; let key = dr + String ( v . id || '' ) ; if ( seen . has ( key ) ) continue ; seen . add ( key ) ; arr . push ( vod ( v . title || '' , v . id || '' , dr , v . year || '' , v ) ) } if ( arr . length >= 24 ) break } return arr }
38+ function renderTabs ( ) { tabs . innerHTML = `<button class="tab ${ state . tid === 'home' ?'active' :'' } " data-id="home">首页</button>` + CLASSES . map ( x => `<button class="tab ${ state . tid === x [ 0 ] ?'active' :'' } " data-id="${ x [ 0 ] } ">${ x [ 1 ] } </button>` ) . join ( '' ) ; tabs . querySelectorAll ( '.tab' ) . forEach ( b => b . onclick = ( ) => { state . tid = b . dataset . id ; state . page = 1 ; load ( ) } ) }
39+ function card ( v , i ) { return `<article class="card" data-i="${ i } "><div class="poster">${ v . vod_pic ?`<img loading="lazy" referrerpolicy="no-referrer" src="${ esc ( v . vod_pic ) } ">` :'<div class="noimg">观</div>' } <span class="badge">${ esc ( v . vod_remarks || '资源' ) } </span></div><div class="meta"><div class="name">${ esc ( v . vod_name ) } </div><div class="remark">${ esc ( v . vod_remarks || '点击查看' ) } </div></div></article>` }
40+ function renderGrid ( list , pager ) { state . last = list ; content . innerHTML = list . length ?`<div class="grid">${ list . map ( card ) . join ( '' ) } </div>${ pager ?`<div class="pager"><button id="prev" ${ state . page <= 1 ?'disabled' :'' } >上一页</button><button id="next">下一页</button></div>` :'' } ` :'<div class="empty">暂无内容</div>' ; content . querySelectorAll ( '.card' ) . forEach ( c => c . onclick = ( ) => openDetail ( list [ parseInt ( c . dataset . i ) ] ) ) ; let p = $ ( '#prev' ) , n = $ ( '#next' ) ; if ( p ) p . onclick = ( ) => { if ( state . page > 1 ) { state . page -- ; load ( ) } } ; if ( n ) n . onclick = ( ) => { state . page ++ ; load ( ) } }
41+ async function load ( ) { renderTabs ( ) ; content . innerHTML = '<div class="empty">加载中…</div>' ; try { let list = [ ] ; if ( state . tid === 'home' ) { let h = await req ( '/' ) ; list = listFromInlist ( obj ( h , 'inlist' ) ) . slice ( 0 , 24 ) ; if ( ! list . length ) list = ( await suggest ( 'home' ) ) . slice ( 0 , 24 ) ; renderGrid ( list , false ) ; status . textContent = '首页已加载' } else { if ( state . page === 1 ) { let h = await req ( '/' + state . tid ) ; list = listFromInlist ( obj ( h , 'inlist' ) ) ; let p = obj ( h , 'page' ) || { } ; state . pageCount = parseInt ( p . pages || 100 ) || 100 ; if ( ! list . length ) list = await suggest ( state . tid ) } else { let d = await jsonReq ( `/res/change/${ state . tid } /${ state . page } ` , HOST + '/' + state . tid ) ; list = listFromInlist ( d ) ; if ( ! list . length ) list = await suggest ( state . tid ) } renderGrid ( list , true ) ; status . textContent = `${ ( CLASSES . find ( x => x [ 0 ] === state . tid ) || [ ] ) [ 1 ] } 第 ${ state . page } 页 · ${ list . length } 条` } } catch ( e ) { content . innerHTML = '<div class="empty">加载失败:' + esc ( e . message ) + '</div>' ; status . textContent = '加载失败' } }
42+ async function search ( q ) { q = clean ( q ) ; if ( ! q ) return ; renderTabs ( ) ; content . innerHTML = '<div class="empty">搜索中…</div>' ; try { let h = await req ( '/search?q=' + encodeURIComponent ( q ) ) ; let list = listFromSearch ( obj ( h , 'search' ) ) ; if ( ! list . length ) { let d = await jsonReq ( '/res/search_suggest?q=' + encodeURIComponent ( q ) ) ; list = Array . isArray ( d ) ?d . map ( v => vod ( v . title || '' , v . id || '' , v . dir || 'mv' , v . year || '' , v ) ) :[ ] } renderGrid ( list , false ) ; status . textContent = `搜索:${ q } · ${ list . length } 条` } catch ( e ) { content . innerHTML = '<div class="empty">搜索失败:' + esc ( e . message ) + '</div>' } }
43+ async function openDetail ( v ) { $ ( '#sheet' ) . classList . add ( 'active' ) ; $ ( '#panel' ) . innerHTML = '<div class="empty">读取详情…</div>' ; try { let item = v . _raw || b64d ( v . vod_id ) , dr = item . dir || 'mv' , vid = item . id || '' , h = await req ( `/${ dr } /${ vid } ` ) , d = obj ( h , 'd' ) || item , down = await jsonReq ( `/res/downurl/${ dr } /${ vid } ` , HOST + `/${ dr } /${ vid } ` ) ; let lines = buildLines ( down ) ; currentDetail = { vod :{ vod_id :v . vod_id , vod_name :d . title || item . title || v . vod_name , vod_pic :pic ( dr , vid ) , vod_year :String ( d . year || '' ) , vod_area :Array . isArray ( d . diqu ) ?d . diqu . join ( ' / ' ) :'' , vod_actor :Array . isArray ( d . zhuyan ) ?d . zhuyan . join ( ' / ' ) :'' , vod_content :d . summary || '' , vod_remarks :String ( d . status || v . vod_remarks || '' ) . replace ( / < e m > | < \/ e m > / g, '' ) } , lines} ; let vod = currentDetail . vod ; $ ( '#panel' ) . innerHTML = `<button class="btn" id="closeBtn">返回</button><div class="head"><img class="pic" referrerpolicy="no-referrer" src="${ esc ( vod . vod_pic ) } " onerror="this.style.display='none'"><div><div class="title">${ esc ( vod . vod_name ) } </div><div class="info"><div><b>年份:</b>${ esc ( vod . vod_year ) } </div><div><b>地区:</b>${ esc ( vod . vod_area ) } </div><div><b>演员:</b>${ esc ( vod . vod_actor ) } </div><div><b>状态:</b>${ esc ( vod . vod_remarks ) } </div></div></div></div><div class="pill">简介</div><div class="desc">${ esc ( vod . vod_content || '暂无简介' ) } </div><div class="pill">播放线路</div><div class="line-tabs" id="lineTabs">${ lines . map ( ( x , i ) => `<button class="line-tab ${ i === 0 ?'active' :'' } " data-line="${ esc ( x . name ) } ">${ esc ( x . name ) } (${ x . eps . length } )</button>` ) . join ( '' ) } </div><div class="eps" id="eps"></div>` ; $ ( '#closeBtn' ) . onclick = closeDetail ; $ ( '#lineTabs' ) . querySelectorAll ( '.line-tab' ) . forEach ( b => b . onclick = ( ) => { $ ( '#lineTabs' ) . querySelectorAll ( '.line-tab' ) . forEach ( x => x . classList . remove ( 'active' ) ) ; b . classList . add ( 'active' ) ; renderEpisodes ( b . dataset . line ) } ) ; renderEpisodes ( lines [ 0 ] && lines [ 0 ] . name ) } catch ( e ) { $ ( '#panel' ) . innerHTML = '<button class="btn" onclick="window.__gyClose()">返回</button><div class="empty">详情失败:' + esc ( e . message ) + '</div>' } }
44+ function buildLines ( down ) { let pf = [ ] ; if ( ! down || typeof down !== 'object' ) return [ ] ; let pan = down . panlist || { } ; if ( pan . id ) { let groups = { } , tn = pan . tname || [ ] ; ( pan . url || [ ] ) . forEach ( ( u , i ) => { if ( ! u ) return ; let tp = ( pan . type || [ ] ) [ i ] || 0 , name = Number . isInteger ( tp ) && tp < tn . length ?tn [ tp ] :'网盘' , title = ( pan . name || [ ] ) [ i ] || '网盘链接' , pwd = ( pan . p || [ ] ) [ i ] || '' ; u = String ( u ) . replace ( / \\ \/ / g, '/' ) ; if ( pwd && ! / p w d = / . test ( u ) ) u += ( ( u . includes ( '?' ) ?'&' :'?' ) + 'pwd=' + pwd ) ; groups [ name ] = groups [ name ] || [ ] ; groups [ name ] . push ( { name :title . replace ( / [ # \$ ] / g, ' ' ) , url :u , type :'pan' } ) } ) ; Object . keys ( groups ) . forEach ( k => pf . push ( { name :k , eps :groups [ k ] } ) ) } let dl = down . downlist && down . downlist . list || { } ; if ( dl . m ) { let eps = [ ] ; ( dl . m || [ ] ) . slice ( 0 , 80 ) . forEach ( ( m , i ) => { let title = ( dl . t || [ ] ) [ i ] || '磁力' ; eps . push ( { name :title . replace ( / [ # \$ ] / g, ' ' ) , url :'magnet:?xt=urn:btih:' + m + '&dn=' + encodeURIComponent ( title ) , type :'magnet' } ) } ) ; if ( eps . length ) pf . push ( { name :'磁力' , eps} ) } ( down . playlist || [ ] ) . forEach ( line => { if ( ! line || typeof line !== 'object' ) return ; let eps = ( line . list || [ ] ) . map ( ( n , i ) => ( { name :String ( n ) , url :HOST + '/py/' + line . i + '/' + ( i + 1 ) , type :'online' } ) ) ; if ( eps . length ) pf . push ( { name :line . t || '在线' , eps} ) } ) ; return pf }
45+ function renderEpisodes ( line ) { let box = $ ( '#eps' ) , ln = currentDetail && currentDetail . lines . find ( x => x . name === line ) ; let list = ln && ln . eps || [ ] ; box . innerHTML = list . map ( ( x , i ) => `<button class="ep" data-line="${ esc ( line ) } " data-index="${ i } ">${ esc ( x . name ) } </button>` ) . join ( '' ) || '<div class="empty">暂无资源</div>' ; box . querySelectorAll ( '.ep' ) . forEach ( b => b . onclick = ( ) => playFull ( b . dataset . line , parseInt ( b . dataset . index ) , b . textContent ) ) }
46+ async function playFull ( lineName , index , mark ) { let d = currentDetail && currentDetail . vod ; if ( ! d ) return ; let lines = currentDetail . lines || [ ] , ln = lines . find ( x => x . name === lineName ) || lines [ 0 ] , cur = ln . eps [ index ] || ln . eps [ 0 ] ; if ( ! cur ) return ; let from = [ ] , urls = [ ] ; lines . forEach ( l => { from . push ( l . name ) ; urls . push ( ( l . eps || [ ] ) . map ( e => `${ e . name } $${ playUrl ( e . url , e . type ) } ` ) . join ( '#' ) ) } ) ; let native = ( ln . eps || [ ] ) . map ( e => ( { name :e . name , url :playUrl ( e . url , e . type ) } ) ) , u = playUrl ( cur . url , cur . type ) ; if ( window . fm && typeof fm . vodInline === 'function' ) { await fm . vodInline ( { vod_id :d . vod_id , vod_name :d . vod_name , vod_pic :d . vod_pic , vod_remarks :d . vod_remarks , vod_content :d . vod_content , vod_play_from :from . join ( '$$$' ) , vod_play_url :urls . join ( '$$$' ) , play_from :lineName , mark :mark || cur . name , episodes :native , url :u } ) ; return } if ( window . fm && fm . play ) { await fm . play ( u , d . vod_name + ' ' + ( mark || cur . name ) ) ; return } location . href = u }
47+ function playUrl ( u , type ) { u = String ( u || '' ) ; if ( type === 'pan' && u . startsWith ( 'http' ) ) return u . startsWith ( 'push://' ) ?u :'push://' + u ; return u }
48+ function closeDetail ( ) { $ ( '#sheet' ) . classList . remove ( 'active' ) }
49+ $ ( '#searchForm' ) . onsubmit = e => { e . preventDefault ( ) ; search ( $ ( '#kw' ) . value ) } ; $ ( '#cfgBtn' ) . onclick = ( ) => { $ ( '#cookieInput' ) . value = cookie ; $ ( '#cfgMask' ) . classList . add ( 'active' ) } ; $ ( '#closeCfg' ) . onclick = ( ) => $ ( '#cfgMask' ) . classList . remove ( 'active' ) ; $ ( '#saveCfg' ) . onclick = ( ) => { cookie = $ ( '#cookieInput' ) . value . trim ( ) ; localStorage . setItem ( 'guanying_cookie' , cookie ) ; $ ( '#cfgMask' ) . classList . remove ( 'active' ) ; load ( ) } ; window . __gyClose = closeDetail ; pow ( ) . finally ( load ) ;
50+ } ) ( ) ;
51+ </ script >
52+ </ body >
53+ </ html >
0 commit comments