diff --git a/add_db.php b/add_db.php
new file mode 100644
index 0000000..69ceda2
--- /dev/null
+++ b/add_db.php
@@ -0,0 +1,27 @@
+query("SHOW COLUMNS FROM recommendations LIKE 'status'");
+ $columnExists = $result->fetch();
+
+ if (!$columnExists) {
+ // 添加状态字段(0:待审核, 1:已同意, 2:已驳回)
+ $pdo->exec("ALTER TABLE recommendations
+ ADD COLUMN status TINYINT NOT NULL DEFAULT 0,
+ ADD COLUMN reviewed_at DATETIME NULL");
+
+ echo "状态字段添加成功!请刷新页面继续操作。";
+ } else {
+ echo "状态字段已存在,无需重复添加。";
+ }
+ } catch(PDOException $e) {
+ die("操作失败: " . $e->getMessage());
+ }
+} else {
+ die("数据库连接失败,请先检查连接配置。");
+}
+?>
\ No newline at end of file
diff --git a/add_db_2.php b/add_db_2.php
new file mode 100644
index 0000000..cda44f6
--- /dev/null
+++ b/add_db_2.php
@@ -0,0 +1,26 @@
+query("SHOW COLUMNS FROM recommendations LIKE 'user_identifier'");
+ $columnExists = $result->fetch();
+
+ if (!$columnExists) {
+ // 添加用户标识字段(用于区分不同用户的推荐)
+ $pdo->exec("ALTER TABLE recommendations
+ ADD COLUMN user_identifier VARCHAR(255) NOT NULL DEFAULT ''");
+
+ echo "用户标识字段添加成功!请刷新页面继续操作。";
+ } else {
+ echo "用户标识字段已存在,无需重复添加。";
+ }
+ } catch(PDOException $e) {
+ die("操作失败: " . $e->getMessage());
+ }
+} else {
+ die("数据库连接失败,请先检查连接配置。");
+}
+?>
\ No newline at end of file
diff --git a/admin_music_upload.php b/admin_music_upload.php
new file mode 100644
index 0000000..d76b60c
--- /dev/null
+++ b/admin_music_upload.php
@@ -0,0 +1,305 @@
+&1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,//";
+ $output = shell_exec($command);
+
+ if ($output) {
+ $duration = trim($output);
+ // 转换为 MM:SS 格式
+ list($hours, $minutes, $seconds) = explode(':', $duration);
+ $seconds = floor((float)$seconds);
+ if ((int)$hours > 0) {
+ $minutes = (int)$minutes + (int)$hours * 60;
+ }
+ return sprintf('%d:%02d', $minutes, $seconds);
+ }
+ }
+
+ // 方法2: 使用getid3库(如果安装了)
+ if (class_exists('getID3')) {
+ $getID3 = new getID3;
+ $fileInfo = $getID3->analyze($file_path);
+ if (!empty($fileInfo['playtime_seconds'])) {
+ $seconds = floor($fileInfo['playtime_seconds']);
+ $minutes = floor($seconds / 60);
+ $seconds = $seconds % 60;
+ return sprintf('%d:%02d', $minutes, $seconds);
+ }
+ }
+
+ return false; // 无法获取时长
+}
+
+// 处理审核操作
+if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+ if (isset($_POST['approve_id'])) {
+ $id = $_POST['approve_id'];
+ try {
+ // 1. 获取待审核音乐信息(包含BV号和用户输入的时长)
+ $stmt = $conn->prepare("SELECT * FROM pending_music WHERE id = ?");
+ $stmt->execute([$id]);
+ $music = $stmt->fetch(PDO::FETCH_ASSOC);
+
+ if ($music) {
+ // 2. 验证音频时长(如果可能)
+ $duration_mismatch = false;
+ $actual_duration = getActualAudioDuration($music['file_path']);
+
+ if ($actual_duration && $actual_duration !== $music['duration']) {
+ // 时长不匹配,设置标志但仍继续处理(只是提醒管理员)
+ $duration_mismatch = true;
+ $message .= "注意:用户填写的时长({$music['duration']})与实际音频时长({$actual_duration})不匹配。";
+ }
+
+ // 3. 将信息插入正式音乐表(包含BV号字段)
+ // 如果有实际时长,使用实际时长覆盖用户输入
+ $final_duration = $actual_duration ?: $music['duration'];
+
+ $stmt_insert = $conn->prepare("INSERT INTO music
+ (title, artist, category, description, file_path, duration, upload_time, uploader_name, bvid)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
+ $stmt_insert->execute([
+ $music['title'], $music['artist'], $music['category'],
+ $music['description'], $music['file_path'], $final_duration,
+ $music['upload_time'], $music['uploader_name'], $music['bvid'] ?? ''
+ ]);
+
+ // 4. 从待审核表中删除
+ $stmt_delete = $conn->prepare("DELETE FROM pending_music WHERE id = ?");
+ $stmt_delete->execute([$id]);
+
+ // 构建成功消息
+ $base_message = "音乐《" . htmlspecialchars($music['title']) . "》已通过审核!";
+ if ($duration_mismatch) {
+ $message = $base_message . " " . $message;
+ } else {
+ $message = $base_message;
+ }
+ $message_type = "success";
+ }
+ } catch (PDOException $e) {
+ $message = "数据库错误: " . $e->getMessage();
+ $message_type = "error";
+ }
+ } elseif (isset($_POST['reject_id'])) {
+ $id = $_POST['reject_id'];
+ try {
+ // 获取文件路径用于删除
+ $stmt = $conn->prepare("SELECT file_path, title FROM pending_music WHERE id = ?");
+ $stmt->execute([$id]);
+ $music = $stmt->fetch(PDO::FETCH_ASSOC);
+
+ if ($music) {
+ // 删除服务器上的文件
+ if (file_exists($music['file_path'])) {
+ unlink($music['file_path']);
+ }
+
+ // 从待审核表中删除记录
+ $stmt_delete = $conn->prepare("DELETE FROM pending_music WHERE id = ?");
+ $stmt_delete->execute([$id]);
+
+ $message = "音乐《" . htmlspecialchars($music['title']) . "》已驳回并删除。";
+ $message_type = "success";
+ }
+ } catch (PDOException $e) {
+ $message = "数据库错误: " . $e->getMessage();
+ $message_type = "error";
+ }
+ }
+}
+
+// 获取所有待审核音乐(包含BV号)并计算实际时长
+try {
+ $stmt = $conn->query("SELECT * FROM pending_music ORDER BY upload_time DESC");
+ $pending_music = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+ // 为每条音乐获取实际时长并检查是否匹配
+ foreach ($pending_music as &$music) {
+ $actual_duration = getActualAudioDuration($music['file_path']);
+ $music['actual_duration'] = $actual_duration;
+ $music['duration_match'] = ($actual_duration === $music['duration']);
+ }
+ unset($music); // 解除引用
+} catch (PDOException $e) {
+ $message = "无法加载待审核列表: " . $e->getMessage();
+ $message_type = "error";
+ $pending_music = [];
+}
+
+$categories = [
+ 'cantonese' => '粤语歌曲', 'mandarin' => '国语歌曲', 'waiyu' => '外语歌曲',
+ 'classic' => '经典老歌', 'other' => '其他音乐'
+];
+?>
+
+
+
+
+
+ 管理员审核 - 音乐分享平台
+
+
+
+
+
+
+
+
+
+
+
+
+
当前没有待审核的音乐。
+
+
+
+
+ | ID |
+ 标题 |
+ 歌手 |
+ 上传者 |
+ 分类 |
+ BV号 |
+ 时长信息 |
+ 上传时间 |
+ 预览 |
+ 操作 |
+
+
+
+
+
+ |
+ |
+ |
+ |
+ |
+
+
+
+
+
+
+ 无
+
+ |
+
+
+
+
+ 用户输入:
+
+
+ 实际时长:
+
+
+
+
+
+
+ 时长不匹配,通过审核后将使用实际时长
+
+
+
+
+ 用户输入:
+
+
+ 无法获取实际音频时长
+
+
+
+ |
+ |
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/api.php b/api.php
new file mode 100644
index 0000000..6b7d501
--- /dev/null
+++ b/api.php
@@ -0,0 +1,278 @@
+connect_error) {
+ die(json_encode(array(
+ "success" => false,
+ "message" => "数据库连接失败: " . $conn->connect_error
+ )));
+}
+
+// 设置数据库连接字符集
+$conn->set_charset("utf8mb4");
+
+// 获取硬编码音乐数据
+function getHardcodedMusic() {
+ $musicPath = __DIR__ . '/data/music.php';
+
+ // 检查文件是否存在
+ if (!file_exists($musicPath)) {
+ return array(
+ "success" => false,
+ "message" => "硬编码音乐文件不存在",
+ "data" => array()
+ );
+ }
+
+ // 读取硬编码音乐(使用require_once并捕获返回值)
+ $hardcodedMusic = require_once $musicPath;
+
+ // 验证数据格式
+ if (is_array($hardcodedMusic)) {
+ return array(
+ "success" => true,
+ "message" => "成功获取硬编码音乐",
+ "data" => $hardcodedMusic
+ );
+ } else {
+ return array(
+ "success" => false,
+ "message" => "硬编码音乐格式不正确",
+ "data" => array()
+ );
+ }
+}
+
+// 从数据库获取音乐
+function getDatabaseMusic($conn) {
+ $sql = "SELECT id, title, artist, category, mp3, bvid, duration FROM music";
+ $result = $conn->query($sql);
+
+ $musicList = array();
+ if ($result && $result->num_rows > 0) {
+ while ($row = $result->fetch_assoc()) {
+ $musicList[] = $row;
+ }
+ return array(
+ "success" => true,
+ "message" => "成功获取数据库音乐",
+ "data" => $musicList
+ );
+ } else {
+ return array(
+ "success" => false,
+ "message" => "数据库中没有找到音乐数据",
+ "data" => array()
+ );
+ }
+}
+
+// 获取所有音乐(合并数据库和硬编码)
+function getAllMusic($conn) {
+ // 获取两种来源的音乐
+ $dbMusic = getDatabaseMusic($conn);
+ $hardcodedMusic = getHardcodedMusic();
+
+ // 合并音乐列表(去重处理)
+ $allMusic = array();
+ $ids = array();
+
+ // 添加数据库音乐
+ foreach ($dbMusic['data'] as $music) {
+ $id = $music['id'];
+ if (!in_array($id, $ids)) {
+ $ids[] = $id;
+ $allMusic[] = $music;
+ }
+ }
+
+ // 添加硬编码音乐
+ foreach ($hardcodedMusic['data'] as $music) {
+ $id = $music['id'];
+ if (!in_array($id, $ids)) {
+ $ids[] = $id;
+ $allMusic[] = $music;
+ }
+ }
+
+ return array(
+ "success" => true,
+ "message" => "共获取 " . count($allMusic) . " 首音乐(数据库: " . count($dbMusic['data']) . ", 硬编码: " . count($hardcodedMusic['data']) . ")",
+ "data" => $allMusic,
+ "sources" => array(
+ "database" => $dbMusic['success'],
+ "hardcoded" => $hardcodedMusic['success']
+ )
+ );
+}
+
+// 获取音乐标签(合并数据库和硬编码)
+function getMusicTags($conn) {
+ // 获取所有音乐
+ $allMusic = getAllMusic($conn);
+ $tags = array();
+
+ if ($allMusic['success']) {
+ foreach ($allMusic['data'] as $music) {
+ if (!empty($music['category']) && !in_array($music['category'], $tags)) {
+ $tags[] = $music['category'];
+ }
+ }
+ }
+
+ return array(
+ "success" => !empty($tags),
+ "message" => empty($tags) ? "没有找到音乐标签" : "成功获取 " . count($tags) . " 个标签",
+ "data" => $tags
+ );
+}
+
+// 获取音乐地址(同时检查数据库和硬编码)
+/**
+ * 获取音乐地址(同时检查数据库和硬编码)- 修复版
+ * 增加了对 SQL 语句准备失败的检查,避免触发 500 错误。
+ */
+/**
+ * 获取音乐的分享播放URL
+ * 直接返回格式为 shanwogou.cn/audio/play.php?play=音乐id 的链接
+ */
+function getMusicUrl($conn) {
+ // 1. 验证输入的音乐ID
+ $musicId = $_GET['id'] ?? '';
+ if (empty($musicId)) {
+ return array(
+ "success" => false,
+ "message" => "音乐 ID 不能为空"
+ );
+ }
+
+ // 2. 核心逻辑:检查该ID的音乐是否存在于系统中
+ // (我们不再返回真实MP3地址,但需要确认音乐ID是有效的)
+ $musicExists = false;
+
+ // 2.1 先检查数据库
+ $sql = "SELECT id FROM music WHERE id = ?"; // 只需检查ID是否存在
+ $stmt = $conn->prepare($sql);
+
+ if ($stmt) { // 检查prepare是否成功
+ $stmt->bind_param("s", $musicId);
+ $stmt->execute();
+ $stmt->store_result();
+
+ if ($stmt->num_rows > 0) {
+ $musicExists = true;
+ }
+ $stmt->close();
+ }
+
+ // 2.2 如果数据库中不存在,再检查硬编码文件
+ if (!$musicExists) {
+ $hardcodedMusic = getHardcodedMusic();
+ if ($hardcodedMusic['success']) {
+ foreach ($hardcodedMusic['data'] as $music) {
+ if ((string)$music['id'] === (string)$musicId) {
+ $musicExists = true;
+ break;
+ }
+ }
+ }
+ }
+
+ // 3. 根据检查结果返回响应
+ if ($musicExists) {
+ // 如果音乐存在,生成并返回分享URL
+ $shareUrl = "https://shanwogou.cn/audio/play.php?play=" . urlencode($musicId);
+ return array(
+ "success" => true,
+ "data" => $shareUrl,
+ "source" => "share_link" // 标记来源为分享链接
+ );
+ } else {
+ // 如果音乐不存在,返回错误信息
+ return array(
+ "success" => false,
+ "message" => "没有找到该 ID 的音乐"
+ );
+ }
+}
+
+function getAnnouncements($conn) {
+ // 准备SQL查询,从announcements表中获取所有记录,并按ID倒序排列(最新的在前)
+ $sql = "SELECT id, nr, time FROM announcements ORDER BY id DESC";
+
+ // 执行查询
+ $result = $conn->query($sql);
+
+ // 初始化一个空数组来存储公告数据
+ $announcementList = array();
+
+ // 检查查询是否成功且有结果
+ if ($result && $result->num_rows > 0) {
+ // 循环遍历所有结果行
+ while ($row = $result->fetch_assoc()) {
+ // 将每一行公告数据添加到数组中
+ $announcementList[] = $row;
+ }
+
+ // 返回成功响应,包含公告数据
+ return array(
+ "success" => true,
+ "message" => "成功获取 " . count($announcementList) . " 条公告",
+ "data" => $announcementList
+ );
+ } else {
+ // 如果查询失败或没有数据,返回失败响应
+ return array(
+ "success" => false,
+ "message" => "没有找到公告数据或查询失败",
+ "data" => array()
+ );
+ }
+}
+
+// 处理请求
+// 处理请求
+$action = $_GET['action'] ?? '';
+
+switch ($action) {
+ case 'getAllMusic':
+ $response = getAllMusic($conn);
+ break;
+ case 'getMusicTags':
+ $response = getMusicTags($conn);
+ break;
+ case 'getMusicUrl':
+ $response = getMusicUrl($conn);
+ break;
+ // --- 新增部分 ---
+ case 'getAnnouncements':
+ $response = getAnnouncements($conn);
+ break;
+ // --- 新增结束 ---
+ default:
+ $response = array(
+ "success" => false,
+ "message" => "无效的操作,请使用 action=getAllMusic、getMusicTags、getMusicUrl 或 getAnnouncements"
+ );
+}
+
+// 输出JSON响应
+echo json_encode($response, JSON_UNESCAPED_UNICODE);
+
+// 关闭数据库连接
+$conn->close();
+?>