Files
tvbox_collect/yinshiyuan18/tv/html/6v.html
T
2026-07-11 04:45:19 +00:00

741 lines
34 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
<title>6v影视 · 高清在线</title>
<style>
:root{--panel-rgb:42,50,58;--panel:rgba(var(--panel-rgb),.62);--panel-soft:rgba(var(--panel-rgb),.44);--panel-strong:rgba(var(--panel-rgb),.86);--line:rgba(255,255,255,.16);--text:#f7fbff;--muted:rgba(247,251,255,.68);--accent:#ff6b35;--accent2:#ffb86b;--bad:#ff5c7a;--ok:#68e39a;--radius:18px;--shadow:0 18px 50px rgba(0,0,0,.28)}
*{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;background:transparent;padding:10px 10px 28px}
.top{position:relative;z-index:1;background:rgba(var(--panel-rgb),0.75);border-radius:20px;padding:24px 16px;margin:10px auto 20px;max-width:800px;display:flex;flex-direction:column;align-items:center;gap:16px;box-shadow:var(--shadow)}
.bar{display:flex;flex-direction:row;align-items:center;justify-content:center;gap:16px;width:100%}
.brand{font-size:28px;font-weight:900;letter-spacing:2px;text-shadow:0 4px 14px rgba(0,0,0,.35);white-space:nowrap}
.brand b{color:var(--accent2)}
.search{flex:1;width:100%;max-width:500px;display:flex;gap:8px;align-items:center;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.12);border-radius:999px;padding:6px 6px 6px 18px;box-shadow:inset 0 1px 0 rgba(255,255,255,.05)}
.search input{flex:1;min-width:0;background:transparent;border:0;outline:0;color:var(--text);font-size:15px}
.search input::placeholder{color:var(--muted)}
button,.btn{border:1px solid var(--line);background:var(--panel-soft);color:var(--text);border-radius:999px;padding:8px 16px;font-size:14px;cursor:pointer;white-space:nowrap}
.btn.primary,button.primary{border-color:rgba(255,107,53,.45);background:linear-gradient(135deg,rgba(255,107,53,.90),rgba(255,184,107,.72));color:#17110d;font-weight:800;padding:10px 20px}
.tabs{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;width:100%;padding:0}
.tab{border-radius:999px;padding:8px 18px;font-size:15px;background:rgba(255,255,255,.08);border:2px solid transparent;color:var(--muted);transition:all 0.2s}
.tab.active{background:transparent;color:var(--text);border-color:rgba(255,184,107,0.8);font-weight:800}
.hero{margin:10px 0 16px;padding:18px;border-radius:var(--radius);background:linear-gradient(135deg,rgba(255,107,53,.28),rgba(42,50,58,.54));border:1px solid var(--line);box-shadow:var(--shadow)}
.hero h1{margin:0 0 8px;font-size:24px}
.hero p{margin:0;color:var(--muted);line-height:1.6}
.status{margin:10px 2px;color:var(--muted);font-size:14px;text-align:center}
.grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}
.card{position:relative;min-width:0;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);box-shadow:0 10px 28px rgba(0,0,0,.22);cursor:pointer;transition:transform 0.2s}
.poster{aspect-ratio:3/4;background:rgba(255,255,255,.08);overflow:hidden;position:relative;padding-bottom:133.33%}
.poster img{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;display:block;filter:saturate(1.04) contrast(1.03)}
.poster .noimg{position:absolute;top:0;left:0;height:100%;width:100%;display:flex;align-items:center;justify-content:center;color:var(--muted);font-size:28px}
.meta{padding:8px 8px 10px;background:linear-gradient(180deg,rgba(18,24,30,.15),rgba(18,24,30,.44))}
.name{font-weight:800;font-size:16px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.remark{margin-top:4px;color:var(--muted);font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.section{margin:16px 0}
.section-title{display:flex;align-items:center;justify-content:space-between;margin:0 2px 10px}
.section-title h2{margin:0;font-size:17px;letter-spacing:.3px}
.pagination{display:flex;justify-content:center;gap:20px;margin:24px 0 12px;padding:12px 8px;background:rgba(0,0,0,0.2);border-radius:48px;flex-wrap:wrap}
.page-btn{padding:8px 24px;background:var(--panel-soft);border:1px solid var(--line);border-radius:40px;font-size:14px;cursor:pointer;transition:all 0.2s;min-width:110px}
.page-btn.disabled{opacity:0.4;cursor:not-allowed;pointer-events:none}
.sheet{position:fixed;top:0;right:0;bottom:0;left:0;z-index:40;display:none;background:transparent;overflow:hidden;width:100%;height:100%}
.sheet.active{display:block}
.sheet-inner{height:100vh;min-height:100vh;padding:12px;background:transparent;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;touch-action:pan-y}
.detail-panel{background:var(--panel);border:1px solid var(--line);border-radius:22px;box-shadow:var(--shadow);padding:14px;margin-bottom:42px;touch-action:pan-y}
.detail-head{display:grid;grid-template-columns:118px 1fr;gap:14px}
.detail-pic{width:118px;height:157px;border-radius:14px;object-fit:cover;background:rgba(255,255,255,.08)}
.detail-title{font-size:22px;font-weight:900;margin:2px 0 8px}
.detail-info{color:var(--muted);line-height:1.65;font-size:14px;min-width:0}
.desc{margin-top:12px;color:rgba(255,255,255,.82);line-height:1.72;max-height:8.6em;overflow:auto}
.line-tabs{display:flex;gap:8px;overflow-x:auto;margin:12px 0 8px;padding-bottom:2px}
.line-tab{border-radius:999px;padding:8px 12px;background:rgba(255,255,255,.08);border:1px solid var(--line);white-space:nowrap}
.line-tab.active{background:rgba(255,107,53,.36);border-color:rgba(255,184,107,.55);font-weight:800}
.episodes{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px;max-height:none;overflow:visible;touch-action:pan-y}
.ep{border-radius:12px;padding:9px 11px;background:rgba(255,255,255,.09);border:1px solid var(--line);cursor:pointer;min-width:68px}
.empty{padding:30px 18px;text-align:center;color:var(--muted);background:var(--panel-soft);border:1px dashed var(--line);border-radius:var(--radius)}
.loading{opacity:.75}
.focusable:focus,.card:focus,.rank-item:focus,.ep:focus,button:focus,.tab:focus,input:focus{outline:2px solid rgba(255,184,107,.76);outline-offset:2px;transform:translateY(-1px)}
html.tv-mode .focusable:focus,html.tv-mode .card:focus,html.tv-mode .ep:focus,html.tv-mode button:focus,html.tv-mode .tab:focus{
outline:4px solid var(--accent2) !important;outline-offset:4px !important;
transform:scale(1.05);box-shadow:0 0 20px rgba(255,184,107,.35);
z-index:10;position:relative}
html.tv-mode .card:focus .poster img{transform:scale(1.08)}
html.tv-mode .grid{gap:14px}
html.tv-mode .name{font-size:18px}
html.tv-mode .tab{padding:10px 24px;font-size:17px}
html.tv-mode .ep{padding:12px 18px;font-size:16px;min-width:80px}
html.tv-mode .brand{font-size:34px}
html.tv-mode .search input{font-size:18px}
body.detail-active .app{visibility:hidden;pointer-events:none}
@media (min-width:768px) and (orientation:landscape){.app{padding:18px 28px 44px;max-width:1380px;margin:0 auto}.top{padding:40px;margin:30px auto;border-radius:32px;gap:24px;max-width:900px}.bar{flex-direction:row;gap:24px}.brand{font-size:36px}.search{padding:8px 8px 8px 24px;max-width:650px}.search input{font-size:18px}.btn.primary,button.primary{padding:12px 28px;font-size:16px}.tabs{gap:14px;max-width:700px}.tab{padding:10px 24px;font-size:16px}.grid{grid-template-columns:repeat(6,minmax(0,1fr));gap:16px}.detail-head{grid-template-columns:160px 1fr}.detail-pic{width:160px;height:213px}.focusable:focus,.card:focus,.ep:focus,button:focus,.tab:focus,input:focus{transform:scale(1.05);outline:4px solid #ffb86b;outline-offset:4px}.pagination{gap:28px}.page-btn{padding:10px 32px;font-size:16px}}
@media (min-width:1200px) and (orientation:landscape){.grid{grid-template-columns:repeat(8,minmax(0,1fr))}}
@media (max-width:480px),(orientation:portrait){.app{padding:8px 9px 24px}.top{padding:20px 12px;margin:10px auto;border-radius:20px;gap:16px}.bar{flex-direction:row;gap:10px}.brand{font-size:22px}.search{padding:4px 4px 4px 14px}.search input{font-size:14px}.btn.primary,button.primary{padding:8px 16px;font-size:14px}.tabs{gap:8px}.tab{padding:8px 14px;font-size:14px}.grid{grid-template-columns:repeat(3,minmax(0,1fr));gap:9px}.meta{padding:7px 7px 9px}.name{font-size:13px}.remark{font-size:11px}.detail-head{grid-template-columns:104px 1fr;gap:12px}.detail-pic{width:104px;height:138px}.detail-title{font-size:20px}.sheet-inner{padding:10px}.detail-panel{border-radius:20px;padding:12px}.ep{min-width:64px;padding:8px 9px}.page-btn{padding:6px 16px;min-width:80px}}
</style>
</head>
<body>
<div class="app" id="app">
<div class="top">
<div class="bar">
<div class="brand">6v<b>影视</b></div>
<form class="search" id="searchForm">
<input id="kw" class="focusable" type="text" placeholder="搜索影片、电视剧…" autocomplete="off">
<button class="primary focusable" type="submit">搜索</button>
</form>
</div>
<div class="tabs" id="tabs"></div>
</div>
<div class="status" id="status">准备加载…</div>
<main id="content"></main>
</div>
<div class="sheet" id="detailSheet"><div class="sheet-inner"><div class="detail-panel" id="detailPanel"></div></div></div>
<script>
(function(){
'use strict';
const BASE = 'https://www.xb6v.org';
const CATS = [
['首页', '/'],
['喜剧片', '/xijupian/'],
['动作片', '/dongzuopian/'],
['爱情片', '/aiqingpian/'],
['科幻片', '/kehuanpian/'],
['恐怖片', '/kongbupian/'],
['剧情片', '/juqingpian/'],
['战争片', '/zhanzhengpian/'],
['纪录片', '/jilupian/'],
['动画片', '/donghuapian/'],
['电视剧', '/dianshiju/'],
['综艺', '/ZongYi/']
];
const UA = 'Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 Chrome/120 Mobile Safari/537.36';
let fmReady = false;
let currentUrl = BASE + '/';
let lastFocus = null;
let lastSearchKeyword = '';
const $ = s => document.querySelector(s);
const content = $('#content');
const statusEl = $('#status');
const tabsDiv = $('#tabs');
function detectTV() {
var el = document.documentElement;
if (window.fongmiClient && window.fongmiClient.isLeanback) {
el.classList.add('tv-mode');
return;
}
var fm = window.fm || window.fongmi;
if (fm && fm.device) {
fm.device().then(function(dev) {
if (dev && (Number(dev.type) === 0 || String(dev.mode).toLowerCase() === 'leanback')) {
el.classList.add('tv-mode');
}
}).catch(function(){});
}
}
function markNative() {
if (window.fongmiBridge || window.fm || window.fongmi)
document.documentElement.classList.add('fm-native');
}
function waitFm(ms) {
if (ms === undefined) ms = 1600;
markNative();
if (window.fm) {
fmReady = true;
return Promise.resolve(window.fm);
}
return new Promise(function(resolve) {
var done = false;
var ok = function() {
if (done) return;
done = true;
markNative();
fmReady = !!window.fm;
resolve(window.fm || null);
};
window.addEventListener('fmsdk', ok, { once: true });
setTimeout(ok, ms);
});
}
function abs(url) {
if (!url) return '';
try { return new URL(url, BASE + '/').href; } catch(e) { return url; }
}
function esc(s) {
if (!s) return '';
return String(s).replace(/[&<>'"]/g, function(c) {
if (c === '&') return '&amp;';
if (c === '<') return '&lt;';
if (c === '>') return '&gt;';
if (c === "'") return '&#39;';
if (c === '"') return '&quot;';
return c;
});
}
function clean(s) {
return String(s || '').replace(/\s+/g, ' ').replace(/[\uE000-\uF8FF]/g, '').trim();
}
function imgUrl(u) {
u = abs(u);
if (!u) return '';
try {
if (window.fm && fm.res) return fm.res(u, { headers: { Referer: BASE + '/' } });
} catch(e) {}
return u;
}
// 增强版请求:自动检测编码(GBK/UTF-8)
async function reqText(url, options) {
if (!options) options = {};
url = abs(url);
statusEl.textContent = '加载:' + url;
var headers = Object.assign({
'User-Agent': UA,
'Referer': BASE + '/',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9'
}, options.headers || {});
var lastErr;
async function fetchWithAutoDecode() {
if (window.fm && fm.req) {
try {
var resp = await fm.req(url, {
method: options.method || 'GET',
headers: headers,
responseType: 'arraybuffer',
timeout: options.timeout || 25,
credentials: 'include',
body: options.body
});
if (resp && (resp.ok || resp.status === 200)) {
var buf = resp.body || resp.data;
if (buf) return buf;
}
} catch(e) { lastErr = e; }
}
try {
var r = await fetch(url, {
method: options.method || 'GET',
headers: headers,
cache: 'no-store',
credentials: 'include'
});
if (r.ok) {
return await r.arrayBuffer();
}
} catch(e) { lastErr = e; }
return null;
}
// 优先获取文本(快速)
try {
if (window.fm && fm.req) {
var respText = await fm.req(url, {
method: options.method || 'GET',
headers: headers,
responseType: 'text',
timeout: options.timeout || 25,
credentials: 'include',
body: options.body
});
if (respText && (respText.ok || respText.status === 200)) {
var txt = respText.body || respText.content || respText.data || respText.text || '';
if (txt && !txt.includes('锟斤拷') && !txt.includes('')) return txt;
}
if (String(respText && respText.status) === '520') {
var retryHeaders = Object.assign({}, headers, { Referer: 'https://www.baidu.com/', Accept: 'text/html,*/*' });
var retryUrl = url + (url.indexOf('?') >= 0 ? '&' : '?') + '_t=' + Date.now();
var retryResp = await fm.req(retryUrl, {
method: options.method || 'GET',
headers: retryHeaders,
responseType: 'text',
timeout: 30,
credentials: 'include',
body: options.body
});
if (retryResp && (retryResp.ok || retryResp.status === 200)) {
var txt2 = retryResp.body || retryResp.content || retryResp.data || '';
if (txt2 && !txt2.includes('锟斤拷')) return txt2;
}
}
}
} catch(e) { lastErr = e; }
// 自动解码
var buffer = await fetchWithAutoDecode();
if (buffer) {
var uint8 = new Uint8Array(buffer);
var tempDecoder = new TextDecoder('utf-8');
var headPart = tempDecoder.decode(uint8.slice(0, Math.min(8192, uint8.length)));
var charsetMatch = headPart.match(/<meta[^>]+charset\s*=\s*["']?([a-zA-Z0-9\-]+)["']?/i);
var encoding = 'utf-8';
if (charsetMatch && (charsetMatch[1].toLowerCase() === 'gbk' || charsetMatch[1].toLowerCase() === 'gb2312')) {
encoding = 'gbk';
} else if (headPart.includes('锟斤拷') || headPart.includes('')) {
encoding = 'gbk';
}
try {
var decoder = new TextDecoder(encoding);
var result = decoder.decode(uint8);
if (encoding === 'utf-8' && (result.includes('锟斤拷') || result.includes(''))) {
decoder = new TextDecoder('gbk');
result = decoder.decode(uint8);
}
return result;
} catch(e) { lastErr = e; }
}
try {
var r2 = await fetch(url, { headers: headers, cache: 'no-store', credentials: 'include' });
if (r2.ok) {
return await r2.text();
}
} catch(e) { lastErr = e; }
statusEl.textContent = '加载失败';
throw lastErr || new Error('HTTP 请求全部失败');
}
// 核心:根据URL规律推断下一页和上一页(不依赖HTML)
function inferPaginationFromUrl(url) {
let next = null;
let prev = null;
let matchIndex = url.match(/(\/[^?]*\/)index_(\d+)\.html/);
if (matchIndex) {
let base = matchIndex[1];
let pageNum = parseInt(matchIndex[2], 10);
if (!isNaN(pageNum)) {
next = base + 'index_' + (pageNum + 1) + '.html';
if (pageNum > 2) prev = base + 'index_' + (pageNum - 1) + '.html';
else if (pageNum === 2) prev = base;
return { next, prev };
}
}
let categoryMatch = url.match(/^(.+\/)(?:index\.html)?$/);
if (categoryMatch && !url.includes('index_') && !url.includes('?page=')) {
let base = categoryMatch[1];
next = base + 'index_2.html';
prev = null;
return { next, prev };
}
let pageParamMatch = url.match(/[?&]page=(\d+)/);
if (pageParamMatch) {
let pageNum = parseInt(pageParamMatch[1], 10);
let newUrl = url.replace(/([?&])page=\d+/, (match, prefix) => {
return prefix + 'page=' + (pageNum + 1);
});
next = newUrl;
if (pageNum > 2) {
let prevUrl = url.replace(/([?&])page=\d+/, (match, prefix) => {
return prefix + 'page=' + (pageNum - 1);
});
prev = prevUrl;
} else if (pageNum === 2) {
prev = url.replace(/[?&]page=2/, '').replace(/[?&]$/, '').replace(/\?$/, '');
if (prev.endsWith('?')) prev = prev.slice(0, -1);
}
return { next, prev };
}
let slashPageMatch = url.match(/\/page\/(\d+)(?:\/|$)/);
if (slashPageMatch) {
let pageNum = parseInt(slashPageMatch[1], 10);
let base = url.replace(/\/page\/\d+.*$/, '');
next = base + '/page/' + (pageNum + 1);
if (pageNum > 2) prev = base + '/page/' + (pageNum - 1);
else if (pageNum === 2) prev = base + '/';
return { next, prev };
}
return { next: null, prev: null };
}
function parsePaginationFromHtml(html, currentUrl) {
let nextUrl = null;
let prevUrl = null;
const nextTextMatch = html.match(/<a[^>]+href\s*=\s*["']([^"']+)["'][^>]*>(?:[^<]|<[^>]+>)*下一页(?:[^<]|<[^>]+>)*<\/a>/i);
if (nextTextMatch) nextUrl = nextTextMatch[1];
if (!nextUrl) {
const pageArea = html.match(/<(?:div|ul)[^>]*class\s*=\s*["'][^"']*(?:pages|pagination|page-links)[^"']*["'][^>]*>([\s\S]*?)<\/(?:div|ul)>/i);
if (pageArea) {
const areaHtml = pageArea[1];
const linkRegex = /<a[^>]+href\s*=\s*["']([^"']+)["'][^>]*>([^<]+)<\/a>/gi;
let match;
let links = [];
while ((match = linkRegex.exec(areaHtml)) !== null) {
links.push({ href: match[1], text: match[2].trim() });
}
for (let ln of links) {
if (ln.text === '下一页' || ln.text === '下页' || ln.text === '' || ln.text === '»' || ln.text.toLowerCase() === 'next') {
nextUrl = ln.href;
break;
}
}
if (!nextUrl && links.length > 0) {
let lastLink = links[links.length-1];
if (lastLink.text !== '上一页' && lastLink.text !== '' && lastLink.text !== '«' && lastLink.text.toLowerCase() !== 'prev') {
nextUrl = lastLink.href;
}
}
}
}
const prevTextMatch = html.match(/<a[^>]+href\s*=\s*["']([^"']+)["'][^>]*>(?:[^<]|<[^>]+>)*上一页(?:[^<]|<[^>]+>)*<\/a>/i);
if (prevTextMatch) prevUrl = prevTextMatch[1];
if (!prevUrl) {
const prevClass = html.match(/<a[^>]+class\s*=\s*["'][^"']*prev[^"']*["'][^>]+href\s*=\s*["']([^"']+)["']/i);
if (prevClass) prevUrl = prevClass[1];
}
if (nextUrl && !nextUrl.startsWith('http')) nextUrl = abs(nextUrl);
if (prevUrl && !prevUrl.startsWith('http')) prevUrl = abs(prevUrl);
if (nextUrl === currentUrl || nextUrl === '#' || nextUrl === 'javascript:void(0)') nextUrl = null;
if (prevUrl === currentUrl || prevUrl === '#') prevUrl = null;
return { nextUrl, prevUrl };
}
async function getRealVideoUrl(playPageUrl) {
// 仅保留下载线路,此函数不再被调用(下载线路不走解析),但保留以保持整体结构完整
return null;
}
// 修改后:只保留下载线路,完全移除在线播放线路的解析
async function parseDetailLines(detailUrl) {
try {
var html = await reqText(detailUrl);
var lines = [];
// ========= 仅提取下载地址(原逻辑 保留下载线路) =========
var downloadIdx = html.indexOf('【下载地址】');
if (downloadIdx >= 0) {
var downloadStart = downloadIdx;
var downloadEnd = html.length;
var nextH3 = html.indexOf('<h3', downloadIdx + 6);
var nextWidget = html.indexOf('<div class="widget', downloadIdx + 6);
if (nextH3 > 0 && nextH3 < downloadEnd) downloadEnd = nextH3;
if (nextWidget > 0 && nextWidget < downloadEnd) downloadEnd = nextWidget;
var downloadHtml = html.substring(downloadStart, downloadEnd);
var downRegex = /<a\s+(?:[^>]*?\s+)?href\s*=\s*"([^"]+)"[^>]*>([^<]+)<\/a>/gi;
var downEps = [];
var seenUrls = {};
var m;
while ((m = downRegex.exec(downloadHtml))) {
var downUrl = m[1];
var downTitle = clean(m[2]);
if (seenUrls[downUrl]) continue;
if (downTitle.length < 2 && downUrl.indexOf('magnet:') !== 0) continue;
if (downUrl.indexOf('#respond') >= 0 || downUrl.indexOf('category') >= 0) continue;
seenUrls[downUrl] = true;
if (downTitle === '链接' || downTitle.length < 2 || downUrl.indexOf(downTitle) >= 0) {
if (downUrl.indexOf('quark') > 0) downTitle = '夸克网盘';
else if (downUrl.indexOf('xunlei') > 0) downTitle = '迅雷网盘';
else if (downUrl.indexOf('magnet:') === 0) downTitle = '磁力链接';
else downTitle = '下载';
}
downEps.push({ href: downUrl, title: downTitle, isDownload: true });
}
if (downEps.length > 0) {
lines.push({ sid: String(lines.length + 1), name: '📥 下载地址', eps: downEps });
}
}
// 完全忽略任何在线播放线路(h3播放地址、flatRegex等不再解析)
return lines;
} catch(e) {
console.error('解析详情失败:', e);
return [];
}
}
async function playEpisode(playUrl, title, isDownload) {
if (isDownload) {
statusEl.textContent = '打开:' + title;
if (playUrl.indexOf('magnet:') === 0) {
if (window.fm && fm.play) fm.play(playUrl, title, { referer: BASE });
else window.open(playUrl, '_blank');
} else if (playUrl.indexOf('quark.cn') > 0 || playUrl.indexOf('xunlei.com') > 0 || playUrl.indexOf('pan.baidu.com') > 0) {
if (window.fm && fm.play) {
fm.play('push://' + playUrl, title);
statusEl.textContent = '已推送:' + title;
} else {
window.open(playUrl, '_blank');
}
} else {
if (window.fm && fm.openUrl) fm.openUrl(playUrl);
else window.open(playUrl, '_blank');
}
return;
}
// 由于只保留下载线路,理论上不会进入此分支,但保留原逻辑以防万一
statusEl.textContent = '已忽略在线播放请求';
}
function parseList(html, keyword) {
var items = [];
var postRegex = /<li[^>]*class="[^"]*\bpost\b[^"]*"[^>]*>[\s\S]*?<div class="thumbnail">[\s\S]*?<a[\s\S]*?href\s*=\s*"([^"]+\.html)"[\s\S]*?<img[^>]*src="([^"]+)"[^>]*>[\s\S]*?<h2>[\s\S]*?<a[\s\S]*?href\s*=\s*"[^"]*"[^>]*>([^<]+)<\/a>/gi;
var match;
var seen = {};
while ((match = postRegex.exec(html))) {
var href = match[1];
var img = match[2];
var title = match[3].trim();
if (href.indexOf('/e/') === 0) continue;
if (href === '/' || href === '/index.html') continue;
if (title.length < 2) continue;
var fullUrl = href.startsWith('http') ? href : BASE + href;
if (seen[fullUrl]) continue;
seen[fullUrl] = true;
var badge = '';
var yearMatch = title.match(/\b(19|20)\d{2}\b/);
if (yearMatch) badge = yearMatch[0];
items.push({ href: fullUrl, title: clean(title), remark: badge, image: abs(img) });
}
if (items.length === 0) {
var regex = /<a\s+(?:[^>]*?\s+)?href="([^"]+\.html)"[^>]*>([^<]+)<\/a>/gi;
while ((match = regex.exec(html))) {
var href2 = match[1];
var title2 = match[2].trim();
if (href2.indexOf('/e/') === 0) continue;
if (href2 === '/' || href2 === '/index.html') continue;
if (!href2.match(/\/[a-zA-Z0-9_-]+\/\d+\.html/) && !href2.match(/\/\d+\.html/)) continue;
if (title2.length < 2) continue;
var fullUrl2 = href2.startsWith('http') ? href2 : BASE + href2;
if (seen[fullUrl2]) continue;
seen[fullUrl2] = true;
var badge2 = '';
var yearMatch2 = title2.match(/\b(19|20)\d{2}\b/);
if (yearMatch2) badge2 = yearMatch2[0];
items.push({ href: fullUrl2, title: clean(title2), remark: badge2, image: '' });
}
}
return items;
}
function renderSections(items, keyword, nextUrl, prevUrl) {
if (!items.length) {
content.innerHTML = '<div class="empty">暂无内容</div>';
return;
}
var title = keyword ? '🔍 搜索:' + keyword : '📺 影片列表';
var gridHtml = '\n <section class="section">\n <div class="section-title"><h2>' + esc(title) + '</h2></div>\n <div class="grid">\n ' + items.map(function(it) {
return '\n <article class="card focusable" tabindex="0" data-href="' + esc(it.href) + '">\n <div class="poster">\n ' + (it.image ? '<img loading="lazy" referrerpolicy="no-referrer" src="' + esc(imgUrl(it.image)) + '" alt="' + esc(it.title) + '">' : '<div class="noimg">6v</div>') + '\n </div>\n <div class="meta">\n <div class="name">' + esc(it.title) + '</div>\n <div class="remark">' + esc(it.remark || '点击查看详情') + '</div>\n </div>\n </article>\n ';
}).join('') + '\n </div>\n </section>\n ';
var paginationHtml = '';
if (nextUrl || prevUrl) {
paginationHtml = '<div class="pagination">' +
'<button class="page-btn focusable ' + (!prevUrl ? 'disabled' : '') + '" data-page="prev">◀ 上一页</button>' +
'<button class="page-btn focusable ' + (!nextUrl ? 'disabled' : '') + '" data-page="next">下一页 ▶</button>' +
'</div>';
}
content.innerHTML = gridHtml + paginationHtml;
Array.from(content.querySelectorAll('.card')).forEach(function(card) {
card.onclick = function() { openDetail(card.dataset.href); };
});
if (nextUrl || prevUrl) {
var prevBtn = content.querySelector('.page-btn[data-page="prev"]');
var nextBtn = content.querySelector('.page-btn[data-page="next"]');
if (prevBtn && !prevBtn.classList.contains('disabled')) prevBtn.onclick = function() { if (prevUrl) loadPage(prevUrl); };
if (nextBtn && !nextBtn.classList.contains('disabled')) nextBtn.onclick = function() { if (nextUrl) loadPage(nextUrl); };
}
if (document.documentElement.classList.contains('tv-mode')) {
setTimeout(function() {
var firstCard = content.querySelector('.card');
if (firstCard) firstCard.focus({ preventScroll: true });
}, 100);
}
}
async function loadPage(url) {
url = abs(url);
if (!url) return;
currentUrl = url;
if (!lastSearchKeyword) {
Array.from(tabsDiv.querySelectorAll('.tab')).forEach(function(btn) {
if (abs(btn.dataset.url) === currentUrl) btn.classList.add('active');
else btn.classList.remove('active');
});
} else {
Array.from(tabsDiv.querySelectorAll('.tab')).forEach(function(btn) {
btn.classList.remove('active');
});
}
content.innerHTML = '<div class="empty loading">正在加载内容…</div>';
try {
var html = await reqText(currentUrl);
var items = parseList(html);
let inferred = inferPaginationFromUrl(currentUrl);
let parsed = parsePaginationFromHtml(html, currentUrl);
let finalNext = inferred.next || parsed.nextUrl;
let finalPrev = inferred.prev || parsed.prevUrl;
renderSections(items, lastSearchKeyword || '', finalNext, finalPrev);
statusEl.textContent = '已加载 ' + items.length + ' 条内容';
restoreFocus();
} catch(e) {
content.innerHTML = '<div class="empty">加载失败:' + esc(e.message) + '</div>';
statusEl.textContent = '错误:' + e.message;
}
}
async function search(q) {
q = clean(q);
if (!q) return;
lastSearchKeyword = q;
history.pushState({ route: 'search', q: q }, '', '#search=' + encodeURIComponent(q));
Array.from(tabsDiv.querySelectorAll('.tab')).forEach(function(btn) { btn.classList.remove('active'); });
content.innerHTML = '<div class="empty loading">搜索中…</div>';
var searchUrl = BASE + '/e/search/11index.php';
var postData = 'keyboard=' + encodeURIComponent(q) + '&show=title&tempid=1&tbname=article&mid=1&dopost=search&submit=';
var html;
try {
html = await reqText(searchUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: postData });
} catch(e) {
content.innerHTML = '<div class="empty">搜索失败,请检查网络</div>';
statusEl.textContent = '搜索失败:' + q;
return;
}
var items = parseList(html);
let inferred = inferPaginationFromUrl(searchUrl);
let parsed = parsePaginationFromHtml(html, searchUrl);
let finalNext = inferred.next || parsed.nextUrl;
let finalPrev = inferred.prev || parsed.prevUrl;
currentUrl = searchUrl;
if (!items.length) {
content.innerHTML = '<div class="empty">没有找到相关结果</div>';
statusEl.textContent = '无搜索结果:' + q;
return;
}
renderSections(items, q, finalNext, finalPrev);
statusEl.textContent = '搜索完成:' + q;
}
async function openDetail(detailUrl) {
lastFocus = document.activeElement;
document.body.classList.add('detail-active');
$('#detailSheet').classList.add('active');
$('#detailPanel').innerHTML = '<div class="empty loading">正在读取详情…</div>';
history.pushState({ route: 'detail', url: detailUrl }, '', '#detail=' + encodeURIComponent(detailUrl));
try {
var html = await reqText(detailUrl);
var titleMatch = html.match(/<title>([^<]+)-/);
var title = titleMatch ? titleMatch[1].trim() : '未知影片';
var imgMatch = html.match(/<img[^>]+src="([^"]+\.jpg)"/i);
var image = imgMatch ? imgMatch[1] : '';
var descMatch = html.match(/◎简  介\s*([\s\S]*?)(?:◎|$)/);
var desc = descMatch ? clean(descMatch[1]) : '暂无简介';
var lines = await parseDetailLines(detailUrl);
if (lines.length === 0) {
$('#detailPanel').innerHTML = '<button class="focusable" id="closeDetail">返回</button><div class="empty">未找到下载链接</div>';
$('#closeDetail').onclick = closeDetail;
return;
}
var lineTabsHtml = lines.map(function(ln, idx) { return '<button class="line-tab focusable ' + (idx===0?'active':'') + '" data-sid="' + esc(ln.sid) + '">' + esc(ln.name) + '(' + ln.eps.length + ')</button>'; }).join('');
var panelHtml = '\n <button class="focusable" id="closeDetail">返回</button>\n <div class="detail-head">\n <img class="detail-pic" referrerpolicy="no-referrer" src="' + esc(image) + '" onerror="this.style.display=\'none\'">\n <div>\n <div class="detail-title">' + esc(title) + '</div>\n <div class="detail-info">6v影视资源</div>\n <p class="desc">' + esc(desc) + '</p>\n </div>\n </div>\n <h3>下载线路</h3>\n <div class="line-tabs">' + lineTabsHtml + '</div>\n <div class="episodes" id="episodeBox"></div>\n ';
$('#detailPanel').innerHTML = panelHtml;
$('#closeDetail').onclick = closeDetail;
function renderLine(sid) {
var line = lines.find(function(l) { return l.sid == sid; }) || lines[0];
if (!line) return;
var epHtml = line.eps.map(function(ep) { return '<button class="ep focusable" data-href="' + esc(ep.href) + '"' + (ep.isDownload ? ' data-download="true"' : '') + '>' + esc(ep.title) + '</button>'; }).join('');
$('#episodeBox').innerHTML = epHtml || '<div class="empty">暂无下载项</div>';
Array.from($('#episodeBox').querySelectorAll('.ep')).forEach(function(btn) {
btn.onclick = function() { playEpisode(btn.dataset.href, title + ' ' + btn.textContent, btn.dataset.download === 'true'); };
});
var firstEp = $('#episodeBox').querySelector('.ep');
if (firstEp) firstEp.focus({ preventScroll: true });
}
var lineTabs = $('#detailPanel').querySelectorAll('.line-tab');
lineTabs.forEach(function(tab) {
tab.onclick = function() {
lineTabs.forEach(function(t) { t.classList.remove('active'); });
tab.classList.add('active');
renderLine(tab.dataset.sid);
};
});
renderLine(lines[0].sid);
setTimeout(function() {
var fe = $('#episodeBox') && $('#episodeBox').querySelector('.ep');
if (!fe) $('#closeDetail') && $('#closeDetail').focus();
}, 80);
} catch(e) {
$('#detailPanel').innerHTML = '<button class="focusable" id="closeDetail">返回</button><div class="empty">详情加载失败:' + esc(e.message) + '</div>';
$('#closeDetail').onclick = closeDetail;
}
}
function closeDetail() {
document.body.classList.remove('detail-active');
$('#detailSheet').classList.remove('active');
if (location.hash.startsWith('#detail')) history.back();
if (lastFocus && lastFocus.focus) setTimeout(function() { lastFocus.focus({ preventScroll: true }); }, 50);
}
window.__fyClose = closeDetail;
function renderTabs() {
tabsDiv.innerHTML = CATS.map(function(cat) {
var name = cat[0], path = cat[1];
var url = abs(path);
var active = (url === currentUrl && !lastSearchKeyword) ? 'active' : '';
return '<button class="tab focusable ' + active + '" data-url="' + esc(url) + '">' + esc(name) + '</button>';
}).join('');
Array.from(tabsDiv.querySelectorAll('.tab')).forEach(function(btn) {
btn.onclick = function() {
lastSearchKeyword = '';
loadPage(btn.dataset.url);
};
});
}
function restoreFocus() {
setTimeout(function() {
var el = document.querySelector('.tab.active') || document.querySelector('.card');
if (el) el.focus({ preventScroll: true });
}, 50);
}
function handleKeys(e) {
if (e.key === 'Escape' || e.key === 'Backspace') {
if ($('#detailSheet').classList.contains('active')) {
e.preventDefault();
closeDetail();
return;
}
}
if (e.key === 'Enter' && document.activeElement && document.activeElement.classList.contains('focusable') && document.activeElement.tagName !== 'INPUT') {
document.activeElement.click();
}
}
function boot() {
detectTV();
statusEl.textContent = '正在初始化…';
renderTabs();
$('#searchForm').onsubmit = function(e) { e.preventDefault(); search($('#kw').value); };
document.addEventListener('keydown', handleKeys, true);
window.addEventListener('popstate', function() {
if ($('#detailSheet').classList.contains('active')) closeDetail();
else if (lastSearchKeyword) {
search(lastSearchKeyword);
} else {
loadPage(currentUrl);
}
});
loadPage(BASE + '/');
}
waitFm().then(function() { try { boot(); } catch(e) { statusEl.textContent = '启动失败:' + e.message; } }).catch(function() { boot(); });
})();
</script>
</body>
</html>