export default {
async fetch(request, env, ctx) {
// 设置CORS头
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
};
// 处理预检请求
if (request.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
const url = new URL(request.url);
// 登录逻辑
if (url.pathname === '/login' && request.method === 'POST') {
const formData = await request.formData();
const password = formData.get('password');
const adminPassword = env.ADMIN_PASSWORD || 'admin123'; // 从环境变量读取密码,默认为admin123
if (password === adminPassword) {
// 登录成功,设置 Cookie
return new Response('', {
status: 302,
headers: {
'Set-Cookie': `auth=1; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`,
'Location': '/'
}
});
} else {
return new Response(this.renderLoginPage('密码错误,请重试!'), {
headers: {
'Content-Type': 'text/html;charset=utf-8',
...corsHeaders
}
});
}
}
// 退出逻辑
if (url.pathname === '/logout') {
return new Response('', {
status: 302,
headers: {
'Set-Cookie': `auth=; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=0`,
'Location': '/'
}
});
}
// 检查是否已登录
const cookie = request.headers.get('Cookie') || '';
if (!cookie.includes('auth=1')) {
return new Response(this.renderLoginPage(), {
headers: {
'Content-Type': 'text/html;charset=utf-8',
...corsHeaders
}
});
}
// 读取视频列表文件
let links = [];
let randomLink = '';
try {
const videoList1 = await fetch('https://img.boke.us.to/config/9527qita/video-list.txt');
const videoList2 = await fetch('https://img.boke.us.to/config/9527qita/video-list.txt');
const links1 = (await videoList1.text()).split('\n').filter(line => line.trim() !== '');
const links2 = (await videoList2.text()).split('\n').filter(line => line.trim() !== '');
// 合并两个文件的内容
links = [...links1, ...links2];
// 检查文件是否为空
if (links.length === 0) {
return new Response('链接文件为空或未找到!', {
status: 500,
headers: { 'Content-Type': 'text/html;charset=utf-8' }
});
}
// 默认选择第一个链接
randomLink = links[0];
} catch (error) {
// 如果获取视频列表失败,使用默认视频
console.error('获取视频列表失败:', error);
links = ['https://sample-videos.com/zip/10/mp4/SampleVideo_1280x720_1mb.mp4|测试视频'];
randomLink = links[0];
}
// 生成 HTML 内容
const htmlContent = `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>自动随机播放 MP4 视频</title>
<link rel="shortcut icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2280%22>💠</text></svg>">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">
<style>
body {
margin: 0;
padding: 0;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-color: #f0f0f0;
font-family: Arial, sans-serif;
flex-direction: column;
}
/* 按钮样式 */
#playPauseBtn, #prevBtn, #nextBtn, #togglePlaylistBtn {
background-color: black;
color: white;
border: none;
padding: 10px 20px;
margin: 5px;
cursor: pointer;
border-radius: 5px;
transition: background-color 0.3s ease;
}
/* 按钮悬停效果 */
#playPauseBtn:hover, #prevBtn:hover, #nextBtn:hover, #togglePlaylistBtn:hover {
background-color: #333;
}
.sites {
width: 640px;
background: #F2F2F2;
border: 1px solid rgba(0,0,0,.01);
margin: 15px auto;
padding: 0;
}
/* 居中对齐容器 */
.video-container {
display: flex;
justify-content: center;
align-items: center;
margin: -0cm 0 0 0;
}
/* 标题下移 */
h2 {
text-align: center;
margin-bottom: 1cm;
}
/* 播放列表样式 */
.playlist {
list-style-type: none;
padding: 0;
margin: 10px 0 0 0;
width:638px;
max-height: 162px;
overflow-y: auto;
text-align: left;
border: 1px solid #ccc;
background-color: #F2F2F2;
}
.playlist li {
margin: 5px 0;
cursor: pointer;
color: #000000;
font-size: 15px;
text-decoration: underline;
transition: color 0.3s ease;
}
.playlist li:hover {
color: green;
}
/* 定义正在播放的样式 */
.playlist li.playing {
color: red !important;
font-weight: bold;
}
</style>
</head>
<body>
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1cm;">
<h2 style="margin: 0;">自动随机播放 MP4 视频</h2>
<a href="/logout" style="color: #666; text-decoration: none; padding: 8px 12px; border-radius: 6px; transition: all 0.2s; background-color: rgba(0,0,0,0.1);" onmouseover="this.style.backgroundColor='rgba(0,0,0,0.2)'; this.style.color='#333'" onmouseout="this.style.backgroundColor='rgba(0,0,0,0.1)'; this.style.color='#666'" title="退出登录">
<i class="fas fa-sign-out-alt"></i> 退出
</a>
</div>
<div class="video-container">
<video id="videoPlayer" width="640" height="360" controls>
<source src="${randomLink}" type="video/mp4">
您的浏览器不支持视频播放。
</video>
</div>
<!-- 新增按钮区域 -->
<div style="text-align: center; margin: 10px 0;">
<!-- 新增:播放列表按钮 -->
<button id="togglePlaylistBtn">播放列表</button>
<button id="playPauseBtn">▶</button>
<button id="prevBtn">❚◀</button>
<button id="nextBtn">▶❚</button>
</div>
<div class="sites">
<ul class="playlist" id="playlist" style="display: none;">
${links.map((link, index) => {
const [url, title] = link.split('|');
return `<li data-src="${url}">${index + 1}. ${title}</li>`;
}).join('')}
</ul>
</div>
<script>
// 将视频链接列表转换为 JavaScript 数组
const videoLinks = ${JSON.stringify(links)};
// 选择视频元素
const videoPlayer = document.getElementById('videoPlayer');
// 新增按钮功能
const playPauseBtn = document.getElementById('playPauseBtn');
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
// 默认从第一个视频开始播放,并在页面加载时播放
let currentVideoIndex = 0;
// 页面加载时自动播放第一个视频
document.addEventListener('DOMContentLoaded', () => {
playVideo(currentVideoIndex);
// 新增:为当前播放的列表项添加 playing 类
updatePlaylistStyle(currentVideoIndex);
});
// 新增:更新播放列表样式的函数
function updatePlaylistStyle(index) {
const playlistItems = document.querySelectorAll('#playlist li');
playlistItems.forEach((item, i) => {
if (i === index) {
item.classList.add('playing');
} else {
item.classList.remove('playing');
}
});
}
// 播放视频时更新样式
function playVideo(index) {
const [url] = videoLinks[index].split('|');
videoPlayer.src = url;
videoPlayer.play();
// 更新播放列表样式
updatePlaylistStyle(index);
}
// 播放/暂停按钮功能
playPauseBtn.addEventListener('click', () => {
if (videoPlayer.paused) {
videoPlayer.play();
playPauseBtn.textContent = '⏸';
} else {
videoPlayer.pause();
playPauseBtn.textContent = '▶';
}
});
// 上一首按钮功能
prevBtn.addEventListener('click', () => {
currentVideoIndex = (currentVideoIndex - 1 + videoLinks.length) % videoLinks.length;
playVideo(currentVideoIndex);
});
// 下一首按钮功能
nextBtn.addEventListener('click', () => {
currentVideoIndex = (currentVideoIndex + 1) % videoLinks.length;
playVideo(currentVideoIndex);
});
// 视频播放结束时,随机播放下一个视频
videoPlayer.addEventListener('ended', () => {
// 新增:随机选择未播放的视频
const unplayedVideos = videoLinks.filter((_, index) => !playedIndexes.includes(index));
if (unplayedVideos.length === 0) {
// 如果所有视频都已播放过,重置记录
playedIndexes = [];
currentVideoIndex = Math.floor(Math.random() * videoLinks.length);
} else {
// 随机选择一个未播放的视频
const randomIndex = Math.floor(Math.random() * unplayedVideos.length);
currentVideoIndex = videoLinks.indexOf(unplayedVideos[randomIndex]);
}
playVideo(currentVideoIndex);
playedIndexes.push(currentVideoIndex);
});
// 新增:用于记录已播放视频的索引
let playedIndexes = [];
// 新增:播放列表按钮功能
const togglePlaylistBtn = document.getElementById('togglePlaylistBtn');
const playlist = document.getElementById('playlist');
togglePlaylistBtn.addEventListener('click', () => {
if (playlist.style.display === 'none') {
playlist.style.display = 'block';
togglePlaylistBtn.textContent = '隐藏列表';
} else {
playlist.style.display = 'none';
togglePlaylistBtn.textContent = '播放列表';
}
});
// 点击播放列表中的链接后播放选择的视频
document.getElementById('playlist').addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
const selectedUrl = event.target.getAttribute('data-src');
videoPlayer.src = selectedUrl;
videoPlayer.play();
// 更新当前视频索引
currentVideoIndex = videoLinks.findIndex(link => link.split('|')[0] === selectedUrl);
// 更新播放列表样式
updatePlaylistStyle(currentVideoIndex);
}
});
</script>
</body>
</html>
`;
return new Response(htmlContent, {
headers: {
'Content-Type': 'text/html',
...corsHeaders,
},
})
},
// 登录页面渲染函数
renderLoginPage(msg = '') {
return `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>视频播放器 - 登录</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">
<link rel="shortcut icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2280%22>💠</text></svg>">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Noto Sans SC', 'Segoe UI', Arial, sans-serif;
background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
background-size: 400% 400%;
animation: gradient-animation 15s ease infinite;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
}
@keyframes gradient-animation {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
.login-container {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20px);
border-radius: 20px;
padding: 40px 30px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
width: 100%;
max-width: 400px;
text-align: center;
animation: fadeInUp 0.6s ease-out;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.logo {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
margin-bottom: 20px;
color: #333;
}
.logo i {
font-size: 32px;
color: #0F52BA;
}
.logo span {
font-size: 24px;
font-weight: bold;
}
.login-title {
font-size: 22px;
color: #333;
margin-bottom: 10px;
font-weight: 600;
}
.login-subtitle {
color: #666;
margin-bottom: 30px;
font-size: 14px;
}
.error-message {
background: rgba(211, 47, 47, 0.1);
color: #d32f2f;
padding: 12px;
border-radius: 8px;
margin-bottom: 20px;
font-size: 14px;
border: 1px solid rgba(211, 47, 47, 0.2);
}
.form-group {
margin-bottom: 20px;
text-align: left;
}
.form-group label {
display: block;
color: #333;
margin-bottom: 8px;
font-weight: 500;
font-size: 14px;
}
.password-wrapper {
position: relative;
width: 100%;
}
.password-wrapper input {
width: 100%;
padding: 15px 50px 15px 15px;
border: 2px solid #e0e0e0;
border-radius: 12px;
font-size: 16px;
outline: none;
transition: all 0.3s ease;
background: rgba(255, 255, 255, 0.9);
}
.password-wrapper input:focus {
border-color: #0F52BA;
box-shadow: 0 0 0 3px rgba(15, 82, 186, 0.1);
background: #fff;
}
.password-wrapper input::placeholder {
color: #999;
}
.toggle-password {
position: absolute;
right: 15px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
color: #666;
cursor: pointer;
font-size: 18px;
transition: color 0.3s ease;
padding: 5px;
}
.toggle-password:hover {
color: #0F52BA;
}
.login-btn {
width: 100%;
padding: 15px;
background: linear-gradient(135deg, #0F52BA, #00318C);
border: none;
border-radius: 12px;
color: #fff;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
margin-top: 10px;
}
.login-btn:hover {
background: linear-gradient(135deg, #00318C, #001F5C);
transform: translateY(-2px);
box-shadow: 0 10px 25px rgba(15, 82, 186, 0.3);
}
.login-btn:active {
transform: translateY(0);
}
.login-footer {
margin-top: 20px;
color: #666;
font-size: 12px;
}
.login-footer a {
color: #0F52BA;
text-decoration: none;
}
.login-footer a:hover {
text-decoration: underline;
}
/* 响应式设计 */
@media (max-width: 480px) {
.login-container {
margin: 20px;
padding: 30px 20px;
}
}
</style>
</head>
<body>
<div class="login-container">
<div class="logo">
<i class="fas fa-video"></i>
<span>视频播放器</span>
</div>
<h1 class="login-title">🔒 请输入密码</h1>
<p class="login-subtitle">访问视频播放器需要身份验证</p>
${msg ? `<div class="error-message">${msg}</div>` : ''}
<form method="POST" action="/login">
<div class="form-group">
<label for="password">管理员密码</label>
<div class="password-wrapper">
<input type="password" id="password" name="password" placeholder="输入登录密码" required>
<button type="button" class="toggle-password" onclick="togglePassword()">
<i class="fas fa-eye"></i>
</button>
</div>
</div>
<button type="submit" class="login-btn">
<i class="fas fa-sign-in-alt"></i> 登录
</button>
</form>
<div class="login-footer">
<p>© 2025 视频播放器</p>
</div>
</div>
<script>
function togglePassword() {
const input = document.getElementById('password');
const icon = document.querySelector('.toggle-password i');
if (input.type === 'password') {
input.type = 'text';
icon.className = 'fas fa-eye-slash';
} else {
input.type = 'password';
icon.className = 'fas fa-eye';
}
}
// 回车键登录
document.getElementById('password').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
document.querySelector('form').submit();
}
});
// 自动聚焦到密码输入框
window.addEventListener('load', function() {
document.getElementById('password').focus();
});
</script>
</body>
</html>`;
},
};
export default { async fetch(request, env, ctx) { // 设置CORS头 const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type', };
// 处理预检请求 if (request.method === 'OPTIONS') { return new Response(null, { headers: corsHeaders }); }
const url = new URL(request.url);
// 登录逻辑
if (url.pathname === '/login' && request.method === 'POST') {
const formData = await request.formData();
const password = formData.get('password');
const adminPassword = env.ADMIN_PASSWORD || 'admin123'; // 从环境变量读取密码,默认为admin123
if (password === adminPassword) {
// 登录成功,设置 Cookie
return new Response('', {
status: 302,
headers: {
'Set-Cookie': auth=1; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=3600,
'Location': '/'
}
});
} else {
return new Response(this.renderLoginPage('密码错误,请重试!'), {
headers: {
'Content-Type': 'text/html;charset=utf-8',
...corsHeaders
}
});
}
}
// 退出逻辑
if (url.pathname === '/logout') {
return new Response('', {
status: 302,
headers: {
'Set-Cookie': auth=; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=0,
'Location': '/'
}
});
}
// 检查是否已登录 const cookie = request.headers.get('Cookie') || ''; if (!cookie.includes('auth=1')) { return new Response(this.renderLoginPage(), { headers: { 'Content-Type': 'text/html;charset=utf-8', ...corsHeaders } }); } // 读取视频列表文件 let links = []; let randomLink = '';
try { const videoList1 = await fetch('https://img.boke.us.to/config/9527qita/video-list.txt'); const videoList2 = await fetch('https://img.boke.us.to/config/9527qita/video-list.txt');
const links1 = (await videoList1.text()).split('\n').filter(line => line.trim() !== ''); const links2 = (await videoList2.text()).split('\n').filter(line => line.trim() !== '');
// 合并两个文件的内容 links = [...links1, ...links2];
// 检查文件是否为空 if (links.length === 0) { return new Response('链接文件为空或未找到!', { status: 500, headers: { 'Content-Type': 'text/html;charset=utf-8' } }); }
// 默认选择第一个链接 randomLink = links[0]; } catch (error) { // 如果获取视频列表失败,使用默认视频 console.error('获取视频列表失败:', error); links = ['https://sample-videos.com/zip/10/mp4/SampleVideo1280x7201mb.mp4|测试视频']; randomLink = links[0]; }
// 生成 HTML 内容
const htmlContent =
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>自动随机播放 MP4 视频</title>
<link rel="shortcut icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2280%22>💠</text></svg>">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">
<style>
body {
margin: 0;
padding: 0;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-color: #f0f0f0;
font-family: Arial, sans-serif;
flex-direction: column;
}
/ 按钮样式 / #playPauseBtn, #prevBtn, #nextBtn, #togglePlaylistBtn { background-color: black; color: white; border: none; padding: 10px 20px; margin: 5px; cursor: pointer; border-radius: 5px; transition: background-color 0.3s ease; }
/ 按钮悬停效果 / #playPauseBtn:hover, #prevBtn:hover, #nextBtn:hover, #togglePlaylistBtn:hover { background-color: #333; }
.sites { width: 640px; background: #F2F2F2; border: 1px solid rgba(0,0,0,.01); margin: 15px auto; padding: 0; }
/ 居中对齐容器 / .video-container { display: flex; justify-content: center; align-items: center; margin: -0cm 0 0 0; }
/ 标题下移 / h2 { text-align: center; margin-bottom: 1cm; }
/ 播放列表样式 / .playlist { list-style-type: none; padding: 0; margin: 10px 0 0 0; width:638px; max-height: 162px; overflow-y: auto; text-align: left; border: 1px solid #ccc; background-color: #F2F2F2; }
.playlist li { margin: 5px 0; cursor: pointer; color: #000000; font-size: 15px; text-decoration: underline; transition: color 0.3s ease; }
.playlist li:hover { color: green; }
/ 定义正在播放的样式 / .playlist li.playing { color: red !important; font-weight: bold; } </style> </head> <body>
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1cm;"> <h2 style="margin: 0;">自动随机播放 MP4 视频</h2> <a href="/logout" style="color: #666; text-decoration: none; padding: 8px 12px; border-radius: 6px; transition: all 0.2s; background-color: rgba(0,0,0,0.1);" onmouseover="this.style.backgroundColor='rgba(0,0,0,0.2)'; this.style.color='#333'" onmouseout="this.style.backgroundColor='rgba(0,0,0,0.1)'; this.style.color='#666'" title="退出登录"> <i class="fas fa-sign-out-alt"></i> 退出 </a> </div> <div class="video-container"> <video id="videoPlayer" width="640" height="360" controls> <source src="${randomLink}" type="video/mp4"> 您的浏览器不支持视频播放。 </video> </div>
<!-- 新增按钮区域 --> <div style="text-align: center; margin: 10px 0;"> <!-- 新增:播放列表按钮 --> <button id="togglePlaylistBtn">播放列表</button> <button id="playPauseBtn">▶</button> <button id="prevBtn">❚◀</button> <button id="nextBtn">▶❚</button> </div>
<div class="sites">
<ul class="playlist" id="playlist" style="display: none;">
${links.map((link, index) => {
const [url, title] = link.split('|');
return <li data-src="${url}">${index + 1}. ${title}</li>;
}).join('')}
</ul>
</div>
<script> // 将视频链接列表转换为 JavaScript 数组 const videoLinks = ${JSON.stringify(links)};
// 选择视频元素 const videoPlayer = document.getElementById('videoPlayer');
// 新增按钮功能 const playPauseBtn = document.getElementById('playPauseBtn'); const prevBtn = document.getElementById('prevBtn'); const nextBtn = document.getElementById('nextBtn');
// 默认从第一个视频开始播放,并在页面加载时播放 let currentVideoIndex = 0;
// 页面加载时自动播放第一个视频 document.addEventListener('DOMContentLoaded', () => { playVideo(currentVideoIndex); // 新增:为当前播放的列表项添加 playing 类 updatePlaylistStyle(currentVideoIndex); });
// 新增:更新播放列表样式的函数 function updatePlaylistStyle(index) { const playlistItems = document.querySelectorAll('#playlist li'); playlistItems.forEach((item, i) => { if (i === index) { item.classList.add('playing'); } else { item.classList.remove('playing'); } }); }
// 播放视频时更新样式 function playVideo(index) { const [url] = videoLinks[index].split('|'); videoPlayer.src = url; videoPlayer.play(); // 更新播放列表样式 updatePlaylistStyle(index); }
// 播放/暂停按钮功能 playPauseBtn.addEventListener('click', () => { if (videoPlayer.paused) { videoPlayer.play(); playPauseBtn.textContent = '⏸'; } else { videoPlayer.pause(); playPauseBtn.textContent = '▶'; } });
// 上一首按钮功能 prevBtn.addEventListener('click', () => { currentVideoIndex = (currentVideoIndex - 1 + videoLinks.length) % videoLinks.length; playVideo(currentVideoIndex); });
// 下一首按钮功能 nextBtn.addEventListener('click', () => { currentVideoIndex = (currentVideoIndex + 1) % videoLinks.length; playVideo(currentVideoIndex); });
// 视频播放结束时,随机播放下一个视频 videoPlayer.addEventListener('ended', () => { // 新增:随机选择未播放的视频 const unplayedVideos = videoLinks.filter((_, index) => !playedIndexes.includes(index)); if (unplayedVideos.length === 0) { // 如果所有视频都已播放过,重置记录 playedIndexes = []; currentVideoIndex = Math.floor(Math.random() * videoLinks.length); } else { // 随机选择一个未播放的视频 const randomIndex = Math.floor(Math.random() * unplayedVideos.length); currentVideoIndex = videoLinks.indexOf(unplayedVideos[randomIndex]); } playVideo(currentVideoIndex); playedIndexes.push(currentVideoIndex); });
// 新增:用于记录已播放视频的索引 let playedIndexes = [];
// 新增:播放列表按钮功能 const togglePlaylistBtn = document.getElementById('togglePlaylistBtn'); const playlist = document.getElementById('playlist');
togglePlaylistBtn.addEventListener('click', () => { if (playlist.style.display === 'none') { playlist.style.display = 'block'; togglePlaylistBtn.textContent = '隐藏列表'; } else { playlist.style.display = 'none'; togglePlaylistBtn.textContent = '播放列表'; } });
// 点击播放列表中的链接后播放选择的视频 document.getElementById('playlist').addEventListener('click', function(event) { if (event.target.tagName === 'LI') { const selectedUrl = event.target.getAttribute('data-src'); videoPlayer.src = selectedUrl; videoPlayer.play(); // 更新当前视频索引 currentVideoIndex = videoLinks.findIndex(link => link.split('|')[0] === selectedUrl); // 更新播放列表样式 updatePlaylistStyle(currentVideoIndex); } }); </script>
</body> </html>
;return new Response(htmlContent, { headers: { 'Content-Type': 'text/html', ...corsHeaders, }, }) },
// 登录页面渲染函数
renderLoginPage(msg = '') {
return <!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>视频播放器 - 登录</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">
<link rel="shortcut icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2280%22>💠</text></svg>">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body { font-family: 'Noto Sans SC', 'Segoe UI', Arial, sans-serif; background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab); background-size: 400% 400%; animation: gradient-animation 15s ease infinite; height: 100vh; display: flex; justify-content: center; align-items: center; overflow: hidden; }
@keyframes gradient-animation { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } }
.login-container { background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(20px); border-radius: 20px; padding: 40px 30px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); width: 100%; max-width: 400px; text-align: center; animation: fadeInUp 0.6s ease-out; }
@keyframes fadeInUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
.logo { display: flex; align-items: center; justify-content: center; gap: 12px; margin-bottom: 20px; color: #333; }
.logo i { font-size: 32px; color: #0F52BA; }
.logo span { font-size: 24px; font-weight: bold; }
.login-title { font-size: 22px; color: #333; margin-bottom: 10px; font-weight: 600; }
.login-subtitle { color: #666; margin-bottom: 30px; font-size: 14px; }
.error-message { background: rgba(211, 47, 47, 0.1); color: #d32f2f; padding: 12px; border-radius: 8px; margin-bottom: 20px; font-size: 14px; border: 1px solid rgba(211, 47, 47, 0.2); }
.form-group { margin-bottom: 20px; text-align: left; }
.form-group label { display: block; color: #333; margin-bottom: 8px; font-weight: 500; font-size: 14px; }
.password-wrapper { position: relative; width: 100%; }
.password-wrapper input { width: 100%; padding: 15px 50px 15px 15px; border: 2px solid #e0e0e0; border-radius: 12px; font-size: 16px; outline: none; transition: all 0.3s ease; background: rgba(255, 255, 255, 0.9); }
.password-wrapper input:focus { border-color: #0F52BA; box-shadow: 0 0 0 3px rgba(15, 82, 186, 0.1); background: #fff; }
.password-wrapper input::placeholder { color: #999; }
.toggle-password { position: absolute; right: 15px; top: 50%; transform: translateY(-50%); background: none; border: none; color: #666; cursor: pointer; font-size: 18px; transition: color 0.3s ease; padding: 5px; }
.toggle-password:hover { color: #0F52BA; }
.login-btn { width: 100%; padding: 15px; background: linear-gradient(135deg, #0F52BA, #00318C); border: none; border-radius: 12px; color: #fff; font-size: 16px; font-weight: 600; cursor: pointer; transition: all 0.3s ease; margin-top: 10px; }
.login-btn:hover { background: linear-gradient(135deg, #00318C, #001F5C); transform: translateY(-2px); box-shadow: 0 10px 25px rgba(15, 82, 186, 0.3); }
.login-btn:active { transform: translateY(0); }
.login-footer { margin-top: 20px; color: #666; font-size: 12px; }
.login-footer a { color: #0F52BA; text-decoration: none; }
.login-footer a:hover { text-decoration: underline; }
/ 响应式设计 / @media (max-width: 480px) { .login-container { margin: 20px; padding: 30px 20px; } } </style> </head> <body> <div class="login-container"> <div class="logo"> <i class="fas fa-video"></i> <span>视频播放器</span> </div>
<h1 class="login-title">🔒 请输入密码</h1> <p class="login-subtitle">访问视频播放器需要身份验证</p>
${msg ? <div class="error-message">${msg}</div> : ''}
<form method="POST" action="/login"> <div class="form-group"> <label for="password">管理员密码</label> <div class="password-wrapper"> <input type="password" id="password" name="password" placeholder="输入登录密码" required> <button type="button" class="toggle-password" onclick="togglePassword()"> <i class="fas fa-eye"></i> </button> </div> </div>
<button type="submit" class="login-btn"> <i class="fas fa-sign-in-alt"></i> 登录 </button> </form>
<div class="login-footer"> <p>© 2025 视频播放器</p> </div> </div>
<script> function togglePassword() { const input = document.getElementById('password'); const icon = document.querySelector('.toggle-password i');
if (input.type === 'password') { input.type = 'text'; icon.className = 'fas fa-eye-slash'; } else { input.type = 'password'; icon.className = 'fas fa-eye'; } }
// 回车键登录 document.getElementById('password').addEventListener('keypress', function(e) { if (e.key === 'Enter') { document.querySelector('form').submit(); } });
// 自动聚焦到密码输入框 window.addEventListener('load', function() { document.getElementById('password').focus(); }); </script> </body> </html>; }, };