my / index.html
Finsqw's picture
Add 1 files
5885838 verified
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebDAV音乐播放器</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.progress-container:hover .progress-bar {
height: 8px;
}
.progress-bar {
transition: all 0.3s ease;
}
.waveform {
display: flex;
align-items: flex-end;
height: 40px;
gap: 2px;
}
.wave-bar {
width: 3px;
background-color: #3b82f6;
border-radius: 3px;
transition: height 0.2s ease;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.animate-spin {
animation: spin 2s linear infinite;
}
.album-art {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.glow {
animation: glow 1.5s ease-in-out infinite alternate;
}
@keyframes glow {
from {
box-shadow: 0 0 5px rgba(59, 130, 246, 0.5);
}
to {
box-shadow: 0 0 20px rgba(59, 130, 246, 0.8);
}
}
.connection-pulse {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
opacity: 0.6;
}
50% {
opacity: 1;
}
100% {
opacity: 0.6;
}
}
.connection-error {
animation: shake 0.5s linear;
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
20%, 40%, 60%, 80% { transform: translateX(5px); }
}
.fade-in {
animation: fadeIn 0.5s ease-in;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
</style>
</head>
<body class="bg-gray-100 text-gray-800 font-sans">
<div class="container mx-auto max-w-4xl p-4">
<div class="text-center mb-8">
<h1 class="text-3xl font-bold text-blue-600 mb-2">WebDAV音乐播放器</h1>
<p class="text-gray-600">连接您的WebDAV服务器,随时随地享受音乐</p>
</div>
<!-- 连接面板 -->
<div class="bg-white rounded-xl shadow-lg p-6 mb-6 border border-gray-200">
<h2 class="text-xl font-semibold mb-4 flex items-center">
<i class="fas fa-server mr-2 text-blue-500"></i>
<span>连接设置</span>
</h2>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-4">
<div class="md:col-span-2">
<label class="block text-sm font-medium mb-1">服务器地址</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fas fa-link text-gray-400"></i>
</div>
<input type="text" id="serverUrl" placeholder="https://example.com/webdav"
class="w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
</div>
<div>
<label class="block text-sm font-medium mb-1">用户名</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fas fa-user text-gray-400"></i>
</div>
<input type="text" id="username" placeholder="用户名"
class="w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
</div>
<div>
<label class="block text-sm font-medium mb-1">密码</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fas fa-lock text-gray-400"></i>
</div>
<input type="password" id="password" placeholder="密码"
class="w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
</div>
</div>
<div class="flex justify-between items-center">
<div id="connectionStatus" class="text-sm flex items-center">
<span class="inline-block w-3 h-3 rounded-full bg-gray-400 mr-2"></span>
<span>未连接</span>
</div>
<button id="connectBtn" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors duration-200 flex items-center">
<i class="fas fa-plug mr-2"></i>
<span>连接</span>
</button>
</div>
</div>
<!-- 连接测试面板 -->
<div id="connectionTestPanel" class="bg-blue-50 border-l-4 border-blue-500 text-blue-700 p-4 mb-6 rounded-lg hidden">
<div class="flex items-center">
<i class="fas fa-info-circle mr-2"></i>
<div>
<p class="font-medium">连接测试</p>
<p id="connectionTestMessage" class="text-sm">正在测试连接...</p>
</div>
</div>
</div>
<!-- 错误提示 -->
<div id="errorPanel" class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-6 rounded-lg hidden">
<div class="flex items-center">
<i class="fas fa-exclamation-circle mr-2"></i>
<div>
<p class="font-medium">连接错误</p>
<p id="errorMessage" class="text-sm"></p>
</div>
</div>
</div>
<!-- 音乐播放器 (初始隐藏) -->
<div id="playerPanel" class="hidden">
<div class="bg-white rounded-xl shadow-lg p-6 mb-6 border border-gray-200 fade-in">
<div class="flex justify-between items-center mb-6">
<h2 class="text-xl font-semibold flex items-center">
<i class="fas fa-music mr-2 text-blue-500"></i>
<span>音乐库</span>
</h2>
<div class="flex items-center space-x-2">
<button id="refreshBtn" class="px-3 py-1 bg-gray-100 text-gray-700 rounded-md hover:bg-gray-200 transition-colors duration-200 flex items-center">
<i class="fas fa-sync-alt mr-1"></i>
<span>刷新</span>
</button>
<div class="relative">
<input type="text" id="searchInput" placeholder="搜索音乐..."
class="pl-8 pr-3 py-1 border border-gray-300 rounded-md focus:outline-none focus:ring-1 focus:ring-blue-500">
<div class="absolute inset-y-0 left-0 pl-2 flex items-center pointer-events-none">
<i class="fas fa-search text-gray-400"></i>
</div>
</div>
</div>
</div>
<div id="loadingIndicator" class="text-center py-8 hidden">
<div class="inline-block animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-blue-500 mb-2"></div>
<p class="text-gray-600">正在扫描音乐文件...</p>
</div>
<div id="musicList" class="space-y-2 max-h-96 overflow-y-auto">
<!-- 音乐列表将在这里动态生成 -->
<div class="text-center py-8 text-gray-500">
<i class="fas fa-music fa-2x mb-2"></i>
<p>连接成功后,音乐将显示在这里</p>
</div>
</div>
<div id="playerControls" class="mt-6 bg-gray-50 p-4 rounded-lg hidden">
<div class="flex items-center space-x-4 mb-4">
<div class="flex-shrink-0">
<img id="currentAlbumArt" src="https://via.placeholder.com/80" alt="专辑封面" class="w-16 h-16 rounded-md album-art">
</div>
<div class="flex-grow">
<h3 id="currentSongTitle" class="font-medium">未选择歌曲</h3>
<p id="currentSongArtist" class="text-sm text-gray-600">未知艺术家</p>
</div>
<div class="flex-shrink-0">
<button id="playPauseBtn" class="w-10 h-10 bg-blue-600 text-white rounded-full flex items-center justify-center hover:bg-blue-700 transition-colors duration-200">
<i class="fas fa-play"></i>
</button>
</div>
</div>
<div class="progress-container mb-2">
<div class="flex justify-between text-xs text-gray-500 mb-1">
<span id="currentTime">0:00</span>
<span id="duration">0:00</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-1.5 cursor-pointer">
<div id="progressBar" class="bg-blue-600 h-1.5 rounded-full w-0"></div>
</div>
</div>
<div class="flex justify-between items-center mt-4">
<div class="flex items-center space-x-4">
<button id="prevBtn" class="text-gray-700 hover:text-blue-600 transition-colors duration-200">
<i class="fas fa-step-backward"></i>
</button>
<button id="nextBtn" class="text-gray-700 hover:text-blue-600 transition-colors duration-200">
<i class="fas fa-step-forward"></i>
</button>
</div>
<div class="flex items-center space-x-2">
<button id="volumeBtn" class="text-gray-700 hover:text-blue-600 transition-colors duration-200">
<i class="fas fa-volume-up"></i>
</button>
<input type="range" id="volumeControl" min="0" max="1" step="0.01" value="0.7"
class="w-24 accent-blue-600 cursor-pointer">
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 加载WebDAV客户端库 -->
<script src="https://cdn.jsdelivr.net/npm/webdav@4.11.0/dist/web/index.min.js"></script>
<script>
// 全局变量
let webDAVClient = null;
let isConnected = false;
let retryCount = 0;
const MAX_RETRIES = 2;
let musicFiles = [];
let currentAudio = new Audio();
let currentTrackIndex = -1;
let isPlaying = false;
// DOM元素
const connectBtn = document.getElementById('connectBtn');
const serverUrlInput = document.getElementById('serverUrl');
const usernameInput = document.getElementById('username');
const passwordInput = document.getElementById('password');
const connectionStatus = document.getElementById('connectionStatus');
const errorPanel = document.getElementById('errorPanel');
const errorMessage = document.getElementById('errorMessage');
const connectionTestPanel = document.getElementById('connectionTestPanel');
const connectionTestMessage = document.getElementById('connectionTestMessage');
const playerPanel = document.getElementById('playerPanel');
const musicList = document.getElementById('musicList');
const loadingIndicator = document.getElementById('loadingIndicator');
const playerControls = document.getElementById('playerControls');
const refreshBtn = document.getElementById('refreshBtn');
const searchInput = document.getElementById('searchInput');
const currentAlbumArt = document.getElementById('currentAlbumArt');
const currentSongTitle = document.getElementById('currentSongTitle');
const currentSongArtist = document.getElementById('currentSongArtist');
const playPauseBtn = document.getElementById('playPauseBtn');
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
const volumeBtn = document.getElementById('volumeBtn');
const volumeControl = document.getElementById('volumeControl');
const progressBar = document.getElementById('progressBar');
const currentTime = document.getElementById('currentTime');
const duration = document.getElementById('duration');
// 初始化
document.addEventListener('DOMContentLoaded', () => {
// 尝试从本地存储加载连接信息
const savedConfig = localStorage.getItem('webdavPlayerConfig');
if (savedConfig) {
const config = JSON.parse(savedConfig);
serverUrlInput.value = config.serverUrl || '';
usernameInput.value = config.username || '';
passwordInput.value = config.password || '';
}
// 事件监听器
connectBtn.addEventListener('click', handleConnect);
refreshBtn.addEventListener('click', scanMusicFiles);
searchInput.addEventListener('input', filterMusicList);
playPauseBtn.addEventListener('click', togglePlayPause);
prevBtn.addEventListener('click', playPreviousTrack);
nextBtn.addEventListener('click', playNextTrack);
volumeControl.addEventListener('input', adjustVolume);
volumeBtn.addEventListener('click', toggleMute);
// 进度条点击事件
document.querySelector('.progress-container').addEventListener('click', (e) => {
if (!currentAudio.src) return;
const progressContainer = e.currentTarget;
const clickPosition = e.clientX - progressContainer.getBoundingClientRect().left;
const containerWidth = progressContainer.clientWidth;
const seekPercentage = clickPosition / containerWidth;
const seekTime = seekPercentage * currentAudio.duration;
currentAudio.currentTime = seekTime;
});
// 音频事件监听
currentAudio.addEventListener('timeupdate', updateProgressBar);
currentAudio.addEventListener('ended', playNextTrack);
currentAudio.addEventListener('loadedmetadata', updateDuration);
// 检查WebDAV库是否加载成功
checkWebDAVLibrary();
});
// 检查WebDAV库是否加载成功
function checkWebDAVLibrary() {
if (typeof webdav === 'undefined') {
showError('WebDAV客户端库加载失败,请刷新页面重试');
connectionStatus.classList.add('connection-error');
updateConnectionStatus(false, '库加载失败');
}
}
// 连接处理
async function handleConnect() {
const serverUrl = serverUrlInput.value.trim();
const username = usernameInput.value.trim();
const password = passwordInput.value;
if (!serverUrl) {
showError('请输入服务器地址');
connectionStatus.classList.add('connection-error');
return;
}
// 验证URL格式
try {
new URL(serverUrl);
} catch (e) {
showError('请输入有效的服务器地址 (例如: https://example.com/webdav)');
connectionStatus.classList.add('connection-error');
return;
}
// 保存配置到本地存储
localStorage.setItem('webdavPlayerConfig', JSON.stringify({
serverUrl,
username,
password
}));
// 重置状态
hideError();
connectionStatus.classList.remove('connection-error');
playerPanel.classList.add('hidden');
connectBtn.disabled = true;
connectBtn.innerHTML = '<i class="fas fa-spinner animate-spin mr-2"></i>连接中...';
// 显示连接测试面板
connectionTestPanel.classList.remove('hidden');
connectionTestMessage.textContent = '正在连接到服务器...';
updateConnectionStatus(false, '连接中...', true);
try {
// 检查WebDAV客户端库是否加载
if (typeof webdav === 'undefined') {
throw new Error('WebDAV客户端库加载失败,请刷新页面重试');
}
// 创建客户端
connectionTestMessage.textContent = '正在创建客户端连接...';
webDAVClient = webdav.createClient(serverUrl, {
username,
password,
maxContentLength: 100 * 1024 * 1024 // 100MB
});
// 测试连接
connectionTestMessage.textContent = '正在验证服务器响应...';
const contents = await webDAVClient.getDirectoryContents('/');
// 检查是否有读取权限
if (!contents || !Array.isArray(contents)) {
throw new Error('服务器响应无效,请检查权限');
}
// 连接成功
connectionTestMessage.textContent = '连接成功!正在加载音乐文件...';
isConnected = true;
retryCount = 0;
updateConnectionStatus(true, '连接成功');
// 隐藏测试面板
setTimeout(() => {
connectionTestPanel.classList.add('hidden');
}, 2000);
// 显示播放器面板
playerPanel.classList.remove('hidden');
// 扫描音乐文件
await scanMusicFiles();
} catch (error) {
console.error('连接失败:', error);
isConnected = false;
retryCount++;
// 根据错误类型显示不同的错误信息
let errorMsg = `连接失败: ${error.message}`;
if (error.message.includes('NetworkError')) {
errorMsg = '网络错误,请检查服务器地址和网络连接';
} else if (error.message.includes('401')) {
errorMsg = '认证失败,请检查用户名和密码';
} else if (error.message.includes('404')) {
errorMsg = '服务器路径不存在,请检查WebDAV路径';
} else if (error.message.includes('ECONNREFUSED')) {
errorMsg = '连接被拒绝,服务器可能未运行或端口错误';
}
connectionTestMessage.textContent = '连接失败';
connectionStatus.classList.add('connection-error');
updateConnectionStatus(false, '连接失败');
showError(errorMsg);
playerPanel.classList.add('hidden');
// 自动重试机制
if (retryCount <= MAX_RETRIES) {
setTimeout(() => {
if (!isConnected) {
connectionTestMessage.textContent = `正在尝试重新连接 (${retryCount}/${MAX_RETRIES})...`;
handleConnect();
}
}, 2000);
}
} finally {
connectBtn.disabled = false;
connectBtn.innerHTML = '<i class="fas fa-plug mr-2"></i>连接';
// 如果连接失败,3秒后隐藏测试面板
if (!isConnected && retryCount >= MAX_RETRIES) {
setTimeout(() => {
connectionTestPanel.classList.add('hidden');
}, 3000);
}
}
}
// 扫描音乐文件
async function scanMusicFiles() {
if (!isConnected || !webDAVClient) {
showError('请先连接到WebDAV服务器');
return;
}
try {
// 显示加载指示器
loadingIndicator.classList.remove('hidden');
musicList.innerHTML = '';
// 递归扫描所有目录中的音乐文件
musicFiles = [];
await scanDirectory('/');
// 隐藏加载指示器
loadingIndicator.classList.add('hidden');
if (musicFiles.length === 0) {
musicList.innerHTML = `
<div class="text-center py-8 text-gray-500">
<i class="fas fa-exclamation-circle fa-2x mb-2"></i>
<p>未找到音乐文件</p>
<p class="text-sm mt-2">支持的格式: .mp3, .ogg, .wav, .flac, .m4a</p>
</div>
`;
playerControls.classList.add('hidden');
} else {
renderMusicList();
playerControls.classList.remove('hidden');
}
} catch (error) {
console.error('扫描音乐文件失败:', error);
loadingIndicator.classList.add('hidden');
showError(`扫描音乐文件失败: ${error.message}`);
}
}
// 递归扫描目录
async function scanDirectory(path) {
try {
const contents = await webDAVClient.getDirectoryContents(path);
for (const item of contents) {
if (item.type === 'directory') {
// 如果是目录,递归扫描
await scanDirectory(item.filename);
} else if (isMusicFile(item.filename)) {
// 如果是音乐文件,添加到列表
musicFiles.push({
filename: item.filename,
basename: item.basename,
lastmod: item.lastmod,
size: item.size,
path: path
});
}
}
} catch (error) {
console.error(`扫描目录 ${path} 失败:`, error);
throw error;
}
}
// 检查是否是音乐文件
function isMusicFile(filename) {
const musicExtensions = ['.mp3', '.ogg', '.wav', '.flac', '.m4a'];
return musicExtensions.some(ext => filename.toLowerCase().endsWith(ext));
}
// 渲染音乐列表
function renderMusicList(filterText = '') {
musicList.innerHTML = '';
const filteredFiles = filterText
? musicFiles.filter(file =>
file.basename.toLowerCase().includes(filterText.toLowerCase()))
: musicFiles;
if (filteredFiles.length === 0) {
musicList.innerHTML = `
<div class="text-center py-8 text-gray-500">
<i class="fas fa-search fa-2x mb-2"></i>
<p>没有找到匹配的音乐</p>
</div>
`;
return;
}
filteredFiles.forEach((file, index) => {
const musicItem = document.createElement('div');
musicItem.className = `flex items-center p-3 hover:bg-gray-50 rounded-md cursor-pointer transition-colors duration-200 ${currentTrackIndex === index ? 'bg-blue-50' : ''}`;
musicItem.innerHTML = `
<div class="flex-shrink-0 w-10 h-10 bg-gray-200 rounded-md flex items-center justify-center text-gray-500">
<i class="fas fa-music"></i>
</div>
<div class="ml-3 flex-grow">
<h3 class="text-sm font-medium truncate">${file.basename}</h3>
<p class="text-xs text-gray-500">${formatFileSize(file.size)}${formatDate(file.lastmod)}</p>
</div>
<div class="ml-2 text-gray-400">
<i class="fas fa-play"></i>
</div>
`;
musicItem.addEventListener('click', () => playTrack(index));
musicList.appendChild(musicItem);
});
}
// 过滤音乐列表
function filterMusicList() {
renderMusicList(searchInput.value.trim());
}
// 播放指定曲目
async function playTrack(index) {
if (index < 0 || index >= musicFiles.length) return;
currentTrackIndex = index;
const file = musicFiles[index];
try {
// 更新UI显示当前播放的曲目
renderMusicList(searchInput.value.trim());
// 获取音乐文件的URL
const fileUrl = await webDAVClient.getFileDownloadLink(file.filename);
// 设置音频源
currentAudio.src = fileUrl;
currentAudio.load();
// 更新播放器信息
currentSongTitle.textContent = file.basename;
currentSongArtist.textContent = 'WebDAV音乐';
currentAlbumArt.src = 'https://via.placeholder.com/80';
// 播放音乐
currentAudio.play()
.then(() => {
isPlaying = true;
playPauseBtn.innerHTML = '<i class="fas fa-pause"></i>';
})
.catch(error => {
console.error('播放失败:', error);
showError('播放失败: ' + error.message);
});
} catch (error) {
console.error('获取音乐文件失败:', error);
showError('获取音乐文件失败: ' + error.message);
}
}
// 切换播放/暂停
function togglePlayPause() {
if (!currentAudio.src) {
if (musicFiles.length > 0) {
playTrack(0);
}
return;
}
if (isPlaying) {
currentAudio.pause();
playPauseBtn.innerHTML = '<i class="fas fa-play"></i>';
} else {
currentAudio.play()
.then(() => {
playPauseBtn.innerHTML = '<i class="fas fa-pause"></i>';
})
.catch(error => {
console.error('播放失败:', error);
showError('播放失败: ' + error.message);
});
}
isPlaying = !isPlaying;
}
// 播放上一曲
function playPreviousTrack() {
if (musicFiles.length === 0) return;
let newIndex = currentTrackIndex - 1;
if (newIndex < 0) {
newIndex = musicFiles.length - 1;
}
playTrack(newIndex);
}
// 播放下一曲
function playNextTrack() {
if (musicFiles.length === 0) return;
let newIndex = currentTrackIndex + 1;
if (newIndex >= musicFiles.length) {
newIndex = 0;
}
playTrack(newIndex);
}
// 调整音量
function adjustVolume() {
currentAudio.volume = volumeControl.value;
// 更新音量按钮图标
if (currentAudio.volume === 0) {
volumeBtn.innerHTML = '<i class="fas fa-volume-mute"></i>';
} else if (currentAudio.volume < 0.5) {
volumeBtn.innerHTML = '<i class="fas fa-volume-down"></i>';
} else {
volumeBtn.innerHTML = '<i class="fas fa-volume-up"></i>';
}
}
// 切换静音
function toggleMute() {
if (currentAudio.volume === 0) {
// 如果静音,恢复之前的音量
currentAudio.volume = volumeControl.value;
volumeBtn.innerHTML = currentAudio.volume < 0.5
? '<i class="fas fa-volume-down"></i>'
: '<i class="fas fa-volume-up"></i>';
} else {
// 如果没静音,保存当前音量并静音
volumeControl.value = currentAudio.volume;
currentAudio.volume = 0;
volumeBtn.innerHTML = '<i class="fas fa-volume-mute"></i>';
}
}
// 更新进度条
function updateProgressBar() {
if (!isNaN(currentAudio.duration)) {
const progress = (currentAudio.currentTime / currentAudio.duration) * 100;
progressBar.style.width = `${progress}%`;
currentTime.textContent = formatTime(currentAudio.currentTime);
}
}
// 更新总时长
function updateDuration() {
if (!isNaN(currentAudio.duration)) {
duration.textContent = formatTime(currentAudio.duration);
}
}
// 格式化时间 (秒 -> MM:SS)
function formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs < 10 ? '0' : ''}${secs}`;
}
// 格式化文件大小
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// 格式化日期
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString();
}
// 更新连接状态显示
function updateConnectionStatus(connected, message, isConnecting = false) {
const statusDot = connectionStatus.querySelector('span:first-child');
const statusText = connectionStatus.querySelector('span:last-child');
if (connected) {
statusDot.className = 'inline-block w-3 h-3 rounded-full bg-green-500 mr-2 glow';
statusText.textContent = message;
} else if (isConnecting) {
statusDot.className = 'inline-block w-3 h-3 rounded-full bg-yellow-500 mr-2 connection-pulse';
statusText.textContent = message;
} else {
statusDot.className = 'inline-block w-3 h-3 rounded-full bg-red-500 mr-2';
statusText.textContent = message;
}
}
// 显示错误信息
function showError(message) {
errorMessage.textContent = message;
errorPanel.classList.remove('hidden');
}
// 隐藏错误信息
function hideError() {
errorPanel.classList.add('hidden');
}
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Finsqw/my" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>