/dev/null 2>/dev/null &'); } } // 过渡到新的 md5 密码并生成默认 token、user_agent (如果不存在或为空) if (!preg_match('/^[a-f0-9]{32}$/i', $Config['manage_password']) || empty($Config['token']) || empty($Config['user_agent'])) { if (!preg_match('/^[a-f0-9]{32}$/i', $Config['manage_password'])) { $Config['manage_password'] = md5($Config['manage_password']); } if (empty($Config['token'])) { $Config['token'] = substr(bin2hex(random_bytes(5)), 0, 10); // 生成 10 位随机字符串 } if (empty($Config['user_agent'])) { $Config['user_agent'] = substr(bin2hex(random_bytes(5)), 0, 10); // 生成 10 位随机字符串 } file_put_contents($configPath, json_encode($Config, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); } // 处理密码更新请求 if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['change_password'])) { $oldPassword = md5($_POST['old_password']); $newPassword = md5($_POST['new_password']); // 验证原密码是否正确 if ($oldPassword === $Config['manage_password']) { // 原密码正确,更新配置中的密码 $Config['manage_password'] = $newPassword; // 将新配置写回 config.json file_put_contents($configPath, json_encode($Config, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); // 设置密码更改成功的标志变量 $passwordChanged = true; } else { $passwordChangeError = "原密码错误"; } } // 检查是否提交登录表单 if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['login'])) { $password = md5($_POST['password']); // 验证密码 if ($password === $Config['manage_password']) { // 密码正确,设置会话变量 $_SESSION['loggedin'] = true; // 设置会话变量,表明用户可以访问 phpliteadmin.php 、 tinyfilemanager.php $_SESSION['can_access_phpliteadmin'] = true; $_SESSION['can_access_tinyfilemanager'] = true; } else { $error = "密码错误"; } } // 处理密码更改成功后的提示 $passwordChangedMessage = isset($passwordChanged) ? "

密码已更改

" : ''; $passwordChangeErrorMessage = isset($passwordChangeError) ? "

$passwordChangeError

" : ''; // 检查是否已登录 if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) { // 显示登录表单 include 'assets/html/login.html'; exit; } // 更新配置 function updateConfigFields() { global $Config, $configPath; // 获取和过滤表单数据 $config_keys = array_keys(array_filter($_POST, function($key) { return $key !== 'update_config'; }, ARRAY_FILTER_USE_KEY)); foreach ($config_keys as $key) { ${$key} = is_numeric($_POST[$key]) ? intval($_POST[$key]) : $_POST[$key]; } // 处理 URL 列表和频道别名 $xml_urls = array_values(array_map(function($url) { return preg_replace('/^#\s*(\S+)(\s*#.*)?$/', '# $1$2', trim(str_replace([",", ":"], [",", ":"], $url))); }, explode("\n", $xml_urls))); $interval_time = $interval_hour * 3600 + $interval_minute * 60; $mysql = ["host" => $mysql_host, "dbname" => $mysql_dbname, "username" => $mysql_username, "password" => $mysql_password]; // 解析频道别名 $channel_mappings = []; if ($mappings = trim($_POST['channel_mappings'] ?? '')) { foreach (explode("\n", $mappings) as $line) { if ($line = trim($line)) { list($search, $replace) = preg_split('/=》|=>/', $line); $channel_mappings[trim($search)] = trim(str_replace(",", ",", trim($replace)), '[]'); } } } // 解析频道 EPG 数据 $channel_bind_epg = isset($_POST['channel_bind_epg']) ? array_filter(array_reduce(json_decode($_POST['channel_bind_epg'], true), function($result, $item) { $epgSrc = preg_replace('/^【已停用】/', '', $item['epg_src']); if (!empty($item['channels'])) $result[$epgSrc] = trim(str_replace(",", ",", trim($item['channels'])), '[]'); return $result; }, [])) : $Config['channel_bind_epg']; // 更新 $Config $oldConfig = $Config; $config_keys_filtered = array_filter($config_keys, function($key) { return !preg_match('/^(mysql_|interval_)/', $key); }); $config_keys_new = ['channel_bind_epg', 'interval_time', 'mysql']; $config_keys_save = array_merge($config_keys_filtered, $config_keys_new); foreach ($config_keys_save as $key) { if (isset($$key)) { $Config[$key] = $$key; } } // 检查 MySQL 有效性 $db_type_set = true; if ($Config['db_type'] === 'mysql') { try { $dsn = "mysql:host={$Config['mysql']['host']};dbname={$Config['mysql']['dbname']};charset=utf8mb4"; $db = new PDO($dsn, $Config['mysql']['username'] ?? null, $Config['mysql']['password'] ?? null); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (PDOException $e) { $Config['db_type'] = 'sqlite'; $db_type_set = false; } } // 将新配置写回 config.json file_put_contents($configPath, json_encode($Config, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); // 重新启动 cron.php ,设置新的定时任务 if ($oldConfig['start_time'] !== $start_time || $oldConfig['end_time'] !== $end_time || $oldConfig['interval_time'] !== $interval_time) { exec('php cron.php > /dev/null 2>/dev/null &'); } return ['db_type_set' => $db_type_set]; } // 处理服务器请求 try { $requestMethod = $_SERVER['REQUEST_METHOD']; $dbResponse = null; if ($requestMethod == 'GET') { // 确定操作类型 $action_map = [ 'get_update_logs', 'get_cron_logs', 'get_channel', 'get_epg_by_channel', 'get_icon', 'get_channel_bind_epg', 'get_channel_match', 'get_gen_list', 'get_live_data', 'parse_source_info', 'toggle_status', 'download_data', 'delete_unused_icons', 'delete_unused_live_data', 'get_version_log' ]; $action = key(array_intersect_key($_GET, array_flip($action_map))) ?: ''; // 根据操作类型执行不同的逻辑 switch ($action) { case 'get_update_logs': // 获取更新日志 $dbResponse = $db->query("SELECT * FROM update_log")->fetchAll(PDO::FETCH_ASSOC); break; case 'get_cron_logs': // 获取 cron 日志 $dbResponse = $db->query("SELECT * FROM cron_log")->fetchAll(PDO::FETCH_ASSOC); break; case 'get_channel': // 获取频道 $channels = $db->query("SELECT DISTINCT channel FROM epg_data ORDER BY channel ASC")->fetchAll(PDO::FETCH_COLUMN); $channelMappings = $Config['channel_mappings']; $mappedChannels = []; foreach ($channelMappings as $mapped => $original) { if (($index = array_search(strtoupper($mapped), $channels)) !== false) { $mappedChannels[] = [ 'original' => $mapped, 'mapped' => $original ]; unset($channels[$index]); // 从剩余频道中移除 } } foreach ($channels as $channel) { $mappedChannels[] = [ 'original' => $channel, 'mapped' => '' ]; } $dbResponse = [ 'channels' => $mappedChannels, 'count' => count($mappedChannels) ]; break; case 'get_epg_by_channel': // 查询 $channel = $_GET['channel']; $date = urldecode($_GET['date']); $stmt = $db->prepare("SELECT epg_diyp FROM epg_data WHERE channel = :channel AND date = :date"); $stmt->execute([':channel' => $channel, ':date' => $date]); $result = $stmt->fetch(PDO::FETCH_ASSOC); // 获取单条结果 if ($result) { $epgData = json_decode($result['epg_diyp'], true); $epgSource = $epgData['source'] ?? ''; $epgOutput = ""; foreach ($epgData['epg_data'] as $epgItem) { $epgOutput .= "{$epgItem['start']} {$epgItem['title']}\n"; } $dbResponse = ['channel' => $channel, 'source' => $epgSource, 'date' => $date, 'epg' => trim($epgOutput)]; } else { $dbResponse = ['channel' => $channel, 'source' => '', 'date' => $date, 'epg' => '无节目信息']; } break; case 'get_icon': // 是否显示无节目单的内置台标 if(isset($_GET['get_all_icon'])) { $iconList = $iconListMerged; } // 获取并合并数据库中的频道和 $iconList 中的频道,去重后按字母排序 $allChannels = array_unique(array_merge( $db->query("SELECT DISTINCT channel FROM epg_data ORDER BY channel ASC")->fetchAll(PDO::FETCH_COLUMN), array_keys($iconList) )); sort($allChannels); // 将默认台标插入到频道列表的开头 $defaultIcon = [ ['channel' => '【默认台标】', 'icon' => $Config['default_icon'] ?? ''] ]; $channelsInfo = array_map(function($channel) use ($iconList) { return ['channel' => $channel, 'icon' => $iconList[$channel] ?? '']; }, $allChannels); $withIcons = array_filter($channelsInfo, function($c) { return !empty($c['icon']);}); $withoutIcons = array_filter($channelsInfo, function($c) { return empty($c['icon']);}); $dbResponse = [ 'channels' => array_merge($defaultIcon, $withIcons, $withoutIcons), 'count' => count($allChannels) ]; break; case 'get_channel_bind_epg': // 获取频道绑定的 EPG $channels = $db->query("SELECT DISTINCT channel FROM epg_data ORDER BY channel ASC")->fetchAll(PDO::FETCH_COLUMN); $channelBindEpg = $Config['channel_bind_epg'] ?? []; $xmlUrls = $Config['xml_urls']; $dbResponse = array_map(function($epgSrc) use ($channelBindEpg) { $cleanEpgSrc = trim(explode('#', strpos($epgSrc, '=>') !== false ? explode('=>', $epgSrc)[1] : ltrim($epgSrc, '# '))[0]); $isInactive = strpos(trim($epgSrc), '#') === 0; return [ 'epg_src' => ($isInactive ? '【已停用】' : '') . $cleanEpgSrc, 'channels' => $channelBindEpg[$cleanEpgSrc] ?? '' ]; }, array_filter($xmlUrls, function($epgSrc) { // 去除空行和包含 tvmao、cntv 的行 return !empty(ltrim($epgSrc, '# ')) && strpos($epgSrc, 'tvmao') === false && strpos($epgSrc, 'cntv') === false; })); $dbResponse = array_merge( array_filter($dbResponse, function($item) { return strpos($item['epg_src'], '【已停用】') === false; }), array_filter($dbResponse, function($item) { return strpos($item['epg_src'], '【已停用】') !== false; }) ); break; case 'get_channel_match': // 获取频道匹配 $channels = $db->query("SELECT channel FROM gen_list")->fetchAll(PDO::FETCH_COLUMN); if (empty($channels)) { echo json_encode(['ori_channels' => [], 'clean_channels' => [], 'match' => [], 'type' => []]); exit; } $cleanChannels = explode("\n", t2s(implode("\n", array_map('cleanChannelName', $channels)))); $epgData = $db->query("SELECT channel FROM epg_data")->fetchAll(PDO::FETCH_COLUMN); $channelMap = array_combine($cleanChannels, $channels); $matches = []; foreach ($cleanChannels as $cleanChannel) { $originalChannel = $channelMap[$cleanChannel]; $matchResult = null; $matchType = '未匹配'; if (in_array($cleanChannel, $epgData)) { $matchResult = $cleanChannel; $matchType = '精确匹配'; if ($cleanChannel !== $originalChannel) { $matchType = '别名/忽略'; } } else { foreach ($epgData as $epgChannel) { if (stripos($epgChannel, $cleanChannel) !== false) { if (!isset($matchResult) || strlen($epgChannel) < strlen($matchResult)) { $matchResult = $epgChannel; $matchType = '正向模糊'; } } elseif (stripos($cleanChannel, $epgChannel) !== false) { if (!isset($matchResult) || strlen($epgChannel) > strlen($matchResult)) { $matchResult = $epgChannel; $matchType = '反向模糊'; } } } } $matches[$cleanChannel] = [ 'ori_channel' => $originalChannel, 'clean_channel' => $cleanChannel, 'match' => $matchResult, 'type' => $matchType ]; } $dbResponse = $matches; break; case 'get_gen_list': // 获取生成列表 $dbResponse = $db->query("SELECT channel FROM gen_list")->fetchAll(PDO::FETCH_COLUMN); break; case 'get_live_data': // 读取文件内容 function readFileContent($filePath) { return file_exists($filePath) ? file_get_contents($filePath) : ''; } $sourceContent = readFileContent($liveDir . 'source.txt'); $templateContent = readFileContent($liveDir . 'template.txt'); // 读取 CSV 文件并返回关联数组 function readCsvFile($filePath, $key = null) { if (!file_exists($filePath)) return []; $data = []; if (($file = fopen($filePath, 'r')) !== false) { $header = fgetcsv($file); while (($row = fgetcsv($file)) !== false) { if (empty(array_filter($row)) || count($row) !== count($header)) continue; $rowData = array_combine($header, $row); if ($key && isset($rowData[$key])) { $data[$rowData[$key]] = $rowData; // 使用指定键映射 } else { $data[] = $rowData; } } fclose($file); } return $data; } $channelsInfo = readCsvFile($liveDir . 'channels_info.csv', 'tag'); $channelsData = readCsvFile($liveDir . 'channels.csv'); // 更新 channelsData 中的 resolution 和 speed foreach ($channelsData as &$row) { if (isset($channelsInfo[$row['tag']])) { $row['resolution'] = str_replace("x", "
x
", $channelsInfo[$row['tag']]['resolution']); $row['speed'] = $channelsInfo[$row['tag']]['speed']; if (is_numeric($row['speed'])) { $row['speed'] .= '
ms';} } } generateLiveFiles($channelsData, 'tv', $saveOnly = true); // 重新生成 M3U 和 TXT 文件 $dbResponse = ['source_content' => $sourceContent, 'template_content' => $templateContent, 'channels' => $channelsData,]; break; case 'parse_source_info': // 解析直播源 $parseResult = doParseSourceInfo(); if ($parseResult !== true) { $dbResponse = ['success' => 'part', 'message' => $parseResult]; } else { $dbResponse = ['success' => 'full']; } break; case 'toggle_status': // 切换状态 $toggleField = $_GET['toggle_button'] === 'toggleLiveSourceSyncBtn' ? 'live_source_auto_sync' : ($_GET['toggle_button'] === 'toggleLiveChannelNameProcessBtn' ? 'live_channel_name_process' : ''); $currentStatus = isset($Config[$toggleField]) && $Config[$toggleField] == 1 ? 1 : 0; $newStatus = ($currentStatus == 1) ? 0 : 1; $Config[$toggleField] = $newStatus; file_put_contents($configPath, json_encode($Config, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); $dbResponse = ['status' => $newStatus]; break; case 'download_data': // 下载数据 $url = filter_var(($_GET['url']), FILTER_VALIDATE_URL); if ($url) { $data = downloadData($url, '', 5); if ($data !== false) { $dbResponse = ['success' => true, 'data' => $data]; } else { $dbResponse = ['success' => false, 'message' => '无法获取URL内容']; } } else { $dbResponse = ['success' => false, 'message' => '无效的URL']; } break; case 'delete_unused_icons': // 清理未在使用的台标 $iconUrls = array_merge( array_map(function($url) { return parse_url($url, PHP_URL_PATH); }, $iconList), [parse_url($Config["default_icon"], PHP_URL_PATH)] ); $iconPath = __DIR__ . '/data/icon'; $deletedCount = 0; foreach (scandir($iconPath) as $file) { if ($file === '.' || $file === '..') continue; $iconRltPath = '/data/icon/' . $file; if (!in_array($iconRltPath, $iconUrls)) { if (@unlink($iconPath . '/' . $file)) { $deletedCount++; } } } $dbResponse = ['success' => true, 'message' => "共清理了 $deletedCount 个台标"]; break; case 'delete_unused_live_data': // 清理未在使用的直播源缓存、未出现在频道列表中的修改记录 $sourceFilePath = $liveDir . 'source.txt'; $sourceContent = file_exists($sourceFilePath) ? file_get_contents($sourceFilePath) : ''; $urls = array_map('trim', explode("\n", $sourceContent)); // 遍历 live/file 目录,删除未使用的文件 $parentRltPath = '/' . basename(__DIR__) . '/data/live/file/'; // 相对路径 $deletedFileCount = 0; foreach (scandir($liveFileDir) as $file) { if ($file === '.' || $file === '..') continue; $fileRltPath = $parentRltPath . $file; if (!array_filter($urls, function($url) use ($fileRltPath) { $url = trim(explode('#', ltrim($url, '# '))[0]); // 处理注释 $urlmd5 = md5(urlencode($url)); // 计算 md5 return $url && (stripos($fileRltPath, $url) !== false || stripos($fileRltPath, $urlmd5) !== false); })) { if (@unlink($liveFileDir . $file)) { // 如果没有匹配的 URL,删除文件 $deletedFileCount++; } } } // 删除 modifications.csv 未在 channels.csv 中出现的条目 $channelsFilePath = $liveDir . 'channels.csv'; $modificationsFilePath = $liveDir . 'modifications.csv'; $deletedRecordCount = 0; if (file_exists($channelsFilePath) && file_exists($modificationsFilePath)) { // 读取 channels.csv 中的 tag 字段 $channelTags = []; $file = fopen($channelsFilePath, 'r'); $header = fgetcsv($file); while (($row = fgetcsv($file)) !== false) { $channelTags[] = $row[array_search('tag', $header)]; } fclose($file); // 过滤 modifications.csv 数据并统计移除行数 $file = fopen($modificationsFilePath, 'r'); $modificationsHeader = fgetcsv($file); $filteredData = []; while (($row = fgetcsv($file)) !== false) { if (in_array($row[array_search('tag', $modificationsHeader)], $channelTags)) { $filteredData[] = $row; } else { $deletedRecordCount++; } } fclose($file); // 写回过滤后的数据 $file = fopen($modificationsFilePath, 'w'); fputcsv($file, $modificationsHeader); foreach ($filteredData as $row) { fputcsv($file, $row); } fclose($file); } $dbResponse = ['success' => true, 'message' => "共清理了 $deletedFileCount 个缓存文件, $deletedRecordCount 条修改记录。"]; break; case 'get_version_log': // 获取更新日志 $checkUpdateEnable = !isset($Config['check_update']) || $Config['check_update'] == 1; $checkUpdate = isset($_GET['do_check_update']) && $_GET['do_check_update'] === 'true'; if (!$checkUpdateEnable && $checkUpdate) { echo json_encode(['success' => true, 'is_updated' => false]); return; } $localFile = 'assets/CHANGELOG.md'; $url = 'https://gitee.com/taksssss/EPG-Server/raw/main/CHANGELOG.md'; $isUpdated = false; $updateMessage = ''; if ($checkUpdate) { $remoteContent = @file_get_contents($url); if ($remoteContent === false) { echo json_encode(['success' => false, 'message' => '无法获取远程版本日志']); return; } $localContent = file_exists($localFile) ? file_get_contents($localFile) : ''; if (strtok($localContent, "\n") !== strtok($remoteContent, "\n")) { file_put_contents($localFile, $remoteContent); $isUpdated = !empty($localContent) ? true : false; $updateMessage = '

🔔 检测到新版本,请自行更新。(该提醒仅显示一次)

'; } } $markdownContent = file_exists($localFile) ? file_get_contents($localFile) : false; if ($markdownContent === false) { echo json_encode(['success' => false, 'message' => '无法读取版本日志']); return; } require_once 'assets/Parsedown.php'; $htmlContent = (new Parsedown())->text($markdownContent); $dbResponse = ['success' => true, 'content' => $updateMessage . $htmlContent, 'is_updated' => $isUpdated]; break; default: $dbResponse = null; break; } if ($dbResponse !== null) { header('Content-Type: application/json'); echo json_encode($dbResponse); exit; } } // 处理 POST 请求 if ($requestMethod === 'POST') { // 定义操作类型和对应的条件 $actions = [ 'update_config' => isset($_POST['update_config']), 'set_gen_list' => isset($_GET['set_gen_list']), 'import_config' => isset($_POST['importExport']) && !empty($_FILES['importFile']['tmp_name']), 'export_config' => isset($_POST['importExport']) && empty($_FILES['importFile']['tmp_name']), 'upload_icon' => isset($_FILES['iconFile']), 'update_icon_list' => isset($_POST['update_icon_list']), 'upload_source_file' => isset($_FILES['liveSourceFile']), 'save_content_to_file' => isset($_POST['save_content_to_file']), 'save_source_info' => isset($_POST['save_source_info']), 'update_config_field' => isset($_POST['update_config_field']), ]; // 确定操作类型 $action = ''; foreach ($actions as $key => $condition) { if ($condition) { $action = $key; break; } } switch ($action) { case 'update_config': // 更新配置 ['db_type_set' => $db_type_set] = updateConfigFields(); echo json_encode([ 'db_type_set' => $db_type_set, 'interval_time' => $Config['interval_time'], 'start_time' => $Config['start_time'], 'end_time' => $Config['end_time'] ]); exit; case 'set_gen_list': // 设置生成列表 $data = json_decode(file_get_contents("php://input"), true)['data'] ?? ''; try { $db->beginTransaction(); $db->exec("DELETE FROM gen_list"); $lines = array_filter(array_map('trim', explode("\n", $data))); foreach ($lines as $line) { $stmt = $db->prepare("INSERT INTO gen_list (channel) VALUES (:channel)"); $stmt->bindValue(':channel', $line, PDO::PARAM_STR); $stmt->execute(); } $db->commit(); echo 'success'; } catch (PDOException $e) { $db->rollBack(); echo "数据库操作失败: " . $e->getMessage(); } exit; case 'import_config': // 导入配置 $zip = new ZipArchive(); $importFile = $_FILES['importFile']['tmp_name']; $successFlag = false; $message = ""; if ($zip->open($importFile) === TRUE) { if ($zip->extractTo('.')) { $successFlag = true; $message = "导入成功!
3秒后自动刷新页面……"; } else { $message = "导入失败!解压过程中发生问题。"; } $zip->close(); } else { $message = "导入失败!无法打开压缩文件。"; } echo json_encode(['success' => $successFlag, 'message' => $message]); exit; case 'export_config': $zip = new ZipArchive(); $zipFileName = 't.gz'; if ($zip->open($zipFileName, ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE) { $dataDir = __DIR__ . '/data'; $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($dataDir), RecursiveIteratorIterator::LEAVES_ONLY ); foreach ($files as $file) { if (!$file->isDir()) { $filePath = $file->getRealPath(); $relativePath = 'data/' . substr($filePath, strlen($dataDir) + 1); $zip->addFile($filePath, $relativePath); } } $zip->close(); header('Content-Type: application/zip'); header('Content-Disposition: attachment; filename=' . $zipFileName); readfile($zipFileName); unlink($zipFileName); } exit; case 'upload_icon': // 上传图标 $file = $_FILES['iconFile']; $fileName = $file['name']; $uploadFile = $iconDir . $fileName; if ($file['type'] === 'image/png' && move_uploaded_file($file['tmp_name'], $uploadFile)) { $iconUrl = $serverUrl . '/data/icon/' . basename($fileName); echo json_encode(['success' => true, 'iconUrl' => $iconUrl]); } else { echo json_encode(['success' => false, 'message' => '文件上传失败']); } exit; case 'update_icon_list': // 更新图标 $iconList = []; $updatedIcons = json_decode($_POST['updatedIcons'], true); // 遍历更新数据 foreach ($updatedIcons as $channelData) { $channelName = strtoupper(trim($channelData['channel'])); if ($channelName === '【默认台标】') { // 保存默认台标到 config.json $Config['default_icon'] = $channelData['icon'] ?? ''; file_put_contents($configPath, json_encode($Config, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); } else { // 处理普通台标数据 $iconList[$channelName] = $channelData['icon']; } } // 过滤掉图标值为空和频道名为空的条目 $iconList = array_filter($iconList, function($icon, $channel) { return !empty($icon) && !empty($channel); }, ARRAY_FILTER_USE_BOTH); if (file_put_contents($iconListPath, json_encode($iconList, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)) === false) { echo json_encode(['success' => false, 'message' => '更新 iconList.json 时发生错误']); } else { echo json_encode(['success' => true]); } exit; case 'upload_source_file': // 上传直播源文件 $file = $_FILES['liveSourceFile']; $fileName = $file['name']; $uploadFile = $liveFileDir . $fileName; if (move_uploaded_file($file['tmp_name'], $uploadFile)) { $liveSourceUrl = '/data/live/file/' . basename($fileName); $sourceFilePath = $liveDir . 'source.txt'; $currentContent = file_get_contents($sourceFilePath); if (!file_exists($sourceFilePath) || strpos($currentContent, $liveSourceUrl) === false) { // 如果文件不存在或文件中没有该 URL,将其追加到文件末尾 $contentToAppend = trim($currentContent) ? PHP_EOL . $liveSourceUrl : $liveSourceUrl; file_put_contents($sourceFilePath, $contentToAppend, FILE_APPEND); } echo json_encode(['success' => true]); } else { echo json_encode(['success' => false, 'message' => '文件上传失败']); } exit; case 'save_content_to_file': // 保存内容到文件 $filePath = __DIR__ . $_POST['file_path'] ?? ''; $content = $_POST['content'] ?? ''; if (file_put_contents($filePath, str_replace(",", ",", $content)) !== false) { echo json_encode(['success' => true]); } else { http_response_code(500); echo json_encode(['success' => false]); } exit; case 'save_source_info': // 更新配置文件 $Config['live_tvg_logo_enable'] = (int)$_POST['live_tvg_logo_enable']; $Config['live_tvg_id_enable'] = (int)$_POST['live_tvg_id_enable']; $Config['live_tvg_name_enable'] = (int)$_POST['live_tvg_name_enable']; if (file_put_contents($configPath, json_encode($Config, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)) === false) { http_response_code(500); echo json_encode(['success' => false, 'message' => '保存配置文件失败']); exit; } // 保存直播源信息 $content = json_decode($_POST['content'], true); if (empty($content)) { echo json_encode(['success' => false, 'message' => '无效的数据']); exit; } $fileName = $_POST['file_path'] ? 'file/' . md5(urlencode($_POST['file_path'])) : 'tv'; generateLiveFiles($content, $fileName, $saveOnly = true); // 重新生成 M3U 和 TXT 文件 echo json_encode(['success' => true]); exit; case 'update_config_field': // 更新单个字段 foreach ($_POST as $key => $value) { // 排除 update_config_field 字段 if ($key !== 'update_config_field') { $Config[$key] = is_numeric($value) ? intval($value) : $value; } } if (file_put_contents($configPath, json_encode($Config, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)) !== false) { echo json_encode(['success' => $Config]); } else { http_response_code(500); echo '保存失败'; } exit; } } } catch (Exception $e) { // 处理数据库连接错误 } // 生成配置管理表单 include 'assets/html/manage.html'; ?>