文章详情

返回首页

CF搭建mp3音乐都放

分享文章 作者: Ws01 创建时间: 2025-11-24 📝 字数: 24,263 字 👁️ 阅读: 15 次

原始 Markdown

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="上一首">&#10074;&#9664;</button>
                  <button id="playPauseBtn" title="播放/暂停">▶</button>
                  <button id="nextBtn" title="下一首">&#9654;&#10074;</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>`;
}

预览

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="上一首">&#10074;&#9664;</button> <button id="playPauseBtn" title="播放/暂停">▶</button> <button id="nextBtn" title="下一首">&#9654;&#10074;</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>; }