文章详情

返回首页

CF上部署视频播放

分享文章 作者: Ws01 创建时间: 2026-02-10 更新时间: 2026-02-10 📝 字数: 26,519 字 👁️ 阅读: 5 次

CF上部署视频播放
特别提醒:
1、部署时替换6个链接地址为你的链接地址
2、默认登录密码为:admin
3、CF上修改登录密码命名:ADMIN_PASSWORD
4、链接地址中的文件为:txt格式,txt格式中的MP4播放书写

MP4文件直链地址|MP4文件名

代码

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 links1 = [], links2 = [], links3 = [], links4 = [], links5 = [], links6 = [], links7 = [];
let randomLink = '';

const parseList = (text) => text.split('\n').filter(line => line.trim() !== '');
const results = await Promise.allSettled([
fetch('链接地址1').then(r => r.text()).then(parseList),
fetch('链接地址2').then(r => r.text()).then(parseList),
fetch('链接地址3').then(r => r.text()).then(parseList),
fetch('链接地址4').then(r => r.text()).then(parseList),
fetch('链接地址5').then(r => r.text()).then(parseList),
fetch('链接地址6').then(r => r.text()).then(parseList),
fetch('链接地址7t').then(r => r.text()).then(parseList)
]);

links1 = results[0].status === 'fulfilled' ? results[0].value : [];
links2 = results[1].status === 'fulfilled' ? results[1].value : [];
links3 = results[2].status === 'fulfilled' ? results[2].value : [];
links4 = results[3].status === 'fulfilled' ? results[3].value : [];
links5 = results[4].status === 'fulfilled' ? results[4].value : [];
links6 = results[5].status === 'fulfilled' ? results[5].value : [];
links7 = results[6].status === 'fulfilled' ? results[6].value : [];
links = [...links1, ...links2, ...links3, ...links4, ...links5, ...links6, ...links7];

if (links.length === 0) {
links1 = links2 = links3 = links4 = links5 = links6 = links7 = links = ['https://sample-videos.com/zip/10/mp4/SampleVideo1280x7201mb.mp4|测试视频'];
}
randomLink = links[0].split('|')[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, #randomBtn, #singleLoopBtn {
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, #randomBtn:hover, #singleLoopBtn:hover {
background-color: #333;
}

/ 播放模式按钮激活状态 /
#randomBtn.active, #singleLoopBtn.active {
background-color: #0F52BA;
}
#randomBtn.active:hover, #singleLoopBtn.active:hover {
background-color: #00318C;
}

.sites {
width: 640px;
background: #F2F2F2;
border: 1px solid rgba(0,0,0,.01);
margin: 6px auto;
padding: 10px;
height: 320px;
display: flex;
flex-direction: column;
box-sizing: border-box;
}

.list-tabs {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 10px;
flex-shrink: 0;
}

.list-tab {
background-color: #ddd;
color: #333;
border: 1px solid #ccc;
padding: 8px 14px;
cursor: pointer;
border-radius: 6px;
font-size: 14px;
transition: all 0.2s ease;
}

.list-tab:hover {
background-color: #ccc;
}

.list-tab.active {
background-color: #0F52BA;
color: #fff;
border-color: #0F52BA;
}

.list-panel {
display: none;
flex: 1;
min-height: 0;
overflow: hidden;
flex-direction: column;
}

.list-panel.active {
display: flex;
}

/ 居中对齐容器 /
.video-container {
display: flex;
justify-content: center;
align-items: center;
margin: -0cm 0 0 0;
}

/ 标题下移 /
h2 {
text-align: center;
margin-bottom: 1cm;
}

/ 播放列表:限制高度,超出部分可上下滚动选择,132px显示5个视频列表,156px显示6个视频列表 /
.playlist {
list-style-type: none;
padding: 0;
margin: 0;
flex: 1;
min-height: 0;
max-height: 156px;
overflow-y: auto;
overflow-x: hidden;
text-align: left;
border: 1px solid #ccc;
background-color: #fff;
border-radius: 4px;
}

.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: 4px 0;">
<button id="togglePlaylistBtn">播放列表</button>
<button id="playPauseBtn">▶</button>
<button id="prevBtn">&#10074;&#9664;</button>
<button id="nextBtn">&#9654;&#10074;</button>
<button id="randomBtn" title="随机播放">🔀</button>
<button id="singleLoopBtn" title="单曲循环">🔁</button>
</div>

<div class="sites" id="playlistContainer" style="display: none;">
<div class="list-tabs">
<button type="button" class="list-tab" data-list-id="1">列表1</button>
<button type="button" class="list-tab" data-list-id="2">列表2</button>
<button type="button" class="list-tab" data-list-id="3">列表3</button>
<button type="button" class="list-tab" data-list-id="4">列表4</button>
<button type="button" class="list-tab" data-list-id="5">列表5</button>
<button type="button" class="list-tab" data-list-id="6">列表6</button>
<button type="button" class="list-tab" data-list-id="7">列表7</button>
<button type="button" class="list-tab active" data-list-id="all">综合列表</button>
</div>
<div class="list-panel active" id="panel-all">
<ul class="playlist" id="playlist-all" data-list-id="all">
${links.map((link, index) => {
const [url, title] = link.split('|');
return <li data-src="${url}" data-list-id="all" data-index="${index}">${index + 1}. ${title}</li>;
}).join('')}
</ul>
</div>
<div class="list-panel" id="panel-1">
<ul class="playlist" id="playlist-1" data-list-id="1">
${links1.map((link, index) => {
const [url, title] = link.split('|');
return <li data-src="${url}" data-list-id="1" data-index="${index}">${index + 1}. ${title}</li>;
}).join('')}
</ul>
</div>
<div class="list-panel" id="panel-2">
<ul class="playlist" id="playlist-2" data-list-id="2">
${links2.map((link, index) => {
const [url, title] = link.split('|');
return <li data-src="${url}" data-list-id="2" data-index="${index}">${index + 1}. ${title}</li>;
}).join('')}
</ul>
</div>
<div class="list-panel" id="panel-3">
<ul class="playlist" id="playlist-3" data-list-id="3">
${links3.map((link, index) => {
const [url, title] = link.split('|');
return <li data-src="${url}" data-list-id="3" data-index="${index}">${index + 1}. ${title}</li>;
}).join('')}
</ul>
</div>
<div class="list-panel" id="panel-4">
<ul class="playlist" id="playlist-4" data-list-id="4">
${links4.map((link, index) => {
const [url, title] = link.split('|');
return <li data-src="${url}" data-list-id="4" data-index="${index}">${index + 1}. ${title}</li>;
}).join('')}
</ul>
</div>
<div class="list-panel" id="panel-5">
<ul class="playlist" id="playlist-5" data-list-id="5">
${links5.map((link, index) => {
const [url, title] = link.split('|');
return <li data-src="${url}" data-list-id="5" data-index="${index}">${index + 1}. ${title}</li>;
}).join('')}
</ul>
</div>
<div class="list-panel" id="panel-6">
<ul class="playlist" id="playlist-6" data-list-id="6">
${links6.map((link, index) => {
const [url, title] = link.split('|');
return <li data-src="${url}" data-list-id="6" data-index="${index}">${index + 1}. ${title}</li>;
}).join('')}
</ul>
</div>
<div class="list-panel" id="panel-7">
<ul class="playlist" id="playlist-7" data-list-id="7">
${links7.map((link, index) => {
const [url, title] = link.split('|');
return <li data-src="${url}" data-list-id="7" data-index="${index}">${index + 1}. ${title}</li>;
}).join('')}
</ul>
</div>
</div>

<script>
const videoLinks = ${JSON.stringify(links)};
const videoLinks1 = ${JSON.stringify(links1)};
const videoLinks2 = ${JSON.stringify(links2)};
const videoLinks3 = ${JSON.stringify(links3)};
const videoLinks4 = ${JSON.stringify(links4)};
const videoLinks5 = ${JSON.stringify(links5)};
const videoLinks6 = ${JSON.stringify(links6)};
const videoLinks7 = ${JSON.stringify(links7)};

const listIdToLinks = { 'all': videoLinks, '1': videoLinks1, '2': videoLinks2, '3': videoLinks3, '4': videoLinks4, '5': videoLinks5, '6': videoLinks6, '7': videoLinks7 };

const videoPlayer = document.getElementById('videoPlayer');
const playPauseBtn = document.getElementById('playPauseBtn');
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');

let currentPlaylistId = 'all';
let currentVideoIndex = 0;
let playMode = 'order';
let playedIndexes = [];

function getCurrentLinks() {
return listIdToLinks[currentPlaylistId] || videoLinks;
}

document.addEventListener('DOMContentLoaded', () => {
playVideo(currentVideoIndex);
updatePlaylistStyle(currentPlaylistId, currentVideoIndex);
updateModeButtons();
});

function updateModeButtons() {
document.getElementById('randomBtn').classList.toggle('active', playMode === 'random');
document.getElementById('singleLoopBtn').classList.toggle('active', playMode === 'single');
}

function updatePlaylistStyle(listId, index) {
document.querySelectorAll('.playlist li').forEach(li => {
li.classList.remove('playing');
if (li.getAttribute('data-list-id') === listId && parseInt(li.getAttribute('data-index'), 10) === index) {
li.classList.add('playing');
}
});
}

function playVideo(index) {
const list = getCurrentLinks();
if (!list.length) return;
index = (index + list.length) % list.length;
currentVideoIndex = index;
const [url] = list[index].split('|');
videoPlayer.src = url;
videoPlayer.play();
updatePlaylistStyle(currentPlaylistId, currentVideoIndex);
}

playPauseBtn.addEventListener('click', () => {
if (videoPlayer.paused) {
videoPlayer.play();
playPauseBtn.textContent = '⏸';
} else {
videoPlayer.pause();
playPauseBtn.textContent = '▶';
}
});

prevBtn.addEventListener('click', () => {
const list = getCurrentLinks();
currentVideoIndex = (currentVideoIndex - 1 + list.length) % list.length;
playVideo(currentVideoIndex);
});

nextBtn.addEventListener('click', () => {
const list = getCurrentLinks();
currentVideoIndex = (currentVideoIndex + 1) % list.length;
playVideo(currentVideoIndex);
});

videoPlayer.addEventListener('ended', () => {
if (playMode === 'single') {
videoPlayer.currentTime = 0;
videoPlayer.play();
return;
}
const list = getCurrentLinks();
if (playMode === 'random') {
const unplayed = list.map((_, i) => i).filter(i => !playedIndexes.includes(i));
if (unplayed.length === 0) {
playedIndexes = [];
currentVideoIndex = Math.floor(Math.random() * list.length);
} else {
currentVideoIndex = unplayed[Math.floor(Math.random() * unplayed.length)];
}
playedIndexes.push(currentVideoIndex);
} else {
currentVideoIndex = (currentVideoIndex + 1) % list.length;
}
playVideo(currentVideoIndex);
});

document.getElementById('randomBtn').addEventListener('click', () => {
playMode = playMode === 'random' ? 'order' : 'random';
playedIndexes = [];
updateModeButtons();
});

document.getElementById('singleLoopBtn').addEventListener('click', () => {
playMode = playMode === 'single' ? 'order' : 'single';
updateModeButtons();
});

const togglePlaylistBtn = document.getElementById('togglePlaylistBtn');
const playlistContainer = document.getElementById('playlistContainer');

togglePlaylistBtn.addEventListener('click', () => {
if (playlistContainer.style.display === 'none') {
playlistContainer.style.display = 'block';
togglePlaylistBtn.textContent = '隐藏列表';
} else {
playlistContainer.style.display = 'none';
togglePlaylistBtn.textContent = '播放列表';
}
});

document.querySelectorAll('.list-tab').forEach(tab => {
tab.addEventListener('click', () => {
const listId = tab.getAttribute('data-list-id');
document.querySelectorAll('.list-tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.list-panel').forEach(p => p.classList.remove('active'));
tab.classList.add('active');
const panel = document.getElementById('panel-' + listId);
if (panel) panel.classList.add('active');
});
});

playlistContainer.addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
const listId = event.target.getAttribute('data-list-id');
const index = parseInt(event.target.getAttribute('data-index'), 10);
const selectedUrl = event.target.getAttribute('data-src');
currentPlaylistId = listId;
currentVideoIndex = index;
playedIndexes = [];
videoPlayer.src = selectedUrl;
videoPlayer.play();
updatePlaylistStyle(currentPlaylistId, 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>`;
},
};