// UI相关函数 function toggleSettings(e) { // 密码保护校验 if (window.isPasswordProtected && window.isPasswordVerified) { if (window.isPasswordProtected() && !window.isPasswordVerified()) { showPasswordModal && showPasswordModal(); return; } } // 阻止事件冒泡,防止触发document的点击事件 e && e.stopPropagation(); const panel = document.getElementById('settingsPanel'); panel.classList.toggle('show'); } // 改进的Toast显示函数 - 支持队列显示多个Toast const toastQueue = []; let isShowingToast = false; function showToast(message, type = 'error') { // 将新的toast添加到队列 toastQueue.push({ message, type }); // 如果当前没有显示中的toast,则开始显示 if (!isShowingToast) { showNextToast(); } } function showNextToast() { if (toastQueue.length === 0) { isShowingToast = false; return; } isShowingToast = true; const { message, type } = toastQueue.shift(); const toast = document.getElementById('toast'); const toastMessage = document.getElementById('toastMessage'); const bgColors = { 'error': 'bg-red-500', 'success': 'bg-green-500', 'info': 'bg-blue-500', 'warning': 'bg-yellow-500' }; const bgColor = bgColors[type] || bgColors.error; toast.className = `fixed top-4 left-1/2 -translate-x-1/2 px-6 py-3 rounded-lg shadow-lg transform transition-all duration-300 ${bgColor} text-white`; toastMessage.textContent = message; // 显示提示 toast.style.opacity = '1'; toast.style.transform = 'translateX(-50%) translateY(0)'; // 3秒后自动隐藏 setTimeout(() => { toast.style.opacity = '0'; toast.style.transform = 'translateX(-50%) translateY(-100%)'; // 等待动画完成后显示下一个toast setTimeout(() => { showNextToast(); }, 300); }, 3000); } // 添加显示/隐藏 loading 的函数 let loadingTimeoutId = null; function showLoading(message = '加载中...') { // 清除任何现有的超时 if (loadingTimeoutId) { clearTimeout(loadingTimeoutId); } const loading = document.getElementById('loading'); const messageEl = loading.querySelector('p'); messageEl.textContent = message; loading.style.display = 'flex'; // 设置30秒后自动关闭loading,防止无限loading loadingTimeoutId = setTimeout(() => { hideLoading(); showToast('操作超时,请稍后重试', 'warning'); }, 30000); } function hideLoading() { // 清除超时 if (loadingTimeoutId) { clearTimeout(loadingTimeoutId); loadingTimeoutId = null; } const loading = document.getElementById('loading'); loading.style.display = 'none'; } function updateSiteStatus(isAvailable) { const statusEl = document.getElementById('siteStatus'); if (isAvailable) { statusEl.innerHTML = ' 可用'; } else { statusEl.innerHTML = ' 不可用'; } } function closeModal() { document.getElementById('modal').classList.add('hidden'); // 清除 iframe 内容 document.getElementById('modalContent').innerHTML = ''; } // 获取搜索历史的增强版本 - 支持新旧格式 function getSearchHistory() { try { const data = localStorage.getItem(SEARCH_HISTORY_KEY); if (!data) return []; const parsed = JSON.parse(data); // 检查是否是数组 if (!Array.isArray(parsed)) return []; // 支持旧格式(字符串数组)和新格式(对象数组) return parsed.map(item => { if (typeof item === 'string') { return { text: item, timestamp: 0 }; } return item; }).filter(item => item && item.text); } catch (e) { console.error('获取搜索历史出错:', e); return []; } } // 保存搜索历史的增强版本 - 添加时间戳和最大数量限制,现在缓存2个月 function saveSearchHistory(query) { if (!query || !query.trim()) return; // 清理输入,防止XSS query = query.trim().substring(0, 50).replace(//g, '>'); let history = getSearchHistory(); // 获取当前时间 const now = Date.now(); // 过滤掉超过2个月的记录(约60天,60*24*60*60*1000 = 5184000000毫秒) history = history.filter(item => typeof item === 'object' && item.timestamp && (now - item.timestamp < 5184000000) ); // 删除已存在的相同项 history = history.filter(item => typeof item === 'object' ? item.text !== query : item !== query ); // 新项添加到开头,包含时间戳 history.unshift({ text: query, timestamp: now }); // 限制历史记录数量 if (history.length > MAX_HISTORY_ITEMS) { history = history.slice(0, MAX_HISTORY_ITEMS); } try { localStorage.setItem(SEARCH_HISTORY_KEY, JSON.stringify(history)); } catch (e) { console.error('保存搜索历史失败:', e); // 如果存储失败(可能是localStorage已满),尝试清理旧数据 try { localStorage.removeItem(SEARCH_HISTORY_KEY); localStorage.setItem(SEARCH_HISTORY_KEY, JSON.stringify(history.slice(0, 3))); } catch (e2) { console.error('再次保存搜索历史失败:', e2); } } renderSearchHistory(); } // 渲染最近搜索历史的增强版本 function renderSearchHistory() { const historyContainer = document.getElementById('recentSearches'); if (!historyContainer) return; const history = getSearchHistory(); if (history.length === 0) { historyContainer.innerHTML = ''; return; } // 创建一个包含标题和清除按钮的行 historyContainer.innerHTML = `
最近搜索:
`; history.forEach(item => { const tag = document.createElement('button'); tag.className = 'search-tag'; tag.textContent = item.text; // 添加时间提示(如果有时间戳) if (item.timestamp) { const date = new Date(item.timestamp); tag.title = `搜索于: ${date.toLocaleString()}`; } tag.onclick = function() { document.getElementById('searchInput').value = item.text; search(); }; historyContainer.appendChild(tag); }); } // 增加清除搜索历史功能 function clearSearchHistory() { // 密码保护校验 if (window.isPasswordProtected && window.isPasswordVerified) { if (window.isPasswordProtected() && !window.isPasswordVerified()) { showPasswordModal && showPasswordModal(); return; } } try { localStorage.removeItem(SEARCH_HISTORY_KEY); renderSearchHistory(); showToast('搜索历史已清除', 'success'); } catch (e) { console.error('清除搜索历史失败:', e); showToast('清除搜索历史失败:', 'error'); } } // 历史面板相关函数 function toggleHistory(e) { // 密码保护校验 if (window.isPasswordProtected && window.isPasswordVerified) { if (window.isPasswordProtected() && !window.isPasswordVerified()) { showPasswordModal && showPasswordModal(); return; } } if (e) e.stopPropagation(); const panel = document.getElementById('historyPanel'); if (panel) { panel.classList.toggle('show'); // 如果打开了历史记录面板,则加载历史数据 if (panel.classList.contains('show')) { loadViewingHistory(); } // 如果设置面板是打开的,则关闭它 const settingsPanel = document.getElementById('settingsPanel'); if (settingsPanel && settingsPanel.classList.contains('show')) { settingsPanel.classList.remove('show'); } } } // 格式化时间戳为友好的日期时间格式 function formatTimestamp(timestamp) { const date = new Date(timestamp); const now = new Date(); const diff = now - date; // 小于1小时,显示"X分钟前" if (diff < 3600000) { const minutes = Math.floor(diff / 60000); return minutes <= 0 ? '刚刚' : `${minutes}分钟前`; } // 小于24小时,显示"X小时前" if (diff < 86400000) { const hours = Math.floor(diff / 3600000); return `${hours}小时前`; } // 小于7天,显示"X天前" if (diff < 604800000) { const days = Math.floor(diff / 86400000); return `${days}天前`; } // 其他情况,显示完整日期 const year = date.getFullYear(); const month = (date.getMonth() + 1).toString().padStart(2, '0'); const day = date.getDate().toString().padStart(2, '0'); const hour = date.getHours().toString().padStart(2, '0'); const minute = date.getMinutes().toString().padStart(2, '0'); return `${year}-${month}-${day} ${hour}:${minute}`; } // 获取观看历史记录 function getViewingHistory() { try { const data = localStorage.getItem('viewingHistory'); return data ? JSON.parse(data) : []; } catch (e) { console.error('获取观看历史失败:', e); return []; } } // 加载观看历史并渲染 function loadViewingHistory() { const historyList = document.getElementById('historyList'); if (!historyList) return; const history = getViewingHistory(); if (history.length === 0) { historyList.innerHTML = `
暂无观看记录
`; return; } // 渲染历史记录 historyList.innerHTML = history.map(item => { // 防止XSS const safeTitle = item.title .replace(//g, '>') .replace(/"/g, '"'); const safeSource = item.sourceName ? item.sourceName.replace(//g, '>').replace(/"/g, '"') : '未知来源'; const episodeText = item.episodeIndex !== undefined ? `第${item.episodeIndex + 1}集` : ''; // 格式化进度信息 let progressHtml = ''; if (item.playbackPosition && item.duration && item.playbackPosition > 10 && item.playbackPosition < item.duration * 0.95) { const percent = Math.round((item.playbackPosition / item.duration) * 100); const formattedTime = formatPlaybackTime(item.playbackPosition); const formattedDuration = formatPlaybackTime(item.duration); progressHtml = `
${formattedTime} / ${formattedDuration}
`; } // 为防止XSS,使用encodeURIComponent编码URL const safeURL = encodeURIComponent(item.url); // 构建历史记录项HTML,添加删除按钮,需要放在position:relative的容器中 return `
${safeTitle}
${episodeText} ${episodeText ? '·' : ''} ${safeSource}
${progressHtml}
${formatTimestamp(item.timestamp)}
`; }).join(''); // 检查是否存在较多历史记录,添加底部边距确保底部按钮不会挡住内容 if (history.length > 5) { historyList.classList.add('pb-4'); } } // 格式化播放时间为 mm:ss 格式 function formatPlaybackTime(seconds) { if (!seconds || isNaN(seconds)) return '00:00'; const minutes = Math.floor(seconds / 60); const remainingSeconds = Math.floor(seconds % 60); return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`; } // 删除单个历史记录项 function deleteHistoryItem(encodedUrl) { try { // 解码URL const url = decodeURIComponent(encodedUrl); // 获取当前历史记录 const history = getViewingHistory(); // 过滤掉要删除的项 const newHistory = history.filter(item => item.url !== url); // 保存回localStorage localStorage.setItem('viewingHistory', JSON.stringify(newHistory)); // 重新加载历史记录显示 loadViewingHistory(); // 显示成功提示 showToast('已删除该记录', 'success'); } catch (e) { console.error('删除历史记录项失败:', e); showToast('删除记录失败', 'error'); } } // 从历史记录播放 function playFromHistory(url, title, episodeIndex, playbackPosition = 0) { try { // 尝试从localStorage获取当前视频的集数信息 let episodesList = []; // 检查viewingHistory,查找匹配的项以获取其集数数据 const historyRaw = localStorage.getItem('viewingHistory'); if (historyRaw) { const history = JSON.parse(historyRaw); // 根据标题查找匹配的历史记录 const historyItem = history.find(item => item.title === title); // 如果找到了匹配的历史记录,尝试获取该条目的集数数据 if (historyItem && historyItem.episodes && Array.isArray(historyItem.episodes)) { episodesList = historyItem.episodes; console.log(`从历史记录找到视频 ${title} 的集数数据:`, episodesList.length); } } // 如果在历史记录中没找到,尝试使用上一个会话的集数数据 if (episodesList.length === 0) { try { const storedEpisodes = JSON.parse(localStorage.getItem('currentEpisodes') || '[]'); if (storedEpisodes.length > 0) { episodesList = storedEpisodes; console.log(`使用localStorage中的集数数据:`, episodesList.length); } } catch (e) { console.error('解析currentEpisodes失败:', e); } } // 将剧集列表保存到localStorage,避免过长的URL if (episodesList.length > 0) { localStorage.setItem('currentEpisodes', JSON.stringify(episodesList)); console.log(`已将剧集列表保存到localStorage,共 ${episodesList.length} 集`); } // 构造带播放进度参数的URL const positionParam = playbackPosition > 10 ? `&position=${Math.floor(playbackPosition)}` : ''; if (url.includes('?')) { // URL已有参数,添加索引和位置参数 let playUrl = url; if (!url.includes('index=') && episodeIndex > 0) { playUrl += `&index=${episodeIndex}`; } if (playbackPosition > 10) { playUrl += positionParam; } window.open(playUrl, '_blank'); } else { // 原始URL,构造player页面链接 const playerUrl = `player.html?url=${encodeURIComponent(url)}&title=${encodeURIComponent(title)}&index=${episodeIndex}${positionParam}`; window.open(playerUrl, '_blank'); } } catch (e) { console.error('从历史记录播放失败:', e); // 回退到原始简单URL const simpleUrl = `player.html?url=${encodeURIComponent(url)}&title=${encodeURIComponent(title)}&index=${episodeIndex}`; window.open(simpleUrl, '_blank'); } } // 添加观看历史 - 确保每个视频标题只有一条记录 function addToViewingHistory(videoInfo) { // 密码保护校验 if (window.isPasswordProtected && window.isPasswordVerified) { if (window.isPasswordProtected() && !window.isPasswordVerified()) { showPasswordModal && showPasswordModal(); return; } } try { const history = getViewingHistory(); // 检查是否已经存在相同标题的记录(同一视频的不同集数) const existingIndex = history.findIndex(item => item.title === videoInfo.title); if (existingIndex !== -1) { // 存在则更新现有记录的集数和时间戳 const existingItem = history[existingIndex]; existingItem.episodeIndex = videoInfo.episodeIndex; existingItem.timestamp = Date.now(); // 确保来源信息保留 if (videoInfo.sourceName && !existingItem.sourceName) { existingItem.sourceName = videoInfo.sourceName; } // 更新播放进度信息,仅当新进度有效且大于10秒时 if (videoInfo.playbackPosition && videoInfo.playbackPosition > 10) { existingItem.playbackPosition = videoInfo.playbackPosition; existingItem.duration = videoInfo.duration || existingItem.duration; } // 更新URL,确保能够跳转到正确的集数 existingItem.url = videoInfo.url; // 重要:确保episodes数据与当前视频匹配 // 只有当videoInfo中包含有效的episodes数据时才更新 if (videoInfo.episodes && Array.isArray(videoInfo.episodes) && videoInfo.episodes.length > 0) { // 如果传入的集数数据与当前保存的不同,则更新 if (!existingItem.episodes || !Array.isArray(existingItem.episodes) || existingItem.episodes.length !== videoInfo.episodes.length) { console.log(`更新 "${videoInfo.title}" 的剧集数据: ${videoInfo.episodes.length}集`); existingItem.episodes = [...videoInfo.episodes]; // 使用深拷贝 } } // 移到最前面 history.splice(existingIndex, 1); history.unshift(existingItem); } else { // 添加新记录到最前面,确保包含剧集数据 const newItem = { ...videoInfo, timestamp: Date.now() }; // 确保episodes字段是一个数组 if (videoInfo.episodes && Array.isArray(videoInfo.episodes)) { newItem.episodes = [...videoInfo.episodes]; // 使用深拷贝 console.log(`保存新视频 "${videoInfo.title}" 的剧集数据: ${videoInfo.episodes.length}集`); } else { // 如果没有提供episodes,初始化为空数组 newItem.episodes = []; } history.unshift(newItem); } // 限制历史记录数量为50条 const maxHistoryItems = 50; if (history.length > maxHistoryItems) { history.splice(maxHistoryItems); } // 保存到本地存储 localStorage.setItem('viewingHistory', JSON.stringify(history)); } catch (e) { console.error('保存观看历史失败:', e); } } // 清空观看历史 function clearViewingHistory() { try { localStorage.removeItem('viewingHistory'); loadViewingHistory(); // 重新加载空的历史记录 showToast('观看历史已清空', 'success'); } catch (e) { console.error('清除观看历史失败:', e); showToast('清除观看历史失败', 'error'); } } // 更新toggleSettings函数以处理历史面板互动 const originalToggleSettings = toggleSettings; toggleSettings = function(e) { if (e) e.stopPropagation(); // 原始设置面板切换逻辑 originalToggleSettings(e); // 如果历史记录面板是打开的,则关闭它 const historyPanel = document.getElementById('historyPanel'); if (historyPanel && historyPanel.classList.contains('show')) { historyPanel.classList.remove('show'); } }; // 点击外部关闭历史面板 document.addEventListener('DOMContentLoaded', function() { document.addEventListener('click', function(e) { const historyPanel = document.getElementById('historyPanel'); const historyButton = document.querySelector('button[onclick="toggleHistory(event)"]'); if (historyPanel && historyButton && !historyPanel.contains(e.target) && !historyButton.contains(e.target) && historyPanel.classList.contains('show')) { historyPanel.classList.remove('show'); } }); });