// 全局变量
let selectedAPIs = JSON.parse(localStorage.getItem('selectedAPIs') || '["heimuer", "dbzy"]'); // 默认选中黑木耳和豆瓣资源
let customAPIs = JSON.parse(localStorage.getItem('customAPIs') || '[]'); // 存储自定义API列表
// 添加当前播放的集数索引
let currentEpisodeIndex = 0;
// 添加当前视频的所有集数
let currentEpisodes = [];
// 添加当前视频的标题
let currentVideoTitle = '';
// 全局变量用于倒序状态
let episodesReversed = false;
// 页面初始化
document.addEventListener('DOMContentLoaded', function() {
// 初始化API复选框
initAPICheckboxes();
// 初始化自定义API列表
renderCustomAPIsList();
// 初始化显示选中的API数量
updateSelectedApiCount();
// 渲染搜索历史
renderSearchHistory();
// 设置默认API选择(如果是第一次加载)
// 在js/app.js文件中找到类似这样的默认设置代码块
if (!localStorage.getItem('hasInitializedDefaults')) {
// 选择所有API源
selectedAPIs = Object.keys(API_SITES).filter(key => key !== 'aggregated' && key !== 'custom');
localStorage.setItem('selectedAPIs', JSON.stringify(selectedAPIs));
// 默认选中过滤开关 - 修改这部分
localStorage.setItem('yellowFilterEnabled', 'false'); // 改为false,默认关闭黄色内容过滤
localStorage.setItem(PLAYER_CONFIG.adFilteringStorage, 'true'); // 保持true,默认开启分片广告过滤
// 默认开启豆瓣热门
localStorage.setItem('doubanEnabled', 'true'); // 设为true,默认开启豆瓣热门推荐
// 标记已初始化默认值
localStorage.setItem('hasInitializedDefaults', 'true');
}
// 设置黄色内容过滤开关初始状态
const yellowFilterToggle = document.getElementById('yellowFilterToggle');
if (yellowFilterToggle) {
yellowFilterToggle.checked = localStorage.getItem('yellowFilterEnabled') === 'true';
}
// 设置广告过滤开关初始状态
const adFilterToggle = document.getElementById('adFilterToggle');
if (adFilterToggle) {
adFilterToggle.checked = localStorage.getItem(PLAYER_CONFIG.adFilteringStorage) !== 'false'; // 默认为true
}
// 设置事件监听器
setupEventListeners();
// 初始检查成人API选中状态
setTimeout(checkAdultAPIsSelected, 100);
});
// 初始化API复选框
function initAPICheckboxes() {
const container = document.getElementById('apiCheckboxes');
container.innerHTML = '';
// 添加普通API组标题
const normalTitle = document.createElement('div');
normalTitle.className = 'api-group-title';
normalTitle.textContent = '普通资源';
container.appendChild(normalTitle);
// 创建普通API源的复选框
Object.keys(API_SITES).forEach(apiKey => {
const api = API_SITES[apiKey];
if (api.adult) return; // 跳过成人内容API,稍后添加
const checked = selectedAPIs.includes(apiKey);
const checkbox = document.createElement('div');
checkbox.className = 'flex items-center';
checkbox.innerHTML = `
`;
container.appendChild(checkbox);
// 添加事件监听器
checkbox.querySelector('input').addEventListener('change', function() {
updateSelectedAPIs();
checkAdultAPIsSelected();
});
});
// 仅在隐藏设置为false时添加成人API组
if (!HIDE_BUILTIN_ADULT_APIS) {
// 添加成人API组标题
const adultTitle = document.createElement('div');
adultTitle.className = 'api-group-title adult';
adultTitle.innerHTML = `黄色资源采集站
`;
container.appendChild(adultTitle);
// 创建成人API源的复选框
Object.keys(API_SITES).forEach(apiKey => {
const api = API_SITES[apiKey];
if (!api.adult) return; // 仅添加成人内容API
const checked = selectedAPIs.includes(apiKey);
const checkbox = document.createElement('div');
checkbox.className = 'flex items-center';
checkbox.innerHTML = `
`;
container.appendChild(checkbox);
// 添加事件监听器
checkbox.querySelector('input').addEventListener('change', function() {
updateSelectedAPIs();
checkAdultAPIsSelected();
});
});
}
// 初始检查成人内容状态
checkAdultAPIsSelected();
}
// 检查是否有成人API被选中
function checkAdultAPIsSelected() {
// 查找所有内置成人API复选框
const adultBuiltinCheckboxes = document.querySelectorAll('#apiCheckboxes .api-adult:checked');
// 查找所有自定义成人API复选框
const customApiCheckboxes = document.querySelectorAll('#customApisList .api-adult:checked');
const hasAdultSelected = adultBuiltinCheckboxes.length > 0 || customApiCheckboxes.length > 0;
const yellowFilterToggle = document.getElementById('yellowFilterToggle');
const yellowFilterContainer = yellowFilterToggle.closest('div').parentNode;
const filterDescription = yellowFilterContainer.querySelector('p.filter-description');
// 如果选择了成人API,禁用黄色内容过滤器
if (hasAdultSelected) {
yellowFilterToggle.checked = false;
yellowFilterToggle.disabled = true;
localStorage.setItem('yellowFilterEnabled', 'false');
// 添加禁用样式
yellowFilterContainer.classList.add('filter-disabled');
// 修改描述文字
if (filterDescription) {
filterDescription.innerHTML = '选中黄色资源站时无法启用此过滤';
}
// 移除提示信息(如果存在)
const existingTooltip = yellowFilterContainer.querySelector('.filter-tooltip');
if (existingTooltip) {
existingTooltip.remove();
}
} else {
// 启用黄色内容过滤器
yellowFilterToggle.disabled = false;
yellowFilterContainer.classList.remove('filter-disabled');
// 恢复原来的描述文字
if (filterDescription) {
filterDescription.innerHTML = '过滤"伦理片"等黄色内容';
}
// 移除提示信息
const existingTooltip = yellowFilterContainer.querySelector('.filter-tooltip');
if (existingTooltip) {
existingTooltip.remove();
}
}
}
// 渲染自定义API列表
function renderCustomAPIsList() {
const container = document.getElementById('customApisList');
if (!container) return;
if (customAPIs.length === 0) {
container.innerHTML = '
未添加自定义API
';
return;
}
container.innerHTML = '';
customAPIs.forEach((api, index) => {
const apiItem = document.createElement('div');
apiItem.className = 'flex items-center justify-between p-1 mb-1 bg-[#222] rounded';
// 根据是否是成人内容设置不同的样式
const textColorClass = api.isAdult ? 'text-pink-400' : 'text-white';
// 将(18+)标记移到最前面
const adultTag = api.isAdult ? '(18+)' : '';
apiItem.innerHTML = `
`;
container.appendChild(apiItem);
// 添加事件监听器
apiItem.querySelector('input').addEventListener('change', function() {
updateSelectedAPIs();
checkAdultAPIsSelected();
});
});
}
// 编辑自定义API
function editCustomApi(index) {
if (index < 0 || index >= customAPIs.length) return;
const api = customAPIs[index];
// 填充表单数据
const nameInput = document.getElementById('customApiName');
const urlInput = document.getElementById('customApiUrl');
const isAdultInput = document.getElementById('customApiIsAdult');
nameInput.value = api.name;
urlInput.value = api.url;
if (isAdultInput) isAdultInput.checked = api.isAdult || false;
// 显示表单
const form = document.getElementById('addCustomApiForm');
if (form) {
form.classList.remove('hidden');
// 替换表单按钮操作
const buttonContainer = form.querySelector('div:last-child');
buttonContainer.innerHTML = `
`;
}
}
// 更新自定义API
function updateCustomApi(index) {
if (index < 0 || index >= customAPIs.length) return;
const nameInput = document.getElementById('customApiName');
const urlInput = document.getElementById('customApiUrl');
const isAdultInput = document.getElementById('customApiIsAdult');
const name = nameInput.value.trim();
let url = urlInput.value.trim();
const isAdult = isAdultInput ? isAdultInput.checked : false;
if (!name || !url) {
showToast('请输入API名称和链接', 'warning');
return;
}
// 确保URL格式正确
if (!/^https?:\/\/.+/.test(url)) {
showToast('API链接格式不正确,需以http://或https://开头', 'warning');
return;
}
// 移除URL末尾的斜杠
if (url.endsWith('/')) {
url = url.slice(0, -1);
}
// 更新API信息
customAPIs[index] = { name, url, isAdult };
localStorage.setItem('customAPIs', JSON.stringify(customAPIs));
// 重新渲染自定义API列表
renderCustomAPIsList();
// 重新检查成人API选中状态
checkAdultAPIsSelected();
// 恢复添加按钮
restoreAddCustomApiButtons();
// 清空表单并隐藏
nameInput.value = '';
urlInput.value = '';
if (isAdultInput) isAdultInput.checked = false;
document.getElementById('addCustomApiForm').classList.add('hidden');
showToast('已更新自定义API: ' + name, 'success');
}
// 取消编辑自定义API
function cancelEditCustomApi() {
// 清空表单
document.getElementById('customApiName').value = '';
document.getElementById('customApiUrl').value = '';
const isAdultInput = document.getElementById('customApiIsAdult');
if (isAdultInput) isAdultInput.checked = false;
// 隐藏表单
document.getElementById('addCustomApiForm').classList.add('hidden');
// 恢复添加按钮
restoreAddCustomApiButtons();
}
// 恢复自定义API添加按钮
function restoreAddCustomApiButtons() {
const form = document.getElementById('addCustomApiForm');
const buttonContainer = form.querySelector('div:last-child');
buttonContainer.innerHTML = `
`;
}
// 更新选中的API列表
function updateSelectedAPIs() {
// 获取所有内置API复选框
const builtInApiCheckboxes = document.querySelectorAll('#apiCheckboxes input:checked');
// 获取选中的内置API
const builtInApis = Array.from(builtInApiCheckboxes).map(input => input.dataset.api);
// 获取选中的自定义API
const customApiCheckboxes = document.querySelectorAll('#customApisList input:checked');
const customApiIndices = Array.from(customApiCheckboxes).map(input => 'custom_' + input.dataset.customIndex);
// 合并内置和自定义API
selectedAPIs = [...builtInApis, ...customApiIndices];
// 保存到localStorage
localStorage.setItem('selectedAPIs', JSON.stringify(selectedAPIs));
// 更新显示选中的API数量
updateSelectedApiCount();
}
// 更新选中的API数量显示
function updateSelectedApiCount() {
const countEl = document.getElementById('selectedApiCount');
if (countEl) {
countEl.textContent = selectedAPIs.length;
}
}
// 全选或取消全选API
function selectAllAPIs(selectAll = true, excludeAdult = false) {
const checkboxes = document.querySelectorAll('#apiCheckboxes input[type="checkbox"]');
checkboxes.forEach(checkbox => {
if (excludeAdult && checkbox.classList.contains('api-adult')) {
checkbox.checked = false;
} else {
checkbox.checked = selectAll;
}
});
updateSelectedAPIs();
checkAdultAPIsSelected();
}
// 显示添加自定义API表单
function showAddCustomApiForm() {
const form = document.getElementById('addCustomApiForm');
if (form) {
form.classList.remove('hidden');
}
}
// 取消添加自定义API - 修改函数来重用恢复按钮逻辑
function cancelAddCustomApi() {
const form = document.getElementById('addCustomApiForm');
if (form) {
form.classList.add('hidden');
document.getElementById('customApiName').value = '';
document.getElementById('customApiUrl').value = '';
const isAdultInput = document.getElementById('customApiIsAdult');
if (isAdultInput) isAdultInput.checked = false;
// 确保按钮是添加按钮
restoreAddCustomApiButtons();
}
}
// 添加自定义API
function addCustomApi() {
const nameInput = document.getElementById('customApiName');
const urlInput = document.getElementById('customApiUrl');
const isAdultInput = document.getElementById('customApiIsAdult');
const name = nameInput.value.trim();
let url = urlInput.value.trim();
const isAdult = isAdultInput ? isAdultInput.checked : false;
if (!name || !url) {
showToast('请输入API名称和链接', 'warning');
return;
}
// 确保URL格式正确
if (!/^https?:\/\/.+/.test(url)) {
showToast('API链接格式不正确,需以http://或https://开头', 'warning');
return;
}
// 移除URL末尾的斜杠
if (url.endsWith('/')) {
url = url.slice(0, -1);
}
// 添加到自定义API列表 - 增加isAdult属性
customAPIs.push({ name, url, isAdult });
localStorage.setItem('customAPIs', JSON.stringify(customAPIs));
// 默认选中新添加的API
const newApiIndex = customAPIs.length - 1;
selectedAPIs.push('custom_' + newApiIndex);
localStorage.setItem('selectedAPIs', JSON.stringify(selectedAPIs));
// 重新渲染自定义API列表
renderCustomAPIsList();
// 更新选中的API数量
updateSelectedApiCount();
// 重新检查成人API选中状态
checkAdultAPIsSelected();
// 清空表单并隐藏
nameInput.value = '';
urlInput.value = '';
if (isAdultInput) isAdultInput.checked = false;
document.getElementById('addCustomApiForm').classList.add('hidden');
showToast('已添加自定义API: ' + name, 'success');
}
// 移除自定义API
function removeCustomApi(index) {
if (index < 0 || index >= customAPIs.length) return;
const apiName = customAPIs[index].name;
// 从列表中移除API
customAPIs.splice(index, 1);
localStorage.setItem('customAPIs', JSON.stringify(customAPIs));
// 从选中列表中移除此API
const customApiId = 'custom_' + index;
selectedAPIs = selectedAPIs.filter(id => id !== customApiId);
// 更新大于此索引的自定义API索引
selectedAPIs = selectedAPIs.map(id => {
if (id.startsWith('custom_')) {
const currentIndex = parseInt(id.replace('custom_', ''));
if (currentIndex > index) {
return 'custom_' + (currentIndex - 1);
}
}
return id;
});
localStorage.setItem('selectedAPIs', JSON.stringify(selectedAPIs));
// 重新渲染自定义API列表
renderCustomAPIsList();
// 更新选中的API数量
updateSelectedApiCount();
// 重新检查成人API选中状态
checkAdultAPIsSelected();
showToast('已移除自定义API: ' + apiName, 'info');
}
// 设置事件监听器
function setupEventListeners() {
// 回车搜索
document.getElementById('searchInput').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
search();
}
});
// 点击外部关闭设置面板
document.addEventListener('click', function(e) {
const panel = document.getElementById('settingsPanel');
const settingsButton = document.querySelector('button[onclick="toggleSettings(event)"]');
if (!panel.contains(e.target) && !settingsButton.contains(e.target) && panel.classList.contains('show')) {
panel.classList.remove('show');
}
});
// 黄色内容过滤开关事件绑定
const yellowFilterToggle = document.getElementById('yellowFilterToggle');
if (yellowFilterToggle) {
yellowFilterToggle.addEventListener('change', function(e) {
localStorage.setItem('yellowFilterEnabled', e.target.checked);
});
}
// 广告过滤开关事件绑定
const adFilterToggle = document.getElementById('adFilterToggle');
if (adFilterToggle) {
adFilterToggle.addEventListener('change', function(e) {
localStorage.setItem(PLAYER_CONFIG.adFilteringStorage, e.target.checked);
});
}
}
// 重置搜索区域
function resetSearchArea() {
// 清理搜索结果
document.getElementById('results').innerHTML = '';
document.getElementById('searchInput').value = '';
// 恢复搜索区域的样式
document.getElementById('searchArea').classList.add('flex-1');
document.getElementById('searchArea').classList.remove('mb-8');
document.getElementById('resultsArea').classList.add('hidden');
// 确保页脚正确显示,移除相对定位
const footer = document.querySelector('.footer');
if (footer) {
footer.style.position = '';
}
// 如果有豆瓣功能,检查是否需要显示豆瓣推荐区域
if (typeof updateDoubanVisibility === 'function') {
updateDoubanVisibility();
}
}
// 获取自定义API信息
function getCustomApiInfo(customApiIndex) {
const index = parseInt(customApiIndex);
if (isNaN(index) || index < 0 || index >= customAPIs.length) {
return null;
}
return customAPIs[index];
}
// 搜索功能 - 修改为支持多选API
async function search() {
// 密码保护校验
if (window.isPasswordProtected && window.isPasswordVerified) {
if (window.isPasswordProtected() && !window.isPasswordVerified()) {
showPasswordModal && showPasswordModal();
return;
}
}
const query = document.getElementById('searchInput').value.trim();
if (!query) {
showToast('请输入搜索内容', 'info');
return;
}
if (selectedAPIs.length === 0) {
showToast('请至少选择一个API源', 'warning');
return;
}
showLoading();
try {
// 保存搜索历史
saveSearchHistory(query);
// 从所有选中的API源搜索
let allResults = [];
const searchPromises = selectedAPIs.map(async (apiId) => {
try {
let apiUrl, apiName;
// 处理自定义API
if (apiId.startsWith('custom_')) {
const customIndex = apiId.replace('custom_', '');
const customApi = getCustomApiInfo(customIndex);
if (!customApi) return [];
apiUrl = customApi.url + API_CONFIG.search.path + encodeURIComponent(query);
apiName = customApi.name;
} else {
// 内置API
if (!API_SITES[apiId]) return [];
apiUrl = API_SITES[apiId].api + API_CONFIG.search.path + encodeURIComponent(query);
apiName = API_SITES[apiId].name;
}
// 添加超时处理
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 8000);
const response = await fetch(PROXY_URL + encodeURIComponent(apiUrl), {
headers: API_CONFIG.search.headers,
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
return [];
}
const data = await response.json();
if (!data || !data.list || !Array.isArray(data.list) || data.list.length === 0) {
return [];
}
// 添加源信息到每个结果
const results = data.list.map(item => ({
...item,
source_name: apiName,
source_code: apiId,
api_url: apiId.startsWith('custom_') ? getCustomApiInfo(apiId.replace('custom_', ''))?.url : undefined
}));
return results;
} catch (error) {
console.warn(`API ${apiId} 搜索失败:`, error);
return [];
}
});
// 等待所有搜索请求完成
const resultsArray = await Promise.all(searchPromises);
// 合并所有结果
resultsArray.forEach(results => {
if (Array.isArray(results) && results.length > 0) {
allResults = allResults.concat(results);
}
});
// 更新搜索结果计数
const searchResultsCount = document.getElementById('searchResultsCount');
if (searchResultsCount) {
searchResultsCount.textContent = allResults.length;
}
// 显示结果区域,调整搜索区域
document.getElementById('searchArea').classList.remove('flex-1');
document.getElementById('searchArea').classList.add('mb-8');
document.getElementById('resultsArea').classList.remove('hidden');
// 隐藏豆瓣推荐区域(如果存在)
const doubanArea = document.getElementById('doubanArea');
if (doubanArea) {
doubanArea.classList.add('hidden');
}
const resultsDiv = document.getElementById('results');
// 如果没有结果
if (!allResults || allResults.length === 0) {
resultsDiv.innerHTML = `
`;
hideLoading();
return;
}
// 处理搜索结果过滤:如果启用了黄色内容过滤,则过滤掉分类含有敏感内容的项目
const yellowFilterEnabled = localStorage.getItem('yellowFilterEnabled') === 'true';
if (yellowFilterEnabled) {
const banned = ['伦理片','门事件','萝莉少女','制服诱惑','国产传媒','cosplay','黑丝诱惑','无码','日本无码','有码','日本有码','SWAG','网红主播', '色情片','同性片','福利视频','福利片'];
allResults = allResults.filter(item => {
const typeName = item.type_name || '';
return !banned.some(keyword => typeName.includes(keyword));
});
}
// 添加XSS保护,使用textContent和属性转义
resultsDiv.innerHTML = allResults.map(item => {
const safeId = item.vod_id ? item.vod_id.toString().replace(/[^\w-]/g, '') : '';
const safeName = (item.vod_name || '').toString()
.replace(//g, '>')
.replace(/"/g, '"');
const sourceInfo = item.source_name ?
`${item.source_name}` : '';
const sourceCode = item.source_code || '';
// 添加API URL属性,用于详情获取
const apiUrlAttr = item.api_url ?
`data-api-url="${item.api_url.replace(/"/g, '"')}"` : '';
// 更紧凑的卡片布局
const hasCover = item.vod_pic && item.vod_pic.startsWith('http');
return `
${hasCover ? `
` : ''}
${safeName}
${(item.type_name || '').toString().replace(/
${(item.type_name || '').toString().replace(/` : ''}
${(item.vod_year || '') ?
`
${item.vod_year}
` : ''}
${(item.vod_remarks || '暂无介绍').toString().replace(/
${sourceInfo ? `
${sourceInfo}
` : '
'}
`;
}).join('');
} catch (error) {
console.error('搜索错误:', error);
if (error.name === 'AbortError') {
showToast('搜索请求超时,请检查网络连接', 'error');
} else {
showToast('搜索请求失败,请稍后重试', 'error');
}
} finally {
hideLoading();
}
}
// 显示详情 - 修改为支持自定义API
async function showDetails(id, vod_name, sourceCode) {
// 密码保护校验
if (window.isPasswordProtected && window.isPasswordVerified) {
if (window.isPasswordProtected() && !window.isPasswordVerified()) {
showPasswordModal && showPasswordModal();
return;
}
}
if (!id) {
showToast('视频ID无效', 'error');
return;
}
showLoading();
try {
// 构建API参数
let apiParams = '';
// 处理自定义API源
if (sourceCode.startsWith('custom_')) {
const customIndex = sourceCode.replace('custom_', '');
const customApi = getCustomApiInfo(customIndex);
if (!customApi) {
showToast('自定义API配置无效', 'error');
hideLoading();
return;
}
apiParams = '&customApi=' + encodeURIComponent(customApi.url) + '&source=custom';
} else {
// 内置API
apiParams = '&source=' + sourceCode;
}
const response = await fetch('/api/detail?id=' + encodeURIComponent(id) + apiParams);
const data = await response.json();
const modal = document.getElementById('modal');
const modalTitle = document.getElementById('modalTitle');
const modalContent = document.getElementById('modalContent');
// 显示来源信息
const sourceName = data.videoInfo && data.videoInfo.source_name ?
` (${data.videoInfo.source_name})` : '';
// 不对标题进行截断处理,允许完整显示
modalTitle.innerHTML = `${vod_name || '未知视频'}${sourceName}`;
currentVideoTitle = vod_name || '未知视频';
if (data.episodes && data.episodes.length > 0) {
// 安全处理集数URL
const safeEpisodes = data.episodes.map(url => {
try {
// 确保URL是有效的并且是http或https开头
return url && (url.startsWith('http://') || url.startsWith('https://'))
? url.replace(/"/g, '"')
: '';
} catch (e) {
return '';
}
}).filter(url => url); // 过滤掉空URL
// 保存当前视频的所有集数
currentEpisodes = safeEpisodes;
episodesReversed = false; // 默认正序
modalContent.innerHTML = `
${renderEpisodes(vod_name)}
`;
} else {
modalContent.innerHTML = '没有找到可播放的视频
';
}
modal.classList.remove('hidden');
} catch (error) {
console.error('获取详情错误:', error);
showToast('获取详情失败,请稍后重试', 'error');
} finally {
hideLoading();
}
}
// 更新播放视频函数,修改为在新标签页中打开播放页面,并保存到历史记录
function playVideo(url, vod_name, episodeIndex = 0) {
// 密码保护校验
if (window.isPasswordProtected && window.isPasswordVerified) {
if (window.isPasswordProtected() && !window.isPasswordVerified()) {
showPasswordModal && showPasswordModal();
return;
}
}
if (!url) {
showToast('无效的视频链接', 'error');
return;
}
// 获取当前视频来源名称(从模态框标题中提取)
let sourceName = '';
const modalTitle = document.getElementById('modalTitle');
if (modalTitle) {
const sourceSpan = modalTitle.querySelector('span.text-gray-400');
if (sourceSpan) {
// 提取括号内的来源名称, 例如从 "(黑木耳)" 提取 "黑木耳"
const sourceText = sourceSpan.textContent;
const match = sourceText.match(/\(([^)]+)\)/);
if (match && match[1]) {
sourceName = match[1].trim();
}
}
}
// 保存当前状态到localStorage,让播放页面可以获取
const currentVideoTitle = vod_name;
localStorage.setItem('currentVideoTitle', currentVideoTitle);
localStorage.setItem('currentEpisodeIndex', episodeIndex);
localStorage.setItem('currentEpisodes', JSON.stringify(currentEpisodes));
localStorage.setItem('episodesReversed', episodesReversed);
// 构建视频信息对象,使用标题作为唯一标识
const videoTitle = vod_name || currentVideoTitle;
const videoInfo = {
title: videoTitle,
url: url,
episodeIndex: episodeIndex,
sourceName: sourceName,
timestamp: Date.now(),
// 重要:将完整的剧集信息也添加到历史记录中
episodes: currentEpisodes && currentEpisodes.length > 0 ? [...currentEpisodes] : []
};
// 保存到观看历史,添加sourceName
if (typeof addToViewingHistory === 'function') {
addToViewingHistory(videoInfo);
}
// 构建播放页面URL,传递必要参数
const playerUrl = `player.html?url=${encodeURIComponent(url)}&title=${encodeURIComponent(videoTitle)}&index=${episodeIndex}&source=${encodeURIComponent(sourceName)}`;
// 在新标签页中打开播放页面
window.open(playerUrl, '_blank');
}
// 播放上一集
function playPreviousEpisode() {
if (currentEpisodeIndex > 0) {
const prevIndex = currentEpisodeIndex - 1;
const prevUrl = currentEpisodes[prevIndex];
playVideo(prevUrl, currentVideoTitle, prevIndex);
}
}
// 播放下一集
function playNextEpisode() {
if (currentEpisodeIndex < currentEpisodes.length - 1) {
const nextIndex = currentEpisodeIndex + 1;
const nextUrl = currentEpisodes[nextIndex];
playVideo(nextUrl, currentVideoTitle, nextIndex);
}
}
// 处理播放器加载错误
function handlePlayerError() {
hideLoading();
showToast('视频播放加载失败,请尝试其他视频源', 'error');
}
// 辅助函数用于渲染剧集按钮(使用当前的排序状态)
function renderEpisodes(vodName) {
const episodes = episodesReversed ? [...currentEpisodes].reverse() : currentEpisodes;
return episodes.map((episode, index) => {
// 根据倒序状态计算真实的剧集索引
const realIndex = episodesReversed ? currentEpisodes.length - 1 - index : index;
return `
`;
}).join('');
}
// 切换排序状态的函数
function toggleEpisodeOrder() {
episodesReversed = !episodesReversed;
// 重新渲染剧集区域,使用 currentVideoTitle 作为视频标题
const episodesGrid = document.getElementById('episodesGrid');
if (episodesGrid) {
episodesGrid.innerHTML = renderEpisodes(currentVideoTitle);
}
// 更新按钮文本和箭头方向
const toggleBtn = document.querySelector('button[onclick="toggleEpisodeOrder()"]');
if (toggleBtn) {
toggleBtn.querySelector('span').textContent = episodesReversed ? '正序排列' : '倒序排列';
const arrowIcon = toggleBtn.querySelector('svg');
if (arrowIcon) {
arrowIcon.style.transform = episodesReversed ? 'rotate(180deg)' : 'rotate(0deg)';
}
}
}
// app.js 或路由文件中
const authMiddleware = require('./middleware/auth');
const config = require('./config');
// 对所有请求启用鉴权(按需调整作用范围)
if (config.auth.enabled) {
app.use(authMiddleware);
}
// 或者针对特定路由
app.use('/api', authMiddleware);