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>`;
},
};