Files
SunShineMusic/index.php
2025-09-24 14:15:23 +00:00

2183 lines
90 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
require_once 'config.php';
// 页面标题和版本信息
$pageTitle = "落日音乐 - 发现好音乐";
$siteVersion = "1.8.3.6016";
$bqsy = "2024-2025 落日音乐. JGZ_YES. 版权所有";
// 定义网站版权信息
$copyrightInfo = "本网站所有代码和内容受版权保护未经授权禁止复制、修改和使用。Copyright © 2023 音乐分享网站 保留所有权利。";
// 设备识别函数
function isMobileDevice() {
$userAgent = $_SERVER['HTTP_USER_AGENT'];
$mobileKeywords = [
'Android', 'webOS', 'iPhone', 'iPad', 'iPod', 'BlackBerry',
'Windows Phone', 'Opera Mini', 'IEMobile', 'Mobile'
];
foreach ($mobileKeywords as $keyword) {
if (stripos($userAgent, $keyword) !== false) {
return true;
}
}
return false;
}
$isMobile = isMobileDevice();
$htmlClass = $isMobile ? 'mobile' : 'desktop';
// 检查用户登录状态
session_start();
$isLoggedIn = isset($_SESSION['user_logged_in']) && $_SESSION['user_logged_in'] === true;
$userInfo = isset($_SESSION['user_info']) ? $_SESSION['user_info'] : null;
// 处理登出
if (isset($_GET['action']) && $_GET['action'] == 'logout') {
session_unset();
session_destroy();
header("Location: index.php");
exit;
}
// 定义音乐分类及其配色
$categories = [
'all' => ['name' => '全部音乐', 'color' => '#b89e81', 'text_color' => '#5d4037'],
'cantonese' => ['name' => '粤语歌曲', 'color' => '#c8e6c9', 'text_color' => '#2e7d32'],
'mandarin' => ['name' => '国语歌曲', 'color' => '#fff3e0', 'text_color' => '#e65100'],
'waiyu' => ['name' => '外语歌曲', 'color' => '#e3f2fd', 'text_color' => '#0d47a1'],
'classic' => ['name' => '经典老歌', 'color' => '#efebe9', 'text_color' => '#3e2723'],
'other' => ['name' => '其他音乐', 'color' => '#f3e5f5', 'text_color' => '#6a1b9a']
];
// 模拟音乐列表数据(实际项目中可从数据库获取)
$musicList = require_once __DIR__ . '/data/music.php';
// 获取当前页面URL
function getCurrentPageURL() {
$protocol = ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
$host = $_SERVER['HTTP_HOST'];
$script = $_SERVER['SCRIPT_NAME'];
return $protocol . $host . $script;
}
$currentPageUrl = getCurrentPageURL();
?>
<!DOCTYPE html>
<html lang="zh_cn" class="<?php echo $htmlClass; ?>">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title><?php echo htmlspecialchars($pageTitle); ?></title>
<meta name="copyright" content="本网站内容受版权保护,未经许可不得复制或使用">
<link rel="icon" href="https://shanwogou.cn/audio/static/icon/new-icon.png" type="image/png">
<link rel="alternate icon" href="./static/icon/icon.ico" type="image/x-icon">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/* === 引入自定义字体 === */
@font-face {
font-family: "MyCustomFont";
src: url("./static/font/bbc.ttf") format("truetype");
font-weight: normal;
font-style: normal;
}
/* === 基础样式与暗黑模式模式变量 === */
:root {
--bg-color: #f5f5f7;
--text-color: #333;
--card-bg: #ffffff;
--card-shadow: 0 4px 12px rgba(0,0,0,0.1);
--primary-color: #2c3e50;
--primary-hover: #34495e;
--muted-color: #666;
--border-color: #ddd;
--accent-color: #e74c3c;
--notification-bg: #2c3e50;
--notification-text: #ffffff;
--sidebar-bg: #ffffff;
--sidebar-shadow: -2px 0 10px rgba(0,0,0,0.05);
--history-item-hover: #f9f9f9;
--main-font: "MyCustomFont", sans-serif; /* 自定义字体变量 */
}
.dark-mode {
--bg-color: #121212;
--text-color: #e0e0e0;
--card-bg: #1e1e1e;
--card-shadow: 0 4px 12px rgba(0,0,0,0.5);
--primary-color: #bb86fc;
--primary-hover: #9c6cd6;
--muted-color: #b0b0b0;
--border-color: #333;
--accent-color: #ff6b6b;
--notification-bg: #111;
--notification-text: #e0e0e0;
--sidebar-bg: #1a1a1a;
--sidebar-shadow: -2px 0 10px rgba(0,0,0,0.7);
--history-item-hover: #2d2d2d;
}
/* 基础布局 */
body {
font-family: var(--main-font); /* 应用自定义字体 */
margin: 0;
padding: 0;
background-color: var(--bg-color);
color: var(--text-color);
transition: background-color 0.3s ease, color 0.3s ease;
}
/* 桌面端布局 */
.desktop .app-container {
display: flex;
min-height: 100vh;
}
.desktop .main-wrapper {
flex: 1;
display: flex;
flex-direction: column;
margin-left: 280px; /* 为固定侧边栏留出空间 */
}
.desktop .main-content {
flex: 1;
max-width: 1200px;
width: 100%;
margin: 0 auto;
padding: 1rem;
box-sizing: border-box;
}
/* 侧边栏 - 固定在用户左侧 */
.desktop .sidebar {
width: 280px;
background-color: var(--sidebar-bg);
box-shadow: var(--sidebar-shadow);
padding: 1.5rem;
box-sizing: border-box;
overflow-y: auto;
transition: background-color 0.3s ease, box-shadow 0.3s ease;
/* 关键样式:固定在左侧 */
position: fixed;
top: 0;
left: 0;
height: 100vh;
z-index: 90;
}
/* 移动端布局 */
.mobile .app-container {
display: block;
min-height: 100vh;
}
.mobile .main-wrapper {
flex: 1;
display: flex;
flex-direction: column;
margin-left: 0;
}
.mobile .main-content {
flex: 1;
width: 100%;
padding: 0.5rem;
box-sizing: border-box;
}
.mobile .sidebar {
width: 250px;
background-color: var(--sidebar-bg);
box-shadow: var(--sidebar-shadow);
padding: 1.5rem;
box-sizing: border-box;
overflow-y: auto;
transition: transform 0.3s ease, background-color 0.3s ease, box-shadow 0.3s ease;
/* 关键样式:固定在左侧但默认隐藏 */
position: fixed;
top: 0;
left: 0;
height: 100vh;
z-index: 101;
transform: translateX(-100%);
}
.mobile .sidebar.show {
transform: translateX(0);
}
.mobile .sidebar-toggle {
display: block;
position: fixed;
top: 1rem;
left: 1rem;
z-index: 100;
background-color: var(--primary-color);
color: white;
border: none;
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
cursor: pointer;
}
/* 顶部工具栏 */
.top-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 1.5rem;
background-color: var(--card-bg);
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
position: sticky;
top: 0;
z-index: 100;
transition: background-color 0.3s ease, box-shadow 0.3s ease;
}
.site-title {
margin: 0;
font-size: 1.4rem;
color: var(--primary-color);
}
.theme-toggle, .user-menu-btn {
background: none;
border: 1px solid var(--border-color);
color: var(--text-color);
padding: 0.5rem 0.8rem;
border-radius: 20px;
cursor: pointer;
display: flex;
align-items: center;
gap: 0.5rem;
transition: all 0.3s ease;
text-decoration: none;
font-family: var(--main-font); /* 应用自定义字体 */
}
.theme-toggle:hover, .user-menu-btn:hover {
background-color: var(--primary-color);
color: white;
border-color: var(--primary-color);
}
h1, h2, h3 {
color: var(--primary-color);
font-family: var(--main-font); /* 应用自定义字体 */
}
h1 { font-size: 1.8rem; text-align: center; margin: 1rem 0; }
h2 { font-size: 1.4rem; margin-bottom: 0.5rem; }
/* 搜索框 */
.search-container {
margin: 1rem 0;
text-align: center;
}
#search-form {
max-width: 600px;
margin: 0 auto;
}
#search-input {
width: 100%;
max-width: 600px;
padding: 0.8rem 1rem;
border: 1px solid var(--border-color);
border-radius: 25px;
font-size: 1rem;
background-color: var(--card-bg);
color: var(--text-color);
transition: all 0.3s ease;
font-family: var(--main-font); /* 应用自定义字体 */
}
#search-input:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 2px rgba(44, 62, 80, 0.1);
}
/* 音乐项样式 */
.music-item {
background-color: var(--card-bg);
border-radius: 12px;
padding: 1.2rem;
box-shadow: var(--card-shadow);
margin-bottom: 1.2rem;
transition: background-color 0.3s ease, box-shadow 0.3s ease;
}
/* 视图切换 */
.view-toggle { display: flex; justify-content: center; gap: 0.8rem; margin: 1rem 0; }
.view-toggle button {
background-color: var(--card-bg);
border: 1px solid var(--border-color);
color: var(--text-color);
padding: 0.5rem 1rem; border-radius: 20px; cursor: pointer;
transition: all 0.3s ease; display: flex; align-items: center; gap: 0.5rem;
font-family: var(--main-font); /* 应用自定义字体 */
}
.view-toggle button:hover, .view-toggle button.active {
background-color: var(--primary-color); color: white; border-color: var(--primary-color);
}
/* 列表/卡片视图 */
.list-view .music-item { display: block; }
.desktop .card-view {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));
gap: 1.2rem;
}
.mobile .card-view {
display: grid;
grid-template-columns: 1fr;
gap: 1.2rem;
}
/* 分类导航 */
.category-nav { display: flex; gap: 0.8rem; flex-wrap: wrap; justify-content: center; margin-top: 1rem; }
.category-btn {
border: none; padding: 0.5rem 1.2rem; border-radius: 20px; cursor: pointer;
transition: all 0.3s ease; font-size: 0.9rem; font-weight: 500;
font-family: var(--main-font); /* 应用自定义字体 */
}
.category-btn:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
.category-btn.active { box-shadow: 0 4px 8px rgba(0,0,0,0.15); font-weight: bold; }
/* 分类标签 */
.category-tag {
display: inline-block; padding: 0.3rem 0.8rem; border-radius: 15px;
font-size: 0.8rem; font-weight: 500; margin-right: 0.5rem; margin-bottom: 0.8rem;
font-family: var(--main-font); /* 应用自定义字体 */
}
.artist-info {
color: var(--muted-color);
font-size: 0.9rem;
margin-bottom: 1rem;
font-style: italic;
font-family: var(--main-font); /* 应用自定义字体 */
}
/* 自定义音频播放器样式 */
.custom-audio-player {
width: 100%;
margin: 1.5rem 0;
box-sizing: border-box;
background-color: var(--bg-color);
border-radius: 8px;
padding: 0.8rem;
display: flex;
align-items: center;
gap: 0.8rem;
flex-wrap: wrap;
transition: background-color 0.3s ease;
}
.audio-controls {
display: flex;
align-items: center;
gap: 0.8rem;
}
.audio-button {
background: none;
border: none;
cursor: pointer;
color: var(--primary-color);
font-size: 1.2rem;
width: 36px;
height: 36px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
}
.audio-button:hover {
background-color: rgba(44, 62, 80, 0.1);
}
.audio-button.active {
background-color: var(--primary-color);
color: white;
}
.progress-container {
flex-grow: 1;
display: flex;
align-items: center;
gap: 0.5rem;
min-width: 200px;
}
.mobile .progress-container {
order: 2;
width: 100%;
}
.audio-progress {
flex-grow: 1;
height: 4px;
background-color: var(--border-color);
border-radius: 2px;
cursor: pointer;
position: relative;
}
.progress-fill {
height: 100%;
background-color: var(--primary-color);
border-radius: 2px;
width: 0%;
transition: width 0.1s ease;
}
.progress-handle {
position: absolute;
width: 12px;
height: 12px;
border-radius: 50%;
background-color: var(--primary-color);
top: 50%;
transform: translateY(-50%);
left: 0%;
display: none;
transition: left 0.1s ease;
box-shadow: 0 0 5px rgba(0,0,0,0.2);
}
.audio-progress:hover .progress-handle {
display: block;
}
.progress-handle.dragging {
transform: translateY(-50%) scale(1.2);
box-shadow: 0 0 8px rgba(44, 62, 80, 0.5);
}
.time-display {
font-size: 0.8rem;
color: var(--muted-color);
width: 40px;
text-align: center;
font-family: var(--main-font); /* 应用自定义字体 */
}
.time-separator {
font-size: 0.8rem;
color: var(--muted-color);
width: 10px;
text-align: center;
}
/* 音量控制样式 */
.volume-control {
display: flex;
align-items: center;
gap: 0.5rem;
}
.desktop .volume-control {
margin-left: 0.5rem;
min-width: 100px;
}
.mobile .volume-control {
margin-top: 0.5rem;
width: 100%;
justify-content: center;
order: 3;
}
.volume-button {
background: none;
border: none;
cursor: pointer;
color: var(--primary-color);
font-size: 1rem;
width: 28px;
height: 28px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
}
.volume-button:hover {
background-color: rgba(44, 62, 80, 0.1);
}
.volume-slider {
width: 80px;
height: 4px;
-webkit-appearance: none;
appearance: none;
background: var(--border-color);
border-radius: 2px;
outline: none;
transition: all 0.1s ease;
}
.volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--primary-color);
cursor: pointer;
transition: all 0.1s ease;
}
.volume-slider::-webkit-slider-thumb:hover,
.volume-slider::-webkit-slider-thumb:active {
transform: scale(1.2);
box-shadow: 0 0 8px rgba(44, 62, 80, 0.5);
}
.download-link {
display: inline-block;
margin-top: 1rem;
color: var(--primary-color);
text-decoration: none;
padding: 0.5rem 1rem;
border: 1px solid var(--primary-color);
border-radius: 20px;
transition: all 0.3s ease;
font-size: 0.95rem;
font-family: var(--main-font); /* 应用自定义字体 */
}
.download-link:hover {
background-color: var(--primary-color);
color: white;
}
.recommend-container {
background-color: var(--card-bg);
border-radius: 12px;
padding: 1.2rem;
box-shadow: var(--card-shadow);
margin-top: 1.5rem;
transition: background-color 0.3s ease, box-shadow 0.3s ease;
}
.form-group {
margin-bottom: 1.2rem;
text-align: left;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
color: var(--primary-color);
font-weight: 500;
font-family: var(--main-font); /* 应用自定义字体 */
}
.form-control {
width: 100%;
padding: 0.8rem;
border: 1px solid var(--border-color);
border-radius: 8px;
font-size: 1rem;
box-sizing: border-box;
background-color: var(--bg-color);
color: var(--text-color);
transition: border-color 0.3s ease;
font-family: var(--main-font); /* 应用自定义字体 */
}
.form-group label:after {
content: " *";
color: var(--accent-color);
}
.submit-btn {
background-color: var(--primary-color);
color: white;
border: none;
padding: 0.8rem 2rem;
border-radius: 25px;
font-size: 1rem;
cursor: pointer;
transition: background-color 0.3s ease;
width: 100%;
box-sizing: border-box;
font-family: var(--main-font); /* 应用自定义字体 */
}
.submit-btn:hover {
background-color: var(--primary-hover);
}
.recommendations-link {
display: block;
text-align: center;
margin-top: 1rem;
color: var(--primary-color);
text-decoration: none;
font-weight: 500;
padding: 0.5rem 0;
font-family: var(--main-font); /* 应用自定义字体 */
}
.recommendations-link:hover {
text-decoration: underline;
}
.message {
padding: 1rem;
border-radius: 8px;
margin-bottom: 1rem;
text-align: center;
font-size: 0.95rem;
font-family: var(--main-font); /* 应用自定义字体 */
}
.success {
background-color: #d4edda;
color: #155724;
}
.error {
background-color: #f8d7da;
color: #721c24;
}
.error-input {
border-color: var(--accent-color) !important;
box-shadow: 0 0 0 2px rgba(231, 76, 60, 0.1) !important;
}
.login-prompt {
text-align: center;
padding: 1rem;
background-color: rgba(44, 62, 80, 0.1);
border-radius: 8px;
margin-bottom: 1rem;
font-family: var(--main-font); /* 应用自定义字体 */
}
.login-prompt a {
color: var(--primary-color);
font-weight: bold;
text-decoration: none;
}
.login-prompt a:hover {
text-decoration: underline;
}
.file-info {
margin-top: 0.5rem;
font-size: 0.85rem;
color: var(--muted-color);
font-family: var(--main-font); /* 应用自定义字体 */
}
/* 视频容器样式 */
.video-container {
position: relative;
width: 100%;
padding-top: 56.25%; /* 16:9比例 */
margin: 1rem 0;
overflow: hidden;
border-radius: 8px;
}
.mobile .video-container {
padding-top: 62.5%;
}
.video-container iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: none;
}
.download-section {
margin-top: 1rem;
display: flex;
gap: 1rem;
flex-wrap: wrap;
align-items: center;
}
/* 分享功能样式 */
.share-section {
display: flex;
align-items: center;
gap: 0.5rem;
margin-top: 1rem;
}
.mobile .share-section {
flex-direction: column;
align-items: stretch;
}
.share-button {
display: inline-flex;
align-items: center;
gap: 0.3rem;
background-color: var(--bg-color);
border: 1px solid var(--border-color);
padding: 0.5rem 1rem;
border-radius: 20px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 0.9rem;
color: var(--text-color);
font-family: var(--main-font); /* 应用自定义字体 */
}
.share-button:hover {
background-color: var(--primary-color);
color: white;
}
.share-link-container {
display: flex;
align-items: center;
gap: 0.5rem;
flex-grow: 1;
}
.mobile .share-link-container {
width: 100%;
}
.share-link {
flex-grow: 1;
padding: 0.5rem;
border: 1px solid var(--border-color);
border-radius: 4px;
font-size: 0.9rem;
color: var(--text-color);
background-color: var(--bg-color);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-family: var(--main-font); /* 应用自定义字体 */
}
.copy-button {
background-color: var(--bg-color);
border: 1px solid var(--border-color);
padding: 0.5rem;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
color: var(--text-color);
}
.copy-button:hover {
background-color: var(--primary-color);
color: white;
}
.jump-button {
background-color: var(--bg-color);
border: 1px solid var(--border-color);
padding: 0.5rem;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
color: var(--text-color);
text-decoration: none;
margin-left: 5px;
font-family: var(--main-font); /* 应用自定义字体 */
}
.jump-button:hover {
background-color: #27ae60;
color: white;
}
.copy-notification {
position: fixed;
bottom: 20px;
right: 20px;
background-color: var(--notification-bg);
color: var(--notification-text);
padding: 0.8rem 1.2rem;
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
opacity: 0;
transform: translateY(20px);
transition: all 0.3s ease;
pointer-events: none;
font-family: var(--main-font); /* 应用自定义字体 */
}
.copy-notification.show {
opacity: 1;
transform: translateY(0);
}
/* 回到顶部按钮样式 */
.back-to-top {
position: fixed;
bottom: 30px;
right: 30px;
width: 50px;
height: 50px;
background-color: var(--primary-color);
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
cursor: pointer;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
z-index: 999;
}
.back-to-top.show {
opacity: 1;
visibility: visible;
}
.back-to-top:hover {
background-color: var(--primary-hover);
transform: translateY(-5px);
}
/* 网站信息与在线人数样式 */
.site-info {
position: fixed;
bottom: 5px;
right: 5px;
font-size: 10px;
color: var(--muted-color);
z-index: 9999;
text-align: right;
line-height: 1.4;
font-family: var(--main-font); /* 应用自定义字体 */
}
/* 最近播放历史样式 */
.recent-history {
margin-top: 2rem;
}
.history-list {
max-height: calc(100vh - 180px); /* 计算高度,使其在视口内 */
overflow-y: auto;
margin-top: 1rem;
padding-right: 0.5rem;
}
/* 滚动条美化 */
.history-list::-webkit-scrollbar {
width: 6px;
}
.history-list::-webkit-scrollbar-track {
background: var(--bg-color);
border-radius: 3px;
}
.history-list::-webkit-scrollbar-thumb {
background: var(--border-color);
border-radius: 3px;
}
.history-list::-webkit-scrollbar-thumb:hover {
background: var(--muted-color);
}
.history-item {
padding: 0.8rem;
border-radius: 8px;
margin-bottom: 0.8rem;
cursor: pointer;
transition: background-color 0.2s ease;
display: flex;
justify-content: space-between;
align-items: center;
}
.history-item:hover {
background-color: var(--history-item-hover);
}
.history-info {
flex-grow: 1;
}
.history-title {
font-weight: 500;
margin: 0 0 0.2rem 0;
font-family: var(--main-font); /* 应用自定义字体 */
}
.history-artist {
font-size: 0.8rem;
color: var(--muted-color);
margin: 0;
font-family: var(--main-font); /* 应用自定义字体 */
}
.history-time {
font-size: 0.75rem;
color: var(--muted-color);
white-space: nowrap;
margin-left: 1rem;
font-family: var(--main-font); /* 应用自定义字体 */
}
.clear-history {
background: none;
border: 1px solid var(--border-color);
color: var(--muted-color);
padding: 0.4rem 0.8rem;
border-radius: 4px;
cursor: pointer;
font-size: 0.85rem;
transition: all 0.2s ease;
margin-top: 0.5rem;
font-family: var(--main-font); /* 应用自定义字体 */
}
.clear-history:hover {
background-color: var(--accent-color);
color: white;
border-color: var(--accent-color);
}
/* 用户菜单样式 */
.user-menu {
position: relative;
display: inline-block;
}
.user-dropdown {
position: absolute;
right: 0;
top: 100%;
background-color: var(--card-bg);
box-shadow: var(--card-shadow);
border-radius: 8px;
width: 200px;
padding: 0.5rem 0;
margin-top: 0.5rem;
display: none;
z-index: 101;
}
.user-dropdown.show {
display: block;
}
.user-dropdown a, .user-dropdown button {
display: block;
width: 100%;
padding: 0.8rem 1rem;
text-align: left;
background: none;
border: none;
color: var(--text-color);
text-decoration: none;
cursor: pointer;
font-size: 0.9rem;
box-sizing: border-box;
font-family: var(--main-font); /* 应用自定义字体 */
}
.user-dropdown a:hover, .user-dropdown button:hover {
background-color: var(--history-item-hover);
}
.user-info {
padding: 0.8rem 1rem;
border-bottom: 1px solid var(--border-color);
margin-bottom: 0.5rem;
}
.user-name {
font-weight: bold;
margin-bottom: 0.3rem;
font-family: var(--main-font); /* 应用自定义字体 */
}
.user-email {
font-size: 0.8rem;
color: var(--muted-color);
font-family: var(--main-font); /* 应用自定义字体 */
}
.afdian-btn {
display: inline-block;
margin-bottom: 1px; /* 与下方元素拉开距离,可根据需要调整 */
}
/* 用户头像样式 */
.user-avatar {
width: 30px;
height: 30px;
border-radius: 50%;
overflow: hidden;
margin-right: 8px;
display: inline-block;
vertical-align: middle;
}
.user-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
/* 下拉菜单中的头像 */
.dropdown-avatar {
width: 60px;
height: 60px;
border-radius: 50%;
overflow: hidden;
margin: 0 auto 10px;
}
.dropdown-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
/* 调整用户菜单按钮样式 */
.user-menu-btn {
display: flex;
align-items: center;
padding: 6px 12px;
/* 其他原有样式 */
}
/* 头像加载指示器 */
.avatar-loading {
position: relative;
background-color: #f0f0f0;
}
.avatar-loading::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.5) url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="%23333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><path d="M16 12a4 4 0 1 1-8 0"></path></svg>') no-repeat center;
background-size: 20px;
}
.dark-mode .avatar-loading {
background-color: #333;
}
.dark-mode .avatar-loading::after {
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="%23fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><path d="M16 12a4 4 0 1 1-8 0"></path></svg>');
}
</style>
</head>
<body>
<div class="app-container">
<!-- 侧边栏 - 固定在用户左侧 -->
<div class="sidebar" id="sidebar">
<h3>最近播放</h3>
<div class="recent-history">
<div class="history-list" id="history-list">
<!-- 最近播放历史将通过JavaScript动态填充 -->
<p class="history-placeholder" style="color: var(--muted-color); text-align: center; padding: 1rem;">
暂无播放记录
</p>
</div>
<button class="clear-history" id="clear-history">
<i class="fas fa-trash-alt"></i> 清空历史
</button>
</div>
</div>
<!-- 移动端侧边栏切换按钮 -->
<button class="sidebar-toggle" id="sidebar-toggle">
<i class="fas fa-history"></i>
</button>
<div class="main-wrapper">
<!-- 顶部工具栏 - 包含网站标题、暗黑模式切换和登录/注册 -->
<div class="top-bar">
<h1 class="site-title">音乐分享</h1>
<div style="display: flex; gap: 10px;">
<button class="theme-toggle" id="theme-toggle">
<i class="fas fa-moon"></i>
<span>暗黑模式</span>
</button>
<a href="./upload.php" class="theme-toggle">
<i class="fas fa-cloud-upload-alt"></i>
<span>上传音乐</span>
</a>
<?php if ($isLoggedIn && $userInfo): ?>
<!-- 登录状态显示用户菜单 -->
<div class="user-menu">
<button class="user-menu-btn" id="user-menu-btn">
<!-- 显示用户头像 -->
<div class="user-avatar avatar-loading" id="user-avatar-container">
<?php
// 定义从OSS获取头像路径的函数完全重构版
function getUserAvatar($userId, $config, &$debugInfo) {
// 记录调试信息
$debugInfo = [];
$debugInfo[] = "用户ID: {$userId}";
// 默认头像路径(使用两种不同的默认头像作为备用)
$defaultAvatars = [
'./static/icon/icon.png',
'https://shanwogou.cn/audio/static/icon/new-icon.png'
];
$debugInfo[] = "默认头像路径列表: " . implode(', ', $defaultAvatars);
// 检查配置文件
if (empty($config)) {
$debugInfo[] = "错误: 配置文件为空";
return $defaultAvatars;
}
// 检查OSS配置
$ossParams = ['oss_access_key', 'oss_secret_key', 'oss_endpoint', 'oss_bucket'];
$missingOssParams = [];
foreach ($ossParams as $param) {
if (!isset($config[$param]) || empty($config[$param])) {
$missingOssParams[] = $param;
} else {
$debugInfo[] = "找到OSS配置: {$param} = " . (strpos($param, 'key') !== false ? '******' : $config[$param]);
}
}
// 如果有缺失的配置项,直接返回默认头像
if (!empty($missingOssParams)) {
$debugInfo[] = "错误: 缺少OSS配置项 - " . implode(', ', $missingOssParams);
return $defaultAvatars;
}
// 构建多种可能的OSS头像URL格式
try {
// 1. 直接使用配置中的完整访问域名
$ossBaseUrl1 = rtrim($config['oss_endpoint'], '/');
$object = 'sunmusic/profile/' . $userId . '头像.png';
$avatarUrls[] = $ossBaseUrl1 . '/' . $object . '?t=' . time();
// 2. 使用bucket+endpoint格式
$ossBaseUrl2 = rtrim($config['oss_bucket'] . '.' . $config['oss_endpoint'], '/');
$avatarUrls[] = 'https://' . $ossBaseUrl2 . '/' . $object . '?t=' . time();
// 3. 不使用HTTPS的格式作为备选
$avatarUrls[] = 'http://' . $ossBaseUrl2 . '/' . $object . '?t=' . time();
$debugInfo[] = "构建的OSS头像URL列表: " . implode(', ', $avatarUrls);
// 返回所有可能的URL包括默认头像作为最后的备选
return array_merge($avatarUrls, $defaultAvatars);
} catch (Exception $e) {
$debugInfo[] = "错误: 构建URL时发生异常 - " . $e->getMessage();
return $defaultAvatars;
}
}
// 加载配置文件
$config = [];
$configFile = 'pmconfig.php';
$debugInfo = [];
if (file_exists($configFile)) {
$debugInfo[] = "找到配置文件: {$configFile}";
$config = include $configFile;
if (!is_array($config)) {
$debugInfo[] = "错误: 配置文件未返回数组";
$config = [];
}
} else {
$debugInfo[] = "错误: 未找到配置文件 {$configFile}";
}
// 获取用户ID尝试多种可能的键名
$userId = $userInfo['id'] ?? ($userInfo['user_id'] ?? ($userInfo['uid'] ?? 0));
$avatarUrls = getUserAvatar($userId, $config, $debugInfo);
// 显示调试信息(可用于排查问题)
echo '<div style="display:none;" id="avatar-debug-info"><pre>';
echo "头像加载调试信息:\n";
echo implode("\n", $debugInfo);
echo '</pre></div>';
// 输出初始img标签使用JavaScript动态尝试加载
echo '<img id="user-avatar-img" src="" alt="用户头像" style="display:none;">';
?>
</div>
<span><?php echo htmlspecialchars($userInfo['nickname'] ?? '用户'); ?></span>
</button>
<div class="user-dropdown" id="user-dropdown">
<div class="user-info">
<div class="user-name"><?php echo htmlspecialchars($userInfo['nickname'] ?? '用户'); ?></div>
<div class="user-email"><?php echo htmlspecialchars($userInfo['nickname'] ?? '进入个人设置查看头像'); ?></div>
<div class="user-email">
<?php
if (!empty($userInfo['email'])) {
echo htmlspecialchars($userInfo['email']);
} elseif (!empty($userInfo['phone'])) {
echo htmlspecialchars(substr($userInfo['phone'], 0, 3) . '****' . substr($userInfo['phone'], 7));
}
?>
</div>
</div>
<a href="./profile.php"><i class="fas fa-user-cog"></i> 个人设置</a>
<button id="logout-btn"><i class="fas fa-sign-out-alt"></i> 退出登录</button>
</div>
</div>
<?php else: ?>
<!-- 未登录状态显示登录和注册按钮 -->
<a href="login.php" class="theme-toggle">
<i class="fas fa-sign-in-alt"></i>
<span>登录</span>
</a>
<a href="register.php" class="theme-toggle">
<i class="fas fa-user-plus"></i>
<span>注册</span>
</a>
<?php endif; ?>
</div>
</div>
<div class="main-content">
<!-- 版权信息 -->
<div style="display:none">
<?php echo htmlspecialchars($copyrightInfo); ?>
</div>
<!-- 修改搜索框为表单提交到sou.php -->
<div class="search-container">
<form id="search-form" action="sou.php" method="get">
<input type="text" id="search-input" name="s" placeholder="搜索音乐名称或歌手..." value="<?php echo isset($_GET['s']) ? htmlspecialchars($_GET['s']) : ''; ?>">
</form>
</div>
<!-- 音乐分类导航 -->
<div>
<h2>音乐分类</h2>
<div class="category-nav">
<?php foreach ($categories as $key => $category): ?>
<button class="category-btn <?php echo $key === 'all' ? 'active' : ''; ?>"
data-category="<?php echo $key; ?>"
style="background-color: <?php echo $category['color']; ?>;
color: <?php echo $category['text_color']; ?>">
<?php echo $category['name']; ?>
</button>
<?php endforeach; ?>
</div>
<!-- 视图切换按钮 -->
<div class="view-toggle">
<button class="list-view-btn active" data-view="list">
<i class="fas fa-list"></i> 列表视图
</button>
<button class="card-view-btn" data-view="card">
<i class="fas fa-th-large"></i> 卡片视图
</button>
</div>
</div>
<!-- 快捷链接 -->
<div class="music-item">
<h2>其他链接</h2>
<a href="http://leonmmcoset.jjxmm.win:8010/developer_apps.php?id=9" class="recommendations-link" target="_blank" rel="noopener noreferrer">
<img src="https://shanwogou.cn/icon/leonapp.png" alt="LeonAPP" style="width: 120px; height: auto;">
</a>
<a href="https://space.bilibili.com/2143228115?spm_id_from=333.1387.0.0" class="recommendations-link" target="_blank" rel="noopener noreferrer">
<img src="https://shanwogou.cn/icon/bilibili.png" alt="bilibili" style="width: 120px; height: auto;">
</a>
<a href="https://afdian.com/a/sunmusic" class="recommendations-link" target="_blank" rel="noopener noreferrer">
<img src="https://shanwogou.cn/icon/aifadian.png" alt="爱发电支持" style="width: 120px; height: auto;">
</a>
</div>
<div class="music-item">
<h2>最新公告</h2>
<p class="artist-info">作者:<i class="fas fa-crown" style="color: #FFD700;">JGZ_YES</i></p>
<div id="latest-announcement">
<?php
// 数据库连接信息
$servername = "localhost";
$dbUsername = "a1sax1m9i";
$dbPassword = "a1sax1m9i";
$dbName = "a1sax1m9i";
// 创建连接并强制设置编码
$conn_gg = new mysqli($servername, $dbUsername, $dbPassword, $dbName);
// 检查连接
if ($conn_gg->connect_error) {
die("<p>数据库连接失败: " . htmlspecialchars($conn_gg->connect_error, ENT_QUOTES, 'UTF-8') . "</p>");
}
// 关键设置连接字符集为utf8mb4必须在查询前执行
if (!$conn_gg->set_charset("utf8mb4")) {
die("<p>字符集设置失败: " . htmlspecialchars($conn_gg->error, ENT_QUOTES, 'UTF-8') . "</p>");
}
// 查询最新公告(带错误处理)
$sql = "SELECT nr, time FROM announcements ORDER BY id DESC LIMIT 1";
$result = $conn_gg->query($sql);
if ($result === FALSE) {
// 错误信息仍需转义,避免恶意代码
echo "<p>查询错误: " . htmlspecialchars($conn_gg->error, ENT_QUOTES, 'UTF-8') . "</p>";
} elseif ($result->num_rows > 0) {
$row = $result->fetch_assoc();
// 1. 保留nl2br():将数据库中的\n换行符转为HTML的<br>标签
// 2. 移除htmlspecialchars()让HTML标签如<a>、<img>)正常解析
echo "<p>" . nl2br($row["nr"]) . "</p>";
// 发布时间仍需转义(纯文本,避免注入)
echo "<p class='file-info'>发布时间: " . htmlspecialchars($row["time"], ENT_QUOTES, 'UTF-8') . "</p>";
} else {
echo "<p>暂无公告</p>";
}
$conn_gg->close();
?>
</div>
<a href="gg.php" class="recommendations-link">
<i class="fa fa-history"></i>
<span>查看历史公告</span>
</a>
</div>
<!-- 音乐列表容器 -->
<div id="music-list-container" class="list-view">
<?php foreach ($musicList as $music): ?>
<?php
// 生成当前歌曲的分享链接
$shareUrl = "https://shanwogou.cn/audio/play.php" . "?play=" . $music['id'];
// 获取当前音乐分类的颜色配置
$categoryConfig = $categories[$music['category']];
?>
<div class="music-item" data-category="<?php echo $music['category']; ?>" data-id="<?php echo $music['id']; ?>">
<!-- 显示分类标签 -->
<span class="category-tag"
style="background-color: <?php echo $categoryConfig['color']; ?>;
color: <?php echo $categoryConfig['text_color']; ?>">
<?php echo $categoryConfig['name']; ?>
</span>
<h2><?php echo htmlspecialchars($music['title']); ?></h2>
<p class="artist-info">作者:<?php echo htmlspecialchars($music['artist']); ?></p>
<div class="custom-audio-player">
<div class="audio-controls">
<button class="audio-button play-pause" data-audio="<?php echo $music['id']; ?>">▶</button>
<button class="audio-button loop" data-audio="<?php echo $music['id']; ?>">🔄</button>
</div>
<div class="progress-container">
<div class="audio-progress" data-audio="<?php echo $music['id']; ?>">
<div class="progress-fill" data-audio="<?php echo $music['id']; ?>"></div>
<div class="progress-handle" data-audio="<?php echo $music['id']; ?>"></div>
</div>
<span class="time-display current-time" data-audio="<?php echo $music['id']; ?>">0:00</span>
<span class="time-separator">/</span>
<span class="time-display total-time" data-audio="<?php echo $music['id']; ?>"><?php echo $music['duration']; ?></span>
</div>
<div class="volume-control" data-audio="<?php echo $music['id']; ?>">
<button class="volume-button" data-audio="<?php echo $music['id']; ?>">🔊</button>
<input type="range" class="volume-slider" data-audio="<?php echo $music['id']; ?>" min="0" max="1" step="0.05" value="1">
</div>
</div>
<audio id="<?php echo $music['id']; ?>" class="audio-element" src="<?php echo htmlspecialchars($music['mp3']); ?>" type="audio/mpeg">
您的浏览器不支持音频播放
</audio>
<div class="video-container">
<iframe
src="https://player.bilibili.com/player.html?bvid=<?php echo $music['bvid']; ?>&page=1"
allowfullscreen="true"
loading="lazy">
</iframe>
</div>
<div class="download-section">
<p>下载:
<a href="<?php echo htmlspecialchars($music['mp3']); ?>" class="download-link" download>点我下载</a>
</p>
</div>
<!-- 分享功能 -->
<div class="share-section">
<div class="share-link-container">
<input type="text" class="share-link" value="<?php echo htmlspecialchars($shareUrl); ?>" readonly>
<button class="copy-button" data-audio="<?php echo $music['id']; ?>" title="复制链接">
<i class="fas fa-copy"></i>
</button>
<a href="<?php echo htmlspecialchars($shareUrl); ?>"
class="jump-button"
target="_blank"
title="在新窗口打开">
<i class="fas fa-external-link-alt"></i>
</a>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
</div>
<!-- 复制成功提示 -->
<div class="copy-notification" id="copy-notification">
链接已复制到剪贴板!
</div>
<!-- 回到顶部按钮 -->
<div class="back-to-top" id="back-to-top">
<i class="fas fa-arrow-up"></i>
</div>
<!-- 网站信息 -->
<div class="site-info" id="site-info">
<div>版本: <?php echo $siteVersion; ?></div>
<div>© <?php echo $bqsy; ?></div> <!--2024-2025 落日音乐. JGZ_YES. 版权所有-->
<div>歌曲资源如有侵权联系<a href="mailto:383260290@qq.com">383260290@qq.com</a>删除</div>
</div>
<script>
// 核心功能代码
(function(d, w) {
// 工具函数简写
var q = d.querySelector.bind(d);
var qa = d.querySelectorAll.bind(d);
var addEvt = function(el, evt, fn) {
if (el.addEventListener) el.addEventListener(evt, fn);
else if (el.attachEvent) el.attachEvent('on' + evt, fn);
};
// 格式化时间(秒 -> 分:秒)
function formatTime(seconds) {
if (isNaN(seconds)) return "0:00";
var minutes = Math.floor(seconds / 60);
var secs = Math.floor(seconds % 60);
return minutes + ":" + (secs < 10 ? "0" + secs : secs);
}
// 切换播放/暂停状态
function togglePlayback(audio, playBtn) {
if (audio.paused) {
// 暂停其他正在播放的音频
qa('.audio-element').forEach(function(otherAudio) {
if (otherAudio !== audio && !otherAudio.paused) {
otherAudio.pause();
q('.play-pause[data-audio="' + otherAudio.id + '"]').textContent = '▶';
}
});
audio.play();
playBtn.textContent = '⏸';
} else {
audio.pause();
playBtn.textContent = '▶';
}
}
// 初始化侧边栏切换(移动端)
function initSidebarToggle() {
const sidebar = q('#sidebar');
const toggleBtn = q('#sidebar-toggle');
// 点击切换按钮显示/隐藏侧边栏
addEvt(toggleBtn, 'click', function() {
sidebar.classList.toggle('show');
});
// 点击侧边栏外的区域关闭侧边栏
addEvt(d, 'click', function(e) {
if (!sidebar.contains(e.target) &&
e.target !== toggleBtn &&
!toggleBtn.contains(e.target) &&
sidebar.classList.contains('show')) {
sidebar.classList.remove('show');
}
});
// 在小屏幕上,点击历史项后关闭侧边栏
const historyItems = qa('.history-item');
historyItems.forEach(function(item) {
addEvt(item, 'click', function() {
// 检测是否为移动设备
if (document.documentElement.classList.contains('mobile')) {
sidebar.classList.remove('show');
}
});
});
}
// 初始化暗黑模式
function initDarkMode() {
const themeToggle = q('#theme-toggle');
const themeIcon = themeToggle.querySelector('i');
const themeText = themeToggle.querySelector('span');
// 检查用户偏好或本地存储
if (localStorage.getItem('theme') === 'dark' ||
(!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
d.body.classList.add('dark-mode');
themeIcon.classList.remove('fa-moon');
themeIcon.classList.add('fa-sun');
themeText.textContent = '明亮模式';
}
// 绑定切换事件
addEvt(themeToggle, 'click', function() {
d.body.classList.toggle('dark-mode');
if (d.body.classList.contains('dark-mode')) {
themeIcon.classList.remove('fa-moon');
themeIcon.classList.add('fa-sun');
themeText.textContent = '明亮模式';
localStorage.setItem('theme', 'dark');
} else {
themeIcon.classList.remove('fa-sun');
themeIcon.classList.add('fa-moon');
themeText.textContent = '暗黑模式';
localStorage.setItem('theme', 'light');
}
});
}
// 初始化用户菜单
function initUserMenu() {
const userMenuBtn = q('#user-menu-btn');
const userDropdown = q('#user-dropdown');
const logoutBtn = q('#logout-btn');
if (userMenuBtn && userDropdown) {
// 点击用户按钮显示/隐藏下拉菜单
addEvt(userMenuBtn, 'click', function(e) {
e.stopPropagation();
userDropdown.classList.toggle('show');
});
// 点击其他区域关闭下拉菜单
addEvt(d, 'click', function() {
userDropdown.classList.remove('show');
});
// 登出功能
if (logoutBtn) {
addEvt(logoutBtn, 'click', function() {
if (confirm('确定要退出登录吗?')) {
window.location.href = 'index.php?action=logout';
}
});
}
}
}
// 最近播放历史功能
function initPlayHistory() {
const historyList = q('#history-list');
const clearHistoryBtn = q('#clear-history');
const placeholder = historyList.querySelector('.history-placeholder');
// 从localStorage获取历史记录
function getHistory() {
return JSON.parse(localStorage.getItem('playHistory') || '[]');
}
// 保存历史记录到localStorage
function saveHistory(history) {
localStorage.setItem('playHistory', JSON.stringify(history));
}
// 更新历史记录UI
function updateHistoryUI() {
const history = getHistory();
// 清空现有内容(除了占位符)
const items = historyList.querySelectorAll('.history-item');
items.forEach(item => item.remove());
// 显示或隐藏占位符
if (history.length === 0) {
if (placeholder) placeholder.style.display = 'block';
} else {
if (placeholder) placeholder.style.display = 'none';
// 添加历史记录项
history.forEach(item => {
const historyItem = d.createElement('div');
historyItem.className = 'history-item';
historyItem.setAttribute('data-audio', item.id);
// 格式化时间
const date = new Date(item.timestamp);
const formattedTime = `${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
historyItem.innerHTML = `
<div class="history-info">
<h4 class="history-title">${item.title}</h4>
<p class="history-artist">${item.artist}</p>
</div>
<span class="history-time">${formattedTime}</span>
`;
// 点击历史项播放音乐
addEvt(historyItem, 'click', function() {
const audioId = this.getAttribute('data-audio');
const audio = q('#' + audioId);
const playBtn = q('.play-pause[data-audio="' + audioId + '"]');
if (audio && playBtn) {
// 滚动到音乐项
const musicItem = audio.closest('.music-item');
if (musicItem) {
musicItem.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
// 播放音乐
if (audio.paused) {
togglePlayback(audio, playBtn);
}
}
});
historyList.appendChild(historyItem);
});
}
}
// 添加到历史记录
window.addToHistory = function(songId, songTitle, artist) {
let history = getHistory();
// 移除已存在的相同歌曲(避免重复)
history = history.filter(item => item.id !== songId);
// 添加新记录到最前面
history.unshift({
id: songId,
title: songTitle,
artist: artist,
timestamp: new Date().getTime()
});
// 限制历史记录数量
if (history.length > 20) {
history = history.slice(0, 20);
}
saveHistory(history);
updateHistoryUI();
};
// 清空历史记录
addEvt(clearHistoryBtn, 'click', function() {
if (confirm('确定要清空所有播放历史吗?')) {
localStorage.removeItem('playHistory');
updateHistoryUI();
}
});
// 初始加载历史记录
updateHistoryUI();
}
// 播放进度记忆功能
function initPlaybackPositionMemory() {
// 保存播放进度
window.savePlaybackPosition = function(songId, position) {
const positions = JSON.parse(localStorage.getItem('playbackPositions') || '{}');
positions[songId] = {
position: position,
timestamp: new Date().getTime()
};
localStorage.setItem('playbackPositions', JSON.stringify(positions));
};
// 恢复播放进度
window.restorePlaybackPosition = function(songId) {
const positions = JSON.parse(localStorage.getItem('playbackPositions') || '{}');
return positions[songId] ? positions[songId].position : 0;
};
}
// 初始化标题动态变化功能
function initTitleEffects() {
const originalTitle = document.title;
let titleChangeTimer = null;
// 监听页面可见性变化
addEvt(document, 'visibilitychange', function() {
// 清除之前的定时器
if (titleChangeTimer) {
clearTimeout(titleChangeTimer);
titleChangeTimer = null;
}
if (document.hidden) {
// 页面不可见时显示挽留信息
document.title = '你不要走啊 ! ..(。•ˇ‸ˇ•。)…';
} else {
// 页面可见时先显示欢迎回来信息
document.title = '耶,又回来了!(^_^)';
// 3秒后恢复原始标题
titleChangeTimer = setTimeout(function() {
document.title = originalTitle;
titleChangeTimer = null;
}, 3000);
}
});
}
// 初始化播放器
function initAudioPlayers() {
const audios = qa('.audio-element');
audios.forEach(function(audio) {
const id = audio.id;
const playBtn = q('.play-pause[data-audio="' + id + '"]');
const loopBtn = q('.loop[data-audio="' + id + '"]');
const progressBar = q('.audio-progress[data-audio="' + id + '"]');
const progressFill = q('.progress-fill[data-audio="' + id + '"]');
const progressHandle = q('.progress-handle[data-audio="' + id + '"]');
const currentTime = q('.current-time[data-audio="' + id + '"]');
const totalTime = q('.total-time[data-audio="' + id + '"]');
const volumeBtn = q('.volume-button[data-audio="' + id + '"]');
const volumeSlider = q('.volume-slider[data-audio="' + id + '"]');
// 获取歌曲信息
const musicItem = audio.closest('.music-item');
const songTitle = musicItem.querySelector('h2').textContent;
const artist = musicItem.querySelector('.artist-info').textContent.replace('作者:', '');
let originalVolume = 1;
let isDragging = false;
// 音频加载完成 - 恢复上次播放进度
addEvt(audio, 'loadedmetadata', function() {
totalTime.textContent = formatTime(audio.duration);
audio.volume = volumeSlider.value;
// 恢复播放进度
const savedPosition = restorePlaybackPosition(id);
if (savedPosition > 0 && savedPosition < audio.duration) {
audio.currentTime = savedPosition;
// 更新UI
const progress = (savedPosition / audio.duration) * 100;
progressFill.style.width = progress + '%';
progressHandle.style.left = progress + '%';
currentTime.textContent = formatTime(savedPosition);
}
});
// 播放/暂停
addEvt(playBtn, 'click', function() {
togglePlayback(audio, playBtn);
// 添加到播放历史
addToHistory(id, songTitle, artist);
});
// 循环播放
addEvt(loopBtn, 'click', function() {
audio.loop = !audio.loop;
loopBtn.classList.toggle('active', audio.loop);
if (audio.loop) {
loopBtn.style.transform = 'scale(1.1)';
setTimeout(function() {
loopBtn.style.transform = 'scale(1)';
}, 200);
}
});
// 更新进度 - 并保存播放进度
addEvt(audio, 'timeupdate', function() {
if (!isDragging) {
const progress = (audio.currentTime / audio.duration) * 100;
progressFill.style.width = progress + '%';
progressHandle.style.left = progress + '%';
currentTime.textContent = formatTime(audio.currentTime);
// 定期保存播放进度每30秒或进度有显著变化时
if (audio.currentTime > 0 &&
(audio.currentTime % 30 < 0.1 ||
Math.abs(audio.currentTime - (audio.duration * 0.9)) < 0.5)) {
savePlaybackPosition(id, audio.currentTime);
}
}
});
// 播放结束时保存最终进度
addEvt(audio, 'ended', function() {
savePlaybackPosition(id, 0); // 播放结束后重置进度
playBtn.textContent = '▶';
});
// 进度条拖动
const startDrag = function(e) {
isDragging = true;
progressHandle.classList.add('dragging');
updateProgress(e);
};
const updateProgress = function(e) {
if (!audio.duration) return;
// 获取进度条的位置和宽度
const rect = progressBar.getBoundingClientRect();
let pos = (e.clientX - rect.left) / rect.width;
pos = Math.max(0, Math.min(1, pos)); // 限制在0-1之间
// 更新UI
const percentage = pos * 100;
progressFill.style.width = percentage + '%';
progressHandle.style.left = percentage + '%';
currentTime.textContent = formatTime(pos * audio.duration);
};
const endDrag = function() {
if (!isDragging) return;
isDragging = false;
progressHandle.classList.remove('dragging');
// 设置音频位置
const pos = parseFloat(progressFill.style.width) / 100;
audio.currentTime = pos * audio.duration;
savePlaybackPosition(id, audio.currentTime);
};
// 进度条事件
addEvt(progressBar, 'click', updateProgress);
addEvt(progressHandle, 'mousedown', startDrag);
addEvt(d, 'mousemove', function(e) { if (isDragging) updateProgress(e); });
addEvt(d, 'mouseup', endDrag);
addEvt(d, 'mouseleave', endDrag);
// 移动端触摸事件
addEvt(progressBar, 'touchstart', function(e) {
e.preventDefault();
startDrag(e.touches[0]);
});
addEvt(d, 'touchmove', function(e) {
if (isDragging) {
e.preventDefault();
updateProgress(e.touches[0]);
}
});
addEvt(d, 'touchend', endDrag);
// 音量控制
addEvt(volumeBtn, 'click', function() {
if (audio.volume > 0) {
originalVolume = audio.volume;
audio.volume = 0;
volumeBtn.textContent = '🔇';
volumeSlider.value = 0;
} else {
audio.volume = originalVolume;
volumeBtn.textContent = '🔊';
volumeSlider.value = originalVolume;
}
});
addEvt(volumeSlider, 'input', function() {
audio.volume = this.value;
originalVolume = this.value;
volumeBtn.textContent = this.value > 0 ? '🔊' : '🔇';
});
});
}
// 初始化视图切换
function initViewToggle() {
const listViewBtn = q('.list-view-btn');
const cardViewBtn = q('.card-view-btn');
const container = q('#music-list-container');
addEvt(listViewBtn, 'click', function() {
container.classList.remove('card-view');
container.classList.add('list-view');
listViewBtn.classList.add('active');
cardViewBtn.classList.remove('active');
localStorage.setItem('viewMode', 'list');
});
addEvt(cardViewBtn, 'click', function() {
container.classList.remove('list-view');
container.classList.add('card-view');
cardViewBtn.classList.add('active');
listViewBtn.classList.remove('active');
localStorage.setItem('viewMode', 'card');
});
// 恢复用户偏好的视图模式
const savedView = localStorage.getItem('viewMode');
if (savedView === 'card') {
cardViewBtn.click();
}
}
// 初始化分类筛选
function initCategoryFilter() {
const categoryBtns = qa('.category-btn');
const musicItems = qa('.music-item');
categoryBtns.forEach(function(btn) {
addEvt(btn, 'click', function() {
// 更新按钮状态
categoryBtns.forEach(b => b.classList.remove('active'));
this.classList.add('active');
const category = this.getAttribute('data-category');
// 筛选音乐项
musicItems.forEach(function(item) {
if (category === 'all' || item.getAttribute('data-category') === category) {
item.style.display = 'block';
} else {
item.style.display = 'none';
}
});
// 保存用户选择的分类
localStorage.setItem('selectedCategory', category);
});
});
// 恢复用户上次选择的分类
const savedCategory = localStorage.getItem('selectedCategory');
if (savedCategory) {
const savedBtn = q('.category-btn[data-category="' + savedCategory + '"]');
if (savedBtn) savedBtn.click();
}
}
// 初始化搜索功能(首页保留即时筛选,同时支持表单提交到搜索页)
function initSearch() {
const searchForm = q('#search-form');
const searchInput = q('#search-input');
const musicItems = qa('.music-item');
// 回车键提交表单
addEvt(searchInput, 'keypress', function(e) {
if (e.key === 'Enter') {
searchForm.submit();
}
});
// 首页保留即时筛选功能
addEvt(searchInput, 'input', function() {
const searchTerm = this.value.toLowerCase().trim();
musicItems.forEach(function(item) {
const title = item.querySelector('h2').textContent.toLowerCase();
const artist = item.querySelector('.artist-info').textContent.toLowerCase();
if (title.includes(searchTerm) || artist.includes(searchTerm)) {
item.style.display = 'block';
} else {
item.style.display = 'none';
}
});
});
}
// 初始化复制功能
function initCopyFunctionality() {
const copyButtons = qa('.copy-button');
const notification = q('#copy-notification');
copyButtons.forEach(function(btn) {
addEvt(btn, 'click', function() {
const audioId = this.getAttribute('data-audio');
const shareLink = q('.share-link[data-audio="' + audioId + '"]') ||
this.parentElement.querySelector('.share-link');
if (shareLink) {
shareLink.select();
d.execCommand('copy');
// 显示通知
notification.classList.add('show');
setTimeout(function() {
notification.classList.remove('show');
}, 2000);
}
});
});
}
// 初始化回到顶部按钮
function initBackToTop() {
const backToTopBtn = q('#back-to-top');
addEvt(w, 'scroll', function() {
if (w.pageYOffset > 300) {
backToTopBtn.classList.add('show');
} else {
backToTopBtn.classList.remove('show');
}
});
addEvt(backToTopBtn, 'click', function() {
w.scrollTo({
top: 0,
behavior: 'smooth'
});
});
}
// 检查URL参数自动播放指定音乐
function autoPlayFromUrl() {
const params = new URLSearchParams(w.location.search);
const playId = params.get('play');
if (playId) {
const audio = q('#' + playId);
const playBtn = q('.play-pause[data-audio="' + playId + '"]');
if (audio && playBtn) {
// 滚动到音乐项
const musicItem = audio.closest('.music-item');
if (musicItem) {
musicItem.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
// 播放音乐
setTimeout(function() {
if (audio.paused) {
togglePlayback(audio, playBtn);
}
}, 500);
}
}
}
// 初始化头像加载(核心修复部分)
function initAvatarLoading() {
// 获取头像URL列表从PHP传递过来
<?php
// 将头像URL列表转换为JavaScript数组
echo "const avatarUrls = " . json_encode($avatarUrls) . ";\n";
?>
// 显示调试信息
console.log("开始尝试加载头像URL列表:", avatarUrls);
// 获取头像元素
const userAvatarImg = q('#user-avatar-img');
const dropdownAvatarImg = q('#dropdown-avatar-img');
const userAvatarContainer = q('#user-avatar-container');
const dropdownAvatarContainer = q('#dropdown-avatar-container');
// 如果没有头像元素,直接返回
if (!userAvatarImg || !dropdownAvatarImg) {
console.log("未找到头像元素,跳过头像加载");
return;
}
// 尝试加载头像的函数
function tryLoadAvatar(urls, index = 0) {
// 如果所有URL都尝试过了使用最后一个默认头像
if (index >= urls.length) {
console.error("所有头像URL都加载失败使用默认头像");
userAvatarImg.src = urls[urls.length - 1];
dropdownAvatarImg.src = urls[urls.length - 1];
userAvatarImg.style.display = 'block';
dropdownAvatarImg.style.display = 'block';
userAvatarContainer.classList.remove('avatar-loading');
dropdownAvatarContainer.classList.remove('avatar-loading');
return;
}
// 尝试当前URL
const currentUrl = urls[index];
console.log(`尝试加载头像 (${index + 1}/${urls.length}):`, currentUrl);
// 创建临时图片对象测试加载
const testImg = new Image();
// 加载成功
testImg.onload = function() {
console.log(`头像加载成功:`, currentUrl);
userAvatarImg.src = currentUrl;
dropdownAvatarImg.src = currentUrl;
userAvatarImg.style.display = 'block';
dropdownAvatarImg.style.display = 'block';
userAvatarContainer.classList.remove('avatar-loading');
dropdownAvatarContainer.classList.remove('avatar-loading');
};
// 加载失败尝试下一个URL
testImg.onerror = function() {
console.error(`头像加载失败尝试下一个URL:`, currentUrl);
tryLoadAvatar(urls, index + 1);
};
// 设置超时时间5秒
testImg.timeout = 5000;
testImg.ontimeout = function() {
console.error(`头像加载超时尝试下一个URL:`, currentUrl);
tryLoadAvatar(urls, index + 1);
};
// 开始加载
testImg.src = currentUrl;
}
// 开始尝试加载头像
tryLoadAvatar(avatarUrls);
}
// 初始化所有功能
function initAll() {
initSidebarToggle();
initDarkMode();
initUserMenu();
initPlayHistory();
initPlaybackPositionMemory();
initTitleEffects();
initAudioPlayers();
initViewToggle();
initCategoryFilter();
initSearch();
initCopyFunctionality();
initBackToTop();
autoPlayFromUrl();
// 头像加载必须在最后初始化确保DOM已完全加载
initAvatarLoading();
}
// 页面加载完成后初始化
addEvt(w, 'load', initAll);
})(document, window);
</script>
</body>
</html>