Files
SunShineMusic/index.php
2025-09-25 11:33:03 +00:00

2131 lines
86 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.001";
$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);
}
.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>');
}
.recommendations-link {
/* 让链接成为一个块级元素,方便控制 */
display: inline-block;
/* 为 transform 和 box-shadow 的变化添加过渡效果, duration 0.3s, timing-function ease */
/* 这会让悬浮效果更丝滑,而不是瞬间变化 */
transition: transform 0.3s ease, box-shadow 0.3s ease;
/* 可选:移除图片下方可能出现的小间隙 */
line-height: 0;
}
/* 当鼠标悬浮在链接上时 */
.recommendations-link:hover {
transform: translateY(-5px) scale(1.05); /* 向上移动5px并放大1.05倍 */
box-shadow: 0 8px 15px rgba(0, 0, 0, 0.25);
}
/* 确保图片本身是响应式的,这里沿用你 inline 的 style */
.recommendations-link img {
/* 你原有的宽度设置,这里保持一致 */
width: 120px;
height: auto;
/* 可选:为图片添加圆角,视觉效果更好 */
/* border-radius: 8px; */
}
.music-item.centered-links {
display: flex;
flex-direction: column; /* 让子元素垂直排列 */
align-items: center; /* 水平居中对齐 */
justify-content: center; /* 垂直居中(如果容器有高度) */
text-align: center; /* 确保内部文本也居中 */
gap: 15px; /* 增加元素之间的间距,替代<br> */
padding: 20px 0; /* 上下增加一些内边距 */
}
</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 centered-links">
<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>
<a href="http://leonmmcoset.jjxmm.win:2000/JGZ_YES/SunShineMusic" class="recommendations-link" target="_blank" rel="noopener noreferrer">
<img src="https://shanwogou.cn/icon/leongit-jgz&leon.png" alt="LeonGit" 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 data-audio="<?php echo $music['id']; ?>">
<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);
}
// 获取下一首要播放的音频ID
function getNextAudioId(currentId) {
// 获取当前激活的分类
const activeCategory = q('.category-btn.active').getAttribute('data-category');
// 获取当前分类下所有可见的音乐项
let visibleItems = Array.from(qa(`.music-item[data-category="${activeCategory}"]`)).filter(item => {
return item.style.display !== 'none';
});
// 如果当前分类没有歌曲(不太可能),则获取所有歌曲
if (visibleItems.length === 0) {
visibleItems = Array.from(qa('.music-item'));
}
const totalItems = visibleItems.length;
if (totalItems === 0) return null;
// 查找当前歌曲在列表中的索引
let currentIndex = -1;
for (let i = 0; i < totalItems; i++) {
if (visibleItems[i].getAttribute('data-id') === currentId) {
currentIndex = i;
break;
}
}
// 如果没找到当前歌曲或只有一首歌,则返回第一首
if (currentIndex === -1 || totalItems === 1) {
return visibleItems[0].getAttribute('data-id');
}
// 返回下一首的ID如果是最后一首则返回第一首
const nextIndex = (currentIndex + 1) % totalItems;
return visibleItems[nextIndex].getAttribute('data-id');
}
// 切换播放/暂停状态
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 = '⏸';
// 为当前播放的音频注册结束事件,用于自动播放下一首
audio.onended = function() {
// 保存当前歌曲的进度为0
savePlaybackPosition(audio.id, 0);
playBtn.textContent = '▶';
// 获取下一首歌曲的ID
const nextId = getNextAudioId(audio.id);
if (nextId) {
const nextAudio = q('#' + nextId);
const nextPlayBtn = q('.play-pause[data-audio="' + nextId + '"]');
if (nextAudio && nextPlayBtn) {
// 滚动到下一首歌曲
const nextMusicItem = nextAudio.closest('.music-item');
if (nextMusicItem) {
nextMusicItem.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
// 播放下一首歌曲
setTimeout(function() {
togglePlayback(nextAudio, nextPlayBtn);
}, 500);
}
}
};
} 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');
function getHistory() { return JSON.parse(localStorage.getItem('playHistory') || '[]'); }
function saveHistory(history) { localStorage.setItem('playHistory', JSON.stringify(history)); }
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 = '耶,又回来了!(^_^)';
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;
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(() => 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);
if (audio.currentTime > 0 && (audio.currentTime % 30 < 0.1 || Math.abs(audio.currentTime - (audio.duration * 0.9)) < 0.5)) {
savePlaybackPosition(id, audio.currentTime);
}
}
});
// 进度条拖动逻辑
const startDrag = (e) => { isDragging = true; progressHandle.classList.add('dragging'); updateProgress(e); };
const updateProgress = (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));
const percentage = pos * 100;
progressFill.style.width = percentage + '%';
progressHandle.style.left = percentage + '%';
currentTime.textContent = formatTime(pos * audio.duration);
};
const endDrag = () => {
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', (e) => { if (isDragging) updateProgress(e); });
addEvt(d, 'mouseup', endDrag);
addEvt(d, 'mouseleave', endDrag);
addEvt(progressBar, 'touchstart', (e) => { e.preventDefault(); startDrag(e.touches[0]); });
addEvt(d, 'touchmove', (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 + '"]');
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 userAvatarContainer = q('#user-avatar-container');
// 如果没有头像元素,直接返回
if (!userAvatarImg || !userAvatarContainer) {
console.log("未找到头像元素,跳过头像加载");
return;
}
// 尝试加载头像的函数
function tryLoadAvatar(urls, index = 0) {
// 如果所有URL都尝试过了使用最后一个默认头像
if (index >= urls.length) {
console.error("所有头像URL都加载失败使用默认头像");
userAvatarImg.src = urls[urls.length - 1];
userAvatarImg.style.display = 'block';
userAvatarContainer.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;
userAvatarImg.style.display = 'block';
userAvatarContainer.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>