addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const url = new URL(request.url);
// 登录逻辑
if (url.pathname === '/login' && request.method === 'POST') {
const formData = await request.formData();
const password = formData.get('password');
if (password === ADMIN_PASSWORD) {
// 登录成功,设置 Cookie
return new Response('', {
status: 302,
headers: {
'Set-Cookie': `auth=1; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=86400`,
'Location': '/'
}
});
} else {
return new Response(renderLoginPage('密码错误,请重试!'), {
headers: { 'Content-Type': 'text/html;charset=utf-8' }
});
}
}
// 退出逻辑
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(renderLoginPage(), {
headers: { 'Content-Type': 'text/html;charset=utf-8' }
});
}
// 已登录 -> 播放页面
const videoList1 = await fetch('https://img.boke.us.to/config/9527qita/mp3-list1.txt');
const videoList2 = await fetch('https://img.boke.us.to/config/9527qita/mp3-list2.txt');
const videoList3 = await fetch('https://img.boke.us.to/config/9527qita/mp3-list3.txt');
const videoList4 = await fetch('https://img.boke.us.to/config/9527qita/mp3-list4.txt');
const links1 = (await videoList1.text()).split('\n').filter(line => line.trim() !== '');
const links2 = (await videoList2.text()).split('\n').filter(line => line.trim() !== '');
const links3 = (await videoList3.text()).split('\n').filter(line => line.trim() !== '');
const links4 = (await videoList4.text()).split('\n').filter(line => line.trim() !== '');
const links = [...links1, ...links2, ...links3, ...links4];
if (links.length === 0) {
return new Response('链接文件为空或未找到!', { status: 500 });
}
const randomLink = links[0];
const htmlContent = `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>自动随机播放 MP3 音频</title>
<style>
* { box-sizing: border-box; }
body {
text-align: center;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
background:#f0f0f0;
margin:0;
padding: 0;
min-height: 100vh;
}
.sites {
width:620px;
background:#EBEBEB;
border:1px solid rgba(0,0,0,.06);
margin:15px auto;
padding:0px;
color: white;
border-radius: 8px; /* 四角弧度,一般高为5,50为圆*/
}
.sites01 {
width:820px;
background:;
border:2px solid auto;
margin:15px auto;
padding:0px;
}
.sites dl {
height:36px;
line-height:36px;
display:block;
margin:0;
}
.sites dl.alt {
background:#c5dff6;
border-top:1px solid #ffffff;
border-bottom:1px solid #ffffff;
}
.sites dl.alt2 {
background:#dcecfa;
border-top:1px solid #ffffff;
border-bottom:1px solid #ffffff;
}
.sites dt,.sites dd {
text-align:center;
display:block;
float:left;
}
.sites dt {
width:60px;
}
.sites dd {
width:90px;
margin:0;
}
header {
display:flex;
justify-content:space-between;
align-items:center;
background:#222;
color:#fff;
padding: 8px 15px;
position: sticky;
top: 0;
z-index: 100;
height: 80px;
}
header h2 {
margin:0;
font-size: 18px;
font-weight: 500;
}
header a {
color:#fff;
text-decoration:none;
background:#c00;
padding: 6px 10px;
border-radius:4px;
font-size: 16px;
white-space: nowrap;
}
header a:hover { background:#900; }
.main-container {
padding: 15px;
max-width: 100%;
}
.audio-container {
background:#DEDEDE;
width: 100%;
max-width: 610px;
height: 65px;
margin: 15px auto;
border:1px solid #D4D4D4;
border-radius:4px;
display: flex;
align-items: center;
justify-content: center;
}
.audio-container audio {
width: 100%;
height: 100%;
}
.controls {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 8px;
margin: 15px 0;
}
.control-row {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 8px;
margin: 8px 0;
}
button {
margin: 0;
padding: 12px 16px;
border-radius: 8px;
border:none;
background:black;
color:white;
cursor:pointer;
font-size: 16px;
min-width: 75px;
min-height: 45px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
}
button:hover { background:#333; }
button.active { background:#4a90e2; }
button.active:hover { background:#357ABD; }
.playlist {
display:none;
max-height: 282px;
overflow-y:auto;
width: 100%;
max-width: 610px;
margin: 15px auto;
background:#F2F2F2;
border:1px solid #ebebeb;
border-radius:8px;
list-style:none;
padding:0;
text-align:left;
color:#404040;
}
.playlist li {
padding: 12px 15px;
cursor:pointer;
text-decoration:underline;
border-bottom: 1px solid #ddd;
font-size: 16px;
line-height: 1.4;
min-height: 44px;
display: flex;
align-items: center;
}
.playlist li:last-child {
border-bottom: none;
}
.playlist li.playing {
color:red;
font-weight:bold;
background: #fff3cd;
}
@media (max-width: 480px) {
.sites {
width:390px;
}
.audio-container {
width: 100%;
max-width: 385px;
}
.playlist {
max-height: 262px;
width: 100%;
max-width: 385px;
}
header {
padding: 6px 10px;
}
header h2 {
font-size: 16px;
}
header a {
padding: 4px 8px;
font-size: 14px;
}
.main-container {
padding: 10px;
}
.audio-container {
height: 50px;
margin: 10px auto;
}
button {
padding: 10px 12px;
font-size: 15px;
min-width: 55px;
min-height: 42px;
}
.playlist li {
padding: 10px 12px;
font-size: 13px;
min-height: 40px;
}
}
</style>
</head>
<body>
</div>
<div class="sites">
<header>
<h2>自动随机播放 MP3 音频</h2>
<a href="/logout">退出</a>
</header>
<div class="main-container">
<div class="audio-container">
<audio id="audioPlayer" controls>
<source src="${randomLink}" type="audio/mpeg">
</audio>
</div>
<div class="controls">
<div class="control-row">
<button id="prevBtn" title="上一首">❚◀</button>
<button id="playPauseBtn" title="播放/暂停">▶</button>
<button id="nextBtn" title="下一首">▶❚</button>
</div>
<div class="control-row">
<button id="shuffleBtn" title="随机播放">🔀</button>
<button id="repeatBtn" title="单曲循环">🔁</button>
</div>
</div>
<div class="control-row">
<button id="list1Btn">列表1</button>
<button id="list2Btn">列表2</button>
<button id="list3Btn">列表3</button>
<button id="list4Btn">列表4</button>
<button id="togglePlaylistBtn">综合列表</button>
</div>
</div>
<ul class="playlist" id="playlist">
${links.map((line, i) => {
const [url, title] = line.split('|');
return `<li data-src="${url}">${i+1}. ${title}</li>`;
}).join('')}
</ul>
</div>
<script>
const audioLinks1 = ${JSON.stringify(links1)};
const audioLinks2 = ${JSON.stringify(links2)};
const audioLinks3 = ${JSON.stringify(links3)};
const audioLinks4 = ${JSON.stringify(links4)};
const audioLinks = ${JSON.stringify(links)};
const audioPlayer = document.getElementById('audioPlayer');
const playPauseBtn = document.getElementById('playPauseBtn');
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
const playlist = document.getElementById('playlist');
const list1Btn = document.getElementById('list1Btn');
const list2Btn = document.getElementById('list2Btn');
const list3Btn = document.getElementById('list3Btn');
const list4Btn = document.getElementById('list4Btn');
const togglePlaylistBtn = document.getElementById('togglePlaylistBtn');
const shuffleBtn = document.getElementById('shuffleBtn');
const repeatBtn = document.getElementById('repeatBtn');
let currentPlaylist = 'combined'; // 'list1', 'list2', 'list3', 'list4', 'combined'
let currentAudioIndex = 0;
let playedIndexes = [];
let playMode = 'normal'; // 'normal', 'shuffle', 'repeat'
function getCurrentPlaylist() {
switch(currentPlaylist) {
case 'list1': return audioLinks1;
case 'list2': return audioLinks2;
case 'list3': return audioLinks3;
case 'list4': return audioLinks4;
case 'combined': return audioLinks;
default: return audioLinks;
}
}
function updatePlaylistDisplay() {
const currentLinks = getCurrentPlaylist();
playlist.innerHTML = currentLinks.map((line, i) => {
const [url, title] = line.split('|');
return '<li data-src="' + url + '">' + (i+1) + '. ' + title + '</li>';
}).join('');
updatePlaylistStyle(currentAudioIndex);
}
function updatePlaylistStyle(i){
document.querySelectorAll('#playlist li').forEach((el, idx)=>{
el.classList.toggle('playing', idx===i);
});
}
function updateButtonStates() {
shuffleBtn.classList.toggle('active', playMode === 'shuffle');
repeatBtn.classList.toggle('active', playMode === 'repeat');
list1Btn.classList.toggle('active', currentPlaylist === 'list1');
list2Btn.classList.toggle('active', currentPlaylist === 'list2');
list3Btn.classList.toggle('active', currentPlaylist === 'list3');
list4Btn.classList.toggle('active', currentPlaylist === 'list4');
togglePlaylistBtn.classList.toggle('active', currentPlaylist === 'combined');
}
function getNextIndex() {
const currentLinks = getCurrentPlaylist();
if (playMode === 'shuffle') {
const unplayed = currentLinks.map((_, i) => i).filter(i => !playedIndexes.includes(i));
if (unplayed.length === 0) {
playedIndexes = [];
return Math.floor(Math.random() * currentLinks.length);
}
return unplayed[Math.floor(Math.random() * unplayed.length)];
} else if (playMode === 'repeat') {
return currentAudioIndex; // 单曲循环,返回当前索引
} else {
return (currentAudioIndex + 1) % currentLinks.length; // 正常顺序播放
}
}
function getPrevIndex() {
const currentLinks = getCurrentPlaylist();
if (playMode === 'shuffle') {
const unplayed = currentLinks.map((_, i) => i).filter(i => !playedIndexes.includes(i));
if (unplayed.length === 0) {
playedIndexes = [];
return Math.floor(Math.random() * currentLinks.length);
}
return unplayed[Math.floor(Math.random() * unplayed.length)];
} else if (playMode === 'repeat') {
return currentAudioIndex; // 单曲循环,返回当前索引
} else {
return (currentAudioIndex - 1 + currentLinks.length) % currentLinks.length; // 正常顺序播放
}
}
function playAudio(i){
const currentLinks = getCurrentPlaylist();
const [url] = currentLinks[i].split('|');
audioPlayer.src = url;
audioPlayer.play();
currentAudioIndex = i;
updatePlaylistStyle(i);
if (playMode === 'shuffle' && !playedIndexes.includes(i)) {
playedIndexes.push(i);
}
}
document.addEventListener('DOMContentLoaded', ()=>{
updatePlaylistDisplay();
playAudio(0);
updateButtonStates();
});
playPauseBtn.onclick=()=>{
if(audioPlayer.paused){ audioPlayer.play(); playPauseBtn.textContent='⏸'; }
else { audioPlayer.pause(); playPauseBtn.textContent='▶'; }
};
prevBtn.onclick=()=>{
const prevIndex = getPrevIndex();
playAudio(prevIndex);
};
nextBtn.onclick=()=>{
const nextIndex = getNextIndex();
playAudio(nextIndex);
};
audioPlayer.addEventListener('ended', ()=>{
if (playMode === 'repeat') {
// 单曲循环模式,重新播放当前歌曲
playAudio(currentAudioIndex);
} else {
// 正常播放或随机播放模式
const nextIndex = getNextIndex();
playAudio(nextIndex);
}
});
function switchPlaylist(playlistType) {
currentPlaylist = playlistType;
currentAudioIndex = 0;
playedIndexes = [];
updatePlaylistDisplay();
updateButtonStates();
playAudio(0);
// 重置所有播放列表按钮的文本
list1Btn.textContent = '列表1';
list2Btn.textContent = '列表2';
list3Btn.textContent = '列表3';
list4Btn.textContent = '列表4';
togglePlaylistBtn.textContent = '综合列表';
}
list1Btn.onclick=()=>{
if (currentPlaylist === 'list1') {
playlist.style.display= playlist.style.display==='none' ? 'block':'none';
list1Btn.textContent= playlist.style.display==='none' ? '列表1':'隐藏列表';
} else {
switchPlaylist('list1');
playlist.style.display = 'block';
list1Btn.textContent = '隐藏列表';
}
};
list2Btn.onclick=()=>{
if (currentPlaylist === 'list2') {
playlist.style.display= playlist.style.display==='none' ? 'block':'none';
list2Btn.textContent= playlist.style.display==='none' ? '列表2':'隐藏列表';
} else {
switchPlaylist('list2');
playlist.style.display = 'block';
list2Btn.textContent = '隐藏列表';
}
};
list3Btn.onclick=()=>{
if (currentPlaylist === 'list3') {
playlist.style.display= playlist.style.display==='none' ? 'block':'none';
list3Btn.textContent= playlist.style.display==='none' ? '列表3':'隐藏列表';
} else {
switchPlaylist('list3');
playlist.style.display = 'block';
list3Btn.textContent = '隐藏列表';
}
};
list4Btn.onclick=()=>{
if (currentPlaylist === 'list4') {
playlist.style.display= playlist.style.display==='none' ? 'block':'none';
list4Btn.textContent= playlist.style.display==='none' ? '列表4':'隐藏列表';
} else {
switchPlaylist('list4');
playlist.style.display = 'block';
list4Btn.textContent = '隐藏列表';
}
};
togglePlaylistBtn.onclick=()=>{
if (currentPlaylist === 'combined') {
playlist.style.display= playlist.style.display==='none' ? 'block':'none';
togglePlaylistBtn.textContent= playlist.style.display==='none' ? '综合列表':'隐藏列表';
} else {
switchPlaylist('combined');
}
};
shuffleBtn.onclick=()=>{
if (playMode === 'shuffle') {
playMode = 'normal';
} else {
playMode = 'shuffle';
playedIndexes = [currentAudioIndex]; // 将当前歌曲标记为已播放
}
updateButtonStates();
};
repeatBtn.onclick=()=>{
if (playMode === 'repeat') {
playMode = 'normal';
} else {
playMode = 'repeat';
}
updateButtonStates();
};
playlist.onclick=(e)=>{
if(e.target.tagName==='LI'){
const url=e.target.dataset.src;
const currentLinks = getCurrentPlaylist();
const newIndex = currentLinks.findIndex(l=>l.split('|')[0]===url);
playAudio(newIndex);
}
};
</script>
</body>
</html>`;
return new Response(htmlContent, { headers: { 'Content-Type': 'text/html' } });
}
// 登录页面
function 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, user-scalable=no">
<title>登录</title>
<style>
* { box-sizing: border-box; }
body {
display:flex;
justify-content:center;
align-items:center;
min-height:100vh;
margin:0;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
background: linear-gradient(135deg, #74ebd5 0%, #9face6 100%);
}
.login-box {
background:#fff;
padding: 30px 25px;
border-radius:12px;
box-shadow:0 8px 20px rgba(0,0,0,.2);
width: 100%;
max-width: 350px;
text-align:center;
animation: fadeIn 0.6s ease;
}
.login-box h2 {
margin-bottom:20px;
color:#333;
font-size:20px;
font-weight: 500;
}
.msg {
color:red;
margin-bottom:15px;
font-size:14px;
line-height: 1.4;
}
.password-wrapper {
position: relative;
width: 100%;
margin-bottom: 20px;
}
.password-wrapper input {
width:100%;
padding: 14px 45px 14px 14px;
border:1px solid #ccc;
border-radius:8px;
font-size:16px;
transition: all 0.3s ease;
box-sizing: border-box;
background: #fafafa;
}
.password-wrapper input:focus {
border-color:#4a90e2;
outline:none;
box-shadow:0 0 6px rgba(74,144,226,.5);
background: #fff;
}
.toggle-password {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
cursor: pointer;
font-size: 20px;
color: #666;
user-select: none;
padding: 4px;
min-width: 32px;
min-height: 32px;
display: flex;
align-items: center;
justify-content: center;
}
button {
width:100%;
padding: 14px;
border:none;
border-radius:8px;
font-size:16px;
background:linear-gradient(135deg, #4a90e2, #357ABD);
color:white;
cursor:pointer;
transition: all 0.3s ease;
font-weight: 500;
min-height: 48px;
}
button:hover {
background:linear-gradient(135deg, #357ABD, #2E5C9A);
transform: translateY(-1px);
}
button:active {
transform: translateY(0);
}
@keyframes fadeIn {
from { opacity:0; transform:translateY(-20px); }
to { opacity:1; transform:translateY(0); }
}
@media (max-width: 480px) {
body {
padding: 15px;
}
.login-box {
padding: 25px 20px;
max-width: 100%;
}
.login-box h2 {
font-size: 18px;
margin-bottom: 15px;
}
.password-wrapper input {
padding: 12px 40px 12px 12px;
font-size: 16px;
}
.toggle-password {
font-size: 18px;
right: 10px;
}
button {
padding: 12px;
font-size: 15px;
}
}
</style>
</head><body>
<div class="login-box">
<h2>🔒 请输入密码</h2>
${msg ? `<div class="msg">${msg}</div>` : ''}
<form method="POST" action="/login">
<div class="password-wrapper">
<input type="password" id="password" name="password" placeholder="输入登录密码" autocomplete="current-password">
<span class="toggle-password" onclick="togglePassword()">👁️</span>
</div>
<button type="submit">登录</button>
</form>
</div>
<script>
function togglePassword() {
const pwd = document.getElementById('password');
const toggle = document.querySelector('.toggle-password');
if (pwd.type === 'password') {
pwd.type = 'text';
toggle.textContent = '🙈';
} else {
pwd.type = 'password';
toggle.textContent = '👁️';
}
}
// 自动聚焦到密码输入框
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('password').focus();
});
</script>
</body></html>`;
}