637 lines
25 KiB
HTML
637 lines
25 KiB
HTML
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
|
||
<title>PPnix WebHome</title>
|
||
<style>
|
||
* { margin:0; padding:0; box-sizing:border-box; }
|
||
:root {
|
||
--panel-rgb: 45, 55, 68;
|
||
--panel: rgba(var(--panel-rgb), .62);
|
||
--panel-soft: rgba(var(--panel-rgb), .44);
|
||
--panel-strong: rgba(var(--panel-rgb), .82);
|
||
--accent: #e74c3c;
|
||
--text: #e8e8e8;
|
||
--text-dim: #999;
|
||
--line: rgba(255,255,255,.12);
|
||
}
|
||
html, body {
|
||
margin:0; min-height:100%;
|
||
background: transparent;
|
||
color: var(--text);
|
||
font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
|
||
font-size:14px;
|
||
line-height:1.5;
|
||
}
|
||
html:not(.fm-native), html:not(.fm-native) body { background: #1a1e26; }
|
||
a { color:var(--text); text-decoration:none; }
|
||
.header {
|
||
position:sticky; top:0; z-index:100;
|
||
padding:10px 12px;
|
||
background:var(--panel-strong);
|
||
backdrop-filter:blur(8px);
|
||
display:flex; align-items:center; gap:10px;
|
||
border-bottom:1px solid var(--line);
|
||
}
|
||
.header h1 { font-size:18px; font-weight:700; flex-shrink:0; }
|
||
.nav-tabs { display:flex; gap:6px; flex-shrink:0; }
|
||
.nav-tab {
|
||
padding:4px 12px; border-radius:14px; font-size:13px;
|
||
cursor:pointer; background:transparent; color:var(--text-dim);
|
||
transition: all .2s;
|
||
}
|
||
.nav-tab.on { background:var(--accent); color:#fff; }
|
||
.header .search-box {
|
||
flex:1; display:flex; gap:6px; margin-left:auto;
|
||
}
|
||
.header .search-box input {
|
||
flex:1; padding:6px 12px; border-radius:16px; border:1px solid var(--line);
|
||
background:var(--panel); color:var(--text); font-size:13px;
|
||
outline:none; min-width:60px;
|
||
}
|
||
.header .search-box input::placeholder { color:var(--text-dim); }
|
||
.header .search-box button {
|
||
padding:6px 14px; border:none; border-radius:16px;
|
||
background:var(--accent); color:#fff; font-size:13px;
|
||
cursor:pointer; white-space:nowrap;
|
||
}
|
||
.scroll-area { padding:0 12px; }
|
||
.section { margin:12px 0; }
|
||
.section-title {
|
||
font-size:15px; font-weight:600; margin-bottom:8px;
|
||
display:flex; align-items:center; gap:8px;
|
||
}
|
||
.section-title .tab-bar { display:flex; gap:4px; }
|
||
.section-title .tab-item {
|
||
padding:2px 10px; border-radius:10px; font-size:12px;
|
||
cursor:pointer; color:var(--text-dim); font-weight:400;
|
||
}
|
||
.section-title .tab-item.on { background:var(--accent); color:#fff; }
|
||
.section-title .more { margin-left:auto; font-size:12px; color:var(--accent); cursor:pointer; }
|
||
.card-grid {
|
||
display:grid;
|
||
grid-template-columns:repeat(auto-fill, minmax(130px, 1fr));
|
||
gap:10px;
|
||
}
|
||
.card {
|
||
background:var(--panel-soft); border-radius:8px; overflow:hidden;
|
||
cursor:pointer; transition:transform .15s;
|
||
border:1px solid transparent;
|
||
}
|
||
.card:active, .card.focused { transform:scale(0.97); border-color:var(--accent); }
|
||
.card-img {
|
||
width:100%; aspect-ratio:2/3; object-fit:cover;
|
||
display:block; background:var(--panel);
|
||
}
|
||
.card-badge {
|
||
position:absolute; top:4px; right:4px;
|
||
background:rgba(0,0,0,.7); color:#ff6; font-size:11px;
|
||
padding:1px 6px; border-radius:6px; z-index:2;
|
||
}
|
||
.card-body { padding:6px 8px 8px; }
|
||
.card-title { font-size:13px; font-weight:500; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||
.card-meta { font-size:11px; color:var(--text-dim); margin-top:2px; display:flex; align-items:center; gap:4px; }
|
||
.star-bar { color:#f5a623; font-size:11px; }
|
||
.episode-badge {
|
||
position:absolute; bottom:0; left:0; right:0;
|
||
background:linear-gradient(transparent, rgba(0,0,0,.8));
|
||
padding:20px 6px 4px; text-align:right;
|
||
font-size:11px; color:#fff;
|
||
}
|
||
.detail-view {
|
||
position:fixed; inset:0; z-index:200;
|
||
background:transparent; overflow-y:auto;
|
||
display:none;
|
||
}
|
||
.detail-view.active { display:block; }
|
||
body.detail-active .app { visibility:hidden; pointer-events:none; }
|
||
.detail-bg { position:fixed; inset:0; }
|
||
.detail-content { position:relative; z-index:2; padding:12px; }
|
||
.detail-back {
|
||
position:sticky; top:0; z-index:10;
|
||
padding:8px 0; margin-bottom:8px;
|
||
display:flex; align-items:center; gap:8px;
|
||
cursor:pointer; font-size:14px; font-weight:600;
|
||
}
|
||
.detail-poster {
|
||
width:120px; border-radius:8px; float:left; margin:0 12px 12px 0;
|
||
aspect-ratio:2/3; object-fit:cover;
|
||
}
|
||
.detail-info { overflow:hidden; }
|
||
.detail-title { font-size:18px; font-weight:700; margin-bottom:4px; }
|
||
.detail-year { font-size:13px; color:var(--text-dim); margin-bottom:6px; }
|
||
.detail-desc { font-size:12px; color:var(--text-dim); line-height:1.6; margin-bottom:10px; }
|
||
.detail-field { font-size:12px; margin-bottom:3px; }
|
||
.detail-field-label { color:var(--accent); }
|
||
.detail-field-value { color:var(--text-dim); }
|
||
.play-btn {
|
||
display:inline-block; margin:6px 0;
|
||
padding:8px 24px; border:none; border-radius:20px;
|
||
background:var(--accent); color:#fff; font-size:14px;
|
||
cursor:pointer; font-weight:600;
|
||
}
|
||
.episode-grid {
|
||
display:flex; flex-wrap:wrap; gap:6px; margin-top:10px;
|
||
}
|
||
.episode-btn {
|
||
padding:6px 14px; border-radius:14px; border:1px solid var(--line);
|
||
background:var(--panel); color:var(--text); font-size:12px;
|
||
cursor:pointer; transition:all .15s;
|
||
}
|
||
.episode-btn.on { background:var(--accent); border-color:var(--accent); color:#fff; }
|
||
.rank-list { list-style:none; }
|
||
.rank-item {
|
||
display:flex; align-items:center; gap:8px;
|
||
padding:8px 0; border-bottom:1px solid var(--line); cursor:pointer;
|
||
}
|
||
.rank-item:last-child { border-bottom:none; }
|
||
.rank-num {
|
||
width:20px; text-align:center; font-weight:700; font-size:13px;
|
||
color:var(--text-dim); flex-shrink:0;
|
||
}
|
||
.rank-num.top1 { color:#f5a623; }
|
||
.rank-num.top2 { color:#c0c0c0; }
|
||
.rank-num.top3 { color:#cd7f32; }
|
||
.rank-name { flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:13px; }
|
||
.rank-date { font-size:11px; color:var(--text-dim); flex-shrink:0; }
|
||
.loading { text-align:center; padding:40px; color:var(--text-dim); font-size:14px; }
|
||
.refresh-btn {
|
||
width:32px; height:32px; border-radius:50%; border:1px solid var(--line);
|
||
background:var(--panel); color:var(--text); font-size:16px;
|
||
cursor:pointer; display:flex; align-items:center; justify-content:center; flex-shrink:0;
|
||
margin-left:auto;
|
||
}
|
||
.error-msg { color:#ff6; padding:20px; text-align:center; }
|
||
@media (min-width:600px) { .card-grid { grid-template-columns:repeat(auto-fill, minmax(150px, 1fr)); } }
|
||
@media (min-width:900px) { .card-grid { grid-template-columns:repeat(auto-fill, minmax(170px, 1fr)); } }
|
||
::-webkit-scrollbar { width:4px; }
|
||
::-webkit-scrollbar-thumb { background:var(--panel); border-radius:2px; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="app" id="app">
|
||
<div class="header">
|
||
<h1>PPnix</h1>
|
||
<div class="nav-tabs">
|
||
<div class="nav-tab on" data-tab="home">首页</div>
|
||
<div class="nav-tab" data-tab="movie">电影</div>
|
||
<div class="nav-tab" data-tab="tv">电视剧</div>
|
||
</div>
|
||
<div class="search-box">
|
||
<input id="searchInput" placeholder="搜索电影、电视剧" maxlength="30">
|
||
<button id="searchBtn">搜索</button>
|
||
</div>
|
||
</div>
|
||
<div class="scroll-area" id="mainContent">
|
||
<div class="loading">加载中...</div>
|
||
</div>
|
||
</div>
|
||
<div class="detail-view" id="detailView">
|
||
<div class="detail-bg" id="detailBg"></div>
|
||
<div class="detail-content" id="detailContent"></div>
|
||
</div>
|
||
<script>
|
||
var BASE = 'https://www.ppnix.com';
|
||
var LANG = '/cn';
|
||
|
||
function _fm() {
|
||
return new Promise(function (r) {
|
||
if (window.fm) return r(window.fm);
|
||
var t = 0;
|
||
(function c() { if (window.fm) return r(window.fm); if (++t > 30) return r(null); setTimeout(c, 100); })();
|
||
});
|
||
}
|
||
if (window.fongmiBridge || window.fm || window.fongmi)
|
||
document.documentElement.classList.add('fm-native');
|
||
|
||
function _req(url) {
|
||
return _fm().then(function (f) {
|
||
if (f && f.req) return f.req(url, { timeout: 20 });
|
||
return fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' } }).then(function (r) { return r.text(); }).then(function (t) { return { body: t }; });
|
||
}).then(function (res) {
|
||
return (res && res.body != null) ? (typeof res.body === 'string' ? res.body : JSON.stringify(res.body)) : '';
|
||
});
|
||
}
|
||
function _img(url) {
|
||
if (!url) return '';
|
||
try { if (window.fm && fm.res) return fm.res(url, { credentials: 'include' }); } catch (e) {}
|
||
return url;
|
||
}
|
||
function _play(url, title) {
|
||
var fullUrl = url.indexOf('://') > 0 ? url : BASE + url;
|
||
_fm().then(function (f) {
|
||
if (f && f.play) return f.play(fullUrl, title || '', {});
|
||
window.open(fullUrl);
|
||
});
|
||
}
|
||
function _esc(s) { return (s || '').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); }
|
||
|
||
function parseCards(html, isTV) {
|
||
var cards = [];
|
||
var re = /<li>([\s\S]*?)<\/li>/g;
|
||
var m;
|
||
while ((m = re.exec(html)) !== null) {
|
||
var block = m[1];
|
||
var hm = block.match(/href="([^"]+)"/);
|
||
if (!hm) continue;
|
||
var href = hm[1];
|
||
var idm = href.match(/\/(\d+)\.html/);
|
||
if (!idm) continue;
|
||
var id = idm[1];
|
||
var im = block.match(/src="([^"]+)"/);
|
||
var img = im ? im[1] : '';
|
||
var altm = block.match(/alt="([^"]+)"/);
|
||
var title = altm ? altm[1] : '';
|
||
if (!title) {
|
||
var tm = block.match(/<h2>[\s\S]*?<a[^>]*>([^<]+)<\/a>/);
|
||
title = tm ? tm[1].trim() : '';
|
||
}
|
||
var rm = block.match(/class="rate">([^<]+)/);
|
||
var rate = rm ? rm[1] : '';
|
||
var epm = block.match(/<div class="note"><span>([^<]+)<\/span><\/div>/);
|
||
var ep = epm ? epm[1] : '';
|
||
var ym = block.match(/<span class="orange">([^<]+)<\/span>/);
|
||
var year = ym ? ym[1] : '';
|
||
var isTv = href.indexOf('/tv/') > 0;
|
||
cards.push({ id: id, title: title, img: img, rate: rate, ep: ep, year: year, type: isTv ? 'tv' : 'movie', href: href });
|
||
}
|
||
return cards;
|
||
}
|
||
|
||
function loadHome() {
|
||
var el = document.getElementById('mainContent');
|
||
el.innerHTML = '<div class="loading">加载中...</div>';
|
||
_req(BASE + LANG + '/').then(function (html) {
|
||
if (!html) { el.innerHTML = '<div class="error-msg">加载失败</div>'; return; }
|
||
renderHome(html, el);
|
||
});
|
||
}
|
||
|
||
function renderHome(html, el) {
|
||
var h = '';
|
||
var sections = [{ id:'slider', title:'正在上映' }, { id:'slider2', title:'热门电影' }, { id:'slider3', title:'热门电视剧' }];
|
||
for (var si = 0; si < sections.length; si++) {
|
||
var sid = sections[si].id;
|
||
var stitle = sections[si].title;
|
||
var sstart = html.indexOf('id="' + sid + '"');
|
||
if (sstart < 0) continue;
|
||
var sectEnd = html.indexOf('id="slider', sstart + 1);
|
||
if (sectEnd < 0) sectEnd = html.indexOf('<footer class="footer"', sstart);
|
||
if (sectEnd < 0) sectEnd = sstart + 50000;
|
||
var sectHtml = html.substring(sstart, sectEnd);
|
||
var tabs = [];
|
||
var tabHtml = sectHtml.match(/<dl>([\s\S]*?)<\/dl>/);
|
||
if (tabHtml) {
|
||
var tdm = tabHtml[1].matchAll(/<dt[^>]*>([\s\S]*?)<\/dt>/g);
|
||
for (var t of tdm) {
|
||
var tn = t[1].replace(/<[^>]+>/g, '').trim();
|
||
if (tn) tabs.push(tn);
|
||
}
|
||
}
|
||
var lists = [];
|
||
var lre = /<div class="lists-content">([\s\S]*?)<\/div>\s*<\/div>/g;
|
||
var lm;
|
||
while ((lm = lre.exec(sectHtml)) !== null) {
|
||
var ulm = lm[1].match(/<ul>([\s\S]*?)<\/ul>/);
|
||
if (ulm) lists.push(ulm[1]);
|
||
}
|
||
if (lists.length === 0) continue;
|
||
h += '<div class="section">';
|
||
h += '<div class="section-title">';
|
||
h += '<span>' + _esc(stitle) + '</span>';
|
||
if (tabs.length > 0) {
|
||
h += '<div class="tab-bar" data-si="' + si + '">';
|
||
for (var ti = 0; ti < tabs.length; ti++) {
|
||
h += '<span class="tab-item' + (ti === 0 ? ' on' : '') + '" data-ti="' + ti + '">' + _esc(tabs[ti]) + '</span>';
|
||
}
|
||
h += '</div>';
|
||
}
|
||
if (sid === 'slider2') h += '<span class="more" data-go="movie">更多 ></span>';
|
||
else if (sid === 'slider3') h += '<span class="more" data-go="tv">更多 ></span>';
|
||
h += '</div>';
|
||
for (var li = 0; li < lists.length; li++) {
|
||
var cards = parseCards(lists[li], si === 2 || (si === 0 && li > 0));
|
||
var display = li === 0 ? '' : ' style="display:none"';
|
||
h += '<div class="card-grid" data-si="' + si + '" data-ti="' + li + '"' + display + '>';
|
||
for (var ci = 0; ci < cards.length; ci++) {
|
||
h += renderCard(cards[ci]);
|
||
}
|
||
h += '</div>';
|
||
}
|
||
h += '</div>';
|
||
}
|
||
// Rankings
|
||
var rankNames = ['本月排行榜', '电影排行榜', '电视剧排行榜'];
|
||
h += '<div class="section"><div class="section-title"><span>排行榜</span></div>';
|
||
for (var ri = 0; ri < rankNames.length; ri++) {
|
||
var idx = html.indexOf(rankNames[ri]);
|
||
if (idx < 0) continue;
|
||
var chunk = html.substring(idx, idx + 4000);
|
||
var ulm = chunk.match(/<ul>([\s\S]*?)<\/ul>/);
|
||
if (!ulm) continue;
|
||
h += '<div class="section-title" style="font-size:13px;margin-top:8px"><span>' + rankNames[ri] + '</span></div>';
|
||
h += '<ul class="rank-list">';
|
||
var items = ulm[1].match(/<li>([\s\S]*?)<\/li>/g);
|
||
if (items) {
|
||
for (var ii = 0; ii < Math.min(items.length, 10); ii++) {
|
||
var ib = items[ii];
|
||
var a = ib.match(/href="([^"]+)"/);
|
||
var t = ib.match(/title="([^"]+)"/);
|
||
var date = ib.match(/<time>([^<]+)<\/time>/);
|
||
var num = ib.match(/<span>(\d+)<\/span>/);
|
||
var tt = '';
|
||
if (t) tt = t[1];
|
||
else {
|
||
var t2 = ib.match(/<h2>[\s\S]*?<a[^>]*>([^<]+)<\/a>/);
|
||
if (t2) tt = t2[1];
|
||
}
|
||
h += '<li class="rank-item" data-href="' + (a ? _esc(a[1]) : '') + '">';
|
||
var cls = '';
|
||
if (num) { var n = parseInt(num[1]); if (n <= 3) cls = ' top' + n; }
|
||
h += '<span class="rank-num' + cls + '">' + (num ? num[1] : '') + '</span>';
|
||
h += '<span class="rank-name">' + _esc(tt) + '</span>';
|
||
h += '<span class="rank-date">' + (date ? date[1] : '') + '</span>';
|
||
h += '</li>';
|
||
}
|
||
}
|
||
h += '</ul>';
|
||
}
|
||
h += '</div>';
|
||
el.innerHTML = h;
|
||
bindEvents(el);
|
||
}
|
||
|
||
function renderCard(c) {
|
||
var imgUrl = _img(c.img);
|
||
var h = '<div class="card" data-id="' + c.id + '" data-type="' + c.type + '" data-href="' + _esc(c.href) + '">';
|
||
h += '<div style="position:relative">';
|
||
h += '<img class="card-img" src="' + _esc(imgUrl) + '" alt="' + _esc(c.title) + '" loading="lazy" onerror="this.style.display=\'none\'">';
|
||
if (c.ep) h += '<div class="episode-badge">' + _esc(c.ep) + '</div>';
|
||
else if (c.year) h += '<div class="card-badge">' + _esc(c.year) + '</div>';
|
||
h += '</div>';
|
||
h += '<div class="card-body">';
|
||
h += '<div class="card-title">' + _esc(c.title) + '</div>';
|
||
if (c.rate) h += '<div class="card-meta"><span class="star-bar">' + getStars(c.rate) + '</span> ' + _esc(c.rate) + '</div>';
|
||
h += '</div></div>';
|
||
return h;
|
||
}
|
||
|
||
function getStars(r) {
|
||
var v = parseFloat(r);
|
||
if (!v) return '';
|
||
var s = '';
|
||
for (var i = 0; i < 5; i++) s += (i < Math.round(v / 2)) ? '★' : '☆';
|
||
return s;
|
||
}
|
||
|
||
function loadCategory(type, page) {
|
||
page = page || 1;
|
||
var el = document.getElementById('mainContent');
|
||
el.innerHTML = '<div class="loading">加载中...</div>';
|
||
var url = BASE + LANG + '/' + type + '/';
|
||
if (page > 1) url += '---' + (page - 1) + '-.html';
|
||
_req(url).then(function (html) {
|
||
if (!html) { el.innerHTML = '<div class="error-msg">加载失败</div>'; return; }
|
||
renderCategory(html, el, type, page);
|
||
});
|
||
}
|
||
|
||
function renderCategory(html, el, type, page) {
|
||
var ulm = html.match(/<div class="lists-content">\s*<ul>([\s\S]*?)<\/ul>\s*<\/div>/);
|
||
if (!ulm) { el.innerHTML = '<div class="error-msg">没有内容</div>'; return; }
|
||
var cards = parseCards(ulm[1]);
|
||
var h = '<div class="section"><div class="section-title"><span>' + (type === 'movie' ? '电影' : '电视剧') + '</span></div>';
|
||
h += '<div class="card-grid">';
|
||
for (var i = 0; i < cards.length; i++) h += renderCard(cards[i]);
|
||
h += '</div>';
|
||
// Pagination
|
||
var pagem = html.match(/<div class="pagination[^"]*">([\s\S]*?)<\/div>/);
|
||
if (pagem) {
|
||
var pages = [];
|
||
var pm = pagem[1].matchAll(/<a[^>]*href="[^"]*---(\d+)-\.html"[^>]*>(\d+)<\/a>/g);
|
||
for (var p of pm) pages.push(parseInt(p[1]));
|
||
var maxPage = pages.length > 0 ? Math.max.apply(null, pages) : 0;
|
||
h += '<div style="display:flex;gap:6px;justify-content:center;padding:12px 0;flex-wrap:wrap">';
|
||
var start = Math.max(1, page - 4);
|
||
var end = Math.min(maxPage, page + 4);
|
||
if (page > 1) h += '<button class="episode-btn page-btn" data-type="' + type + '" data-page="' + (page - 1) + '">上一页</button>';
|
||
for (var pp = start; pp <= end; pp++) {
|
||
h += '<button class="episode-btn page-btn' + (pp === page ? ' on' : '') + '" data-type="' + type + '" data-page="' + pp + '">' + pp + '</button>';
|
||
}
|
||
if (page < maxPage) h += '<button class="episode-btn page-btn" data-type="' + type + '" data-page="' + (page + 1) + '">下一页</button>';
|
||
h += '</div>';
|
||
}
|
||
h += '</div>';
|
||
el.innerHTML = h;
|
||
bindEvents(el);
|
||
}
|
||
|
||
function doSearch(keyword) {
|
||
if (!keyword.trim()) return;
|
||
var el = document.getElementById('mainContent');
|
||
el.innerHTML = '<div class="loading">搜索中...</div>';
|
||
var url = BASE + LANG + '/search/' + encodeURIComponent(keyword) + '--.html';
|
||
_req(url).then(function (html) {
|
||
if (!html) { el.innerHTML = '<div class="error-msg">搜索失败</div>'; return; }
|
||
var ulm = html.match(/<div class="lists-content">\s*<ul>([\s\S]*?)<\/ul>\s*<\/div>/);
|
||
if (!ulm) {
|
||
el.innerHTML = '<div class="section"><div class="section-title"><span>"' + _esc(keyword) + '" 没有找到结果</span></div></div>';
|
||
return;
|
||
}
|
||
var cards = parseCards(ulm[1]);
|
||
var h = '<div class="section"><div class="section-title"><span>"' + _esc(keyword) + '" 搜索结果 (' + cards.length + ')</span></div>';
|
||
h += '<div class="card-grid">';
|
||
for (var i = 0; i < cards.length; i++) h += renderCard(cards[i]);
|
||
h += '</div></div>';
|
||
el.innerHTML = h;
|
||
bindEvents(el);
|
||
});
|
||
}
|
||
|
||
function loadDetail(href) {
|
||
var el = document.getElementById('detailContent');
|
||
var view = document.getElementById('detailView');
|
||
view.classList.add('active');
|
||
document.body.classList.add('detail-active');
|
||
el.innerHTML = '<div class="loading">加载中...</div>';
|
||
_req(BASE + href).then(function (html) {
|
||
if (!html) { el.innerHTML = '<div class="error-msg">加载失败</div>'; return; }
|
||
renderDetail(html, el, href);
|
||
});
|
||
}
|
||
|
||
function renderDetail(html, el, href) {
|
||
var infoid = 0, m3u8 = [], subs = '', classurl = '';
|
||
var vm = html.match(/infoid\s*=\s*(\d+)/);
|
||
if (vm) infoid = vm[1];
|
||
var mu = html.match(/m3u8\s*=\s*\[([^\]]+)\]/);
|
||
if (mu) {
|
||
var parts = mu[1].split(',').map(function(s) { return s.replace(/['"\s]/g, ''); }).filter(Boolean);
|
||
m3u8 = parts;
|
||
}
|
||
var su = html.match(/sub\s*=\s*'([^']+)'/);
|
||
if (su) subs = su[1];
|
||
var cum = html.match(/classurl\s*=\s*'([^']+)'/);
|
||
if (cum) classurl = cum[1];
|
||
|
||
var poster = '';
|
||
var pm = html.match(/<img[^>]+class="thumb"[^>]+src="([^"]+)"/);
|
||
if (pm) poster = pm[1];
|
||
|
||
var title = '';
|
||
var tm = html.match(/<h1[^>]+class="product-title">([\s\S]*?)<\/h1>/);
|
||
if (tm) {
|
||
title = tm[1].replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();
|
||
}
|
||
|
||
var year = '';
|
||
var ym2 = html.match(/<h1[^>]*>[\s\S]*?<span>\((\d{4})\)<\/span>/);
|
||
if (ym2) year = ym2[1];
|
||
|
||
var rate = '';
|
||
var rm = html.match(/class="rate">([^<]+)/);
|
||
if (rm) rate = rm[1];
|
||
|
||
var director = '', cast = '', genre = '', country = '', aka = '', desc = '';
|
||
var extractSpan = function(label) {
|
||
var re = new RegExp(label + ':<span>([\\s\\S]*?)<\\/span>');
|
||
var m = html.match(re);
|
||
return m ? m[1].replace(/<[^>]+>/g, '').trim() : '';
|
||
};
|
||
director = extractSpan('导演');
|
||
cast = extractSpan('主演');
|
||
genre = extractSpan('类型');
|
||
country = extractSpan('国家');
|
||
aka = extractSpan('又名');
|
||
desc = extractSpan('简介');
|
||
|
||
var isTV = m3u8.length > 1 || (m3u8.length === 1 && m3u8[0] !== '1080P');
|
||
|
||
var h = '';
|
||
h += '<div class="detail-back" id="detailBack"><span>← 返回</span></div>';
|
||
h += '<img class="detail-poster" src="' + _img(poster) + '" alt="' + _esc(title) + '" onerror="this.style.display=\'none\'">';
|
||
h += '<div class="detail-info">';
|
||
h += '<div class="detail-title">' + _esc(title) + '</div>';
|
||
if (year || rate) h += '<div class="detail-year">' + (year ? year : '') + (rate ? ' ' + getStars(rate) + ' ' + rate : '') + '</div>';
|
||
if (director) h += '<div class="detail-field"><span class="detail-field-label">导演:</span><span class="detail-field-value">' + _esc(director) + '</span></div>';
|
||
if (cast) h += '<div class="detail-field"><span class="detail-field-label">主演:</span><span class="detail-field-value">' + _esc(cast) + '</span></div>';
|
||
if (genre) h += '<div class="detail-field"><span class="detail-field-label">类型:</span><span class="detail-field-value">' + _esc(genre) + '</span></div>';
|
||
if (country) h += '<div class="detail-field"><span class="detail-field-value">' + _esc(country) + '</span></div>';
|
||
if (aka) h += '<div class="detail-field"><span class="detail-field-value">又名:' + _esc(aka) + '</span></div>';
|
||
h += '</div><div style="clear:both"></div>';
|
||
if (desc) h += '<div class="detail-desc">' + _esc(desc) + '</div>';
|
||
|
||
// Play button or episode grid
|
||
if (m3u8.length === 1 && !isTV) {
|
||
h += '<button class="play-btn play-action" data-url="/info/m3u8/' + infoid + '/' + m3u8[0] + '.m3u8" data-title="' + _esc(title) + '">▶ 播放</button>';
|
||
}
|
||
if (m3u8.length > 1 || (m3u8.length === 1 && m3u8[0] !== '1080P')) {
|
||
h += '<div style="margin-top:10px"><div style="font-size:13px;font-weight:600;margin-bottom:6px;">选集</div>';
|
||
h += '<div class="episode-grid">';
|
||
for (var ei = 0; ei < m3u8.length; ei++) {
|
||
var ep = m3u8[ei];
|
||
var epLabel = ep;
|
||
if (!isNaN(parseInt(ep)) && parseInt(ep).toString() === ep) {
|
||
epLabel = '第' + ep + '集';
|
||
}
|
||
h += '<button class="episode-btn play-action" data-url="/info/m3u8/' + infoid + '/' + ep + '.m3u8" data-title="' + _esc(title) + ' ' + epLabel + '">' + _esc(epLabel) + '</button>';
|
||
}
|
||
h += '</div></div>';
|
||
}
|
||
|
||
el.innerHTML = h;
|
||
document.getElementById('detailBack').onclick = closeDetail;
|
||
document.getElementById('detailBg').onclick = closeDetail;
|
||
var pbs = el.querySelectorAll('.play-action');
|
||
for (var i = 0; i < pbs.length; i++) {
|
||
pbs[i].onclick = function () {
|
||
_play(this.getAttribute('data-url'), this.getAttribute('data-title'));
|
||
};
|
||
}
|
||
}
|
||
|
||
function closeDetail() {
|
||
document.getElementById('detailView').classList.remove('active');
|
||
document.body.classList.remove('detail-active');
|
||
}
|
||
|
||
function bindEvents(el) {
|
||
var cards = el.querySelectorAll('.card');
|
||
for (var i = 0; i < cards.length; i++) {
|
||
cards[i].onclick = function () {
|
||
var href = this.getAttribute('data-href');
|
||
if (href) loadDetail(href);
|
||
};
|
||
}
|
||
var tabs = el.querySelectorAll('.tab-item');
|
||
for (var i = 0; i < tabs.length; i++) {
|
||
tabs[i].onclick = function () {
|
||
var si = this.getAttribute('data-si');
|
||
var ti = this.getAttribute('data-ti');
|
||
var parent = this.parentNode;
|
||
var children = parent.querySelectorAll('.tab-item');
|
||
for (var j = 0; j < children.length; j++) children[j].classList.remove('on');
|
||
this.classList.add('on');
|
||
var grids = el.querySelectorAll('.card-grid[data-si="' + si + '"]');
|
||
for (var j = 0; j < grids.length; j++) {
|
||
grids[j].style.display = grids[j].getAttribute('data-ti') === ti ? '' : 'none';
|
||
}
|
||
};
|
||
}
|
||
var ranks = el.querySelectorAll('.rank-item');
|
||
for (var i = 0; i < ranks.length; i++) {
|
||
ranks[i].onclick = function () {
|
||
var href = this.getAttribute('data-href');
|
||
if (href) loadDetail(href);
|
||
};
|
||
}
|
||
var mores = el.querySelectorAll('[data-go]');
|
||
for (var i = 0; i < mores.length; i++) {
|
||
mores[i].onclick = function () {
|
||
setTab(this.getAttribute('data-go'));
|
||
};
|
||
}
|
||
var pbs = el.querySelectorAll('.page-btn');
|
||
for (var i = 0; i < pbs.length; i++) {
|
||
pbs[i].onclick = function () {
|
||
loadCategory(this.getAttribute('data-type'), parseInt(this.getAttribute('data-page')));
|
||
};
|
||
}
|
||
}
|
||
|
||
function setTab(tab) {
|
||
var tabs = document.querySelectorAll('.nav-tab');
|
||
for (var i = 0; i < tabs.length; i++) {
|
||
tabs[i].classList.toggle('on', tabs[i].getAttribute('data-tab') === tab);
|
||
}
|
||
if (tab === 'home') loadHome();
|
||
else if (tab === 'movie') loadCategory('movie', 1);
|
||
else if (tab === 'tv') loadCategory('tv', 1);
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', function () {
|
||
var tabs = document.querySelectorAll('.nav-tab');
|
||
for (var i = 0; i < tabs.length; i++) {
|
||
tabs[i].onclick = function () {
|
||
setTab(this.getAttribute('data-tab'));
|
||
};
|
||
}
|
||
document.getElementById('searchBtn').onclick = function () {
|
||
doSearch(document.getElementById('searchInput').value);
|
||
};
|
||
document.getElementById('searchInput').addEventListener('keydown', function (e) {
|
||
if (e.key === 'Enter') doSearch(this.value);
|
||
});
|
||
document.addEventListener('keydown', function (e) {
|
||
if (e.key === 'Escape') closeDetail();
|
||
});
|
||
window.addEventListener('popstate', function () {
|
||
if (document.getElementById('detailView').classList.contains('active')) closeDetail();
|
||
});
|
||
loadHome();
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|