直接上传源码到你网站根目录
然后访问
http(s)://自己的域名/lives.php 即可
lives.php
<?php
require_once 'functions.php';
// 初始化变量
$txtToM3uResult = '';
$m3uToTxtResult = '';
$message = '';
$activeTab = isset($_GET['tab']) ? $_GET['tab'] : 'txt2m3u';
// 处理POST请求
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['save_epg'])) {
setcookie('epg_url', $_POST['epg_url'], time() + 86400 * 30);
$message = 'EPG地址已保存';
}
if (isset($_POST['convert_txt_m3u'])) {
$content = $_POST['txt_content'];
$epgUrl = isset($_COOKIE['epg_url']) ? $_COOKIE['epg_url'] : 'https://epg.catvod.com/epg.xml';
$rules = json_decode(isset($_COOKIE['rules']) ? $_COOKIE['rules'] : '[]', true);
$txtToM3uResult = convertTXTToM3U($content, $epgUrl, $rules);
$activeTab = 'txt2m3u';
}
if (isset($_POST['convert_m3u_txt'])) {
$content = $_POST['m3u_content'];
$m3uToTxtResult = convertM3UToTXT($content);
$activeTab = 'm3u2txt';
}
if (isset($_POST['add_rule'])) {
$keyword = $_POST['keyword'];
$group = $_POST['group'];
$rules = json_decode(isset($_COOKIE['rules']) ? $_COOKIE['rules'] : '[]', true);
$rules[] = ['keyword' => $keyword, 'group' => $group];
setcookie('rules', json_encode($rules), time() + 86400 * 30);
$message = '规则已添加';
$activeTab = 'config';
}
if (isset($_POST['delete_rule'])) {
$index = $_POST['rule_index'];
$rules = json_decode(isset($_COOKIE['rules']) ? $_COOKIE['rules'] : '[]', true);
unset($rules[$index]);
$rules = array_values($rules);
setcookie('rules', json_encode($rules), time() + 86400 * 30);
$message = '规则已删除';
$activeTab = 'config';
}
}
// 获取当前保存的EPG和规则
$currentEpg = isset($_COOKIE['epg_url']) ? $_COOKIE['epg_url'] : 'https://epg.catvod.com/epg.xml';
$currentRules = json_decode(isset($_COOKIE['rules']) ? $_COOKIE['rules'] : '[]', true);
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>在线 TXT/M3U 转换工具</title>
<style>
/* 基础样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
body {
background-color: #f8f9fa;
color: #333;
line-height: 1.6;
}
/* 蓝色顶栏 */
.header {
background-color: #007bff;
color: white;
padding: 15px 20px;
font-weight: 600;
font-size: 18px;
}
/* 页面容器 */
.container {
max-width: 1200px;
margin: 0 auto;
}
/* 标签导航栏 */
.tab-nav {
display: flex;
justify-content: center;
background-color: #fff;
border-bottom: 1px solid #dee2e6;
}
.tab-link {
padding: 12px 20px;
cursor: pointer;
color: #495057;
text-decoration: none;
font-weight: 500;
position: relative;
}
.tab-link.active {
color: #007bff;
border-bottom: 2px solid #007bff;
}
/* 内容区域 */
.content-section {
padding: 25px;
background-color: white;
}
.tab-content {
display: none;
opacity: 0;
transition: opacity 0.3s;
}
.tab-content.active {
display: block;
opacity: 1;
}
/* 蓝色边框标题 */
.blue-title {
border-left: 4px solid #007bff;
padding-left: 12px;
margin: 20px 0 15px 0;
font-size: 16px;
font-weight: 600;
color: #495057;
}
/* 输入区域 */
input[type="text"],
input[type="file"],
textarea {
width: 100%;
padding: 10px 12px;
margin-bottom: 15px;
border: 1px solid #ced4da;
border-radius: 4px;
font-size: 14px;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
input[type="text"]:focus,
textarea:focus {
border-color: #80bdff;
outline: 0;
box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25);
}
textarea {
min-height: 150px;
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 13px;
}
/* 自定义文件输入 */
.file-input-container {
position: relative;
margin-bottom: 15px;
}
.file-input-label {
display: inline-block;
padding: 8px 16px;
background-color: #f8f9fa;
border: 1px solid #ced4da;
border-radius: 4px;
cursor: pointer;
color: #495057;
transition: all 0.2s;
}
.file-input-label:hover {
background-color: #e9ecef;
}
.file-input {
position: absolute;
left: -9999px;
}
.file-name {
margin-left: 10px;
color: #6c757d;
font-size: 14px;
}
/* 按钮 */
.btn {
padding: 8px 16px;
background-color: #f8f9fa;
color: #212529;
border: 1px solid #ced4da;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
margin-right: 8px;
margin-bottom: 8px;
}
.btn:hover {
background-color: #e9ecef;
}
.btn-primary {
background-color: #007bff;
color: white;
border-color: #007bff;
}
.btn-primary:hover {
background-color: #0069d9;
border-color: #0062cc;
}
.btn-danger {
background-color: #dc3545;
color: white;
border-color: #dc3545;
}
.btn-danger:hover {
background-color: #c82333;
border-color: #bd2130;
}
.button-group {
margin-bottom: 20px;
}
/* 搜索框 */
.search-box {
position: relative;
margin-bottom: 20px;
}
.search-box input {
padding-left: 30px;
}
.search-box:before {
content: "🔍";
position: absolute;
left: 10px;
top: 50%;
transform: translateY(-50%);
color: #6c757d;
font-size: 14px;
}
/* 表格样式 */
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
table, th, td {
border: 1px solid #dee2e6;
}
th {
background-color: #f8f9fa;
padding: 10px;
text-align: left;
font-weight: 600;
color: #495057;
}
td {
padding: 10px;
text-align: left;
}
tr:nth-child(even) {
background-color: #f8f9fa;
}
tr:hover {
background-color: #f1f3f5;
}
/* 结果区域 */
.result-textarea {
background-color: #f8f9fa;
font-weight: normal;
}
/* 消息样式 */
.message {
padding: 12px 15px;
margin-bottom: 20px;
border-radius: 4px;
background-color: #d4edda;
border: 1px solid #c3e6cb;
color: #155724;
animation: fadeIn 0.5s;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
/* 两列表单布局 */
.form-row {
display: flex;
gap: 15px;
margin-bottom: 15px;
}
.form-row > * {
flex: 1;
margin-bottom: 0;
}
/* 底部 */
.footer {
text-align: center;
padding: 20px;
color: #6c757d;
font-size: 14px;
margin-top: 10px;
border-top: 1px solid #e9ecef;
}
</style>
</head>
<body>
<div class="header">在线 TXT/M3U 转换工具</div>
<div class="container">
<div class="tab-nav">
<a href="#config" class="tab-link <?php echo $activeTab == 'config' ? 'active' : ''; ?>" onclick="switchTab('config')">配置</a>
<a href="#txt2m3u" class="tab-link <?php echo $activeTab == 'txt2m3u' ? 'active' : ''; ?>" onclick="switchTab('txt2m3u')">TXT 转 M3U</a>
<a href="#m3u2txt" class="tab-link <?php echo $activeTab == 'm3u2txt' ? 'active' : ''; ?>" onclick="switchTab('m3u2txt')">M3U 转 TXT</a>
</div>
<div class="content-section">
<?php if ($message): ?>
<div class="message"><?php echo htmlspecialchars($message); ?></div>
<?php endif; ?>
<!-- 配置标签内容 -->
<div id="config" class="tab-content <?php echo $activeTab == 'config' ? 'active' : ''; ?>">
<div class="blue-title">EPG 地址</div>
<form method="post">
<input type="text" name="epg_url" value="<?php echo htmlspecialchars($currentEpg); ?>" placeholder="例如: https://epg.catvod.com/epg.xml">
<button type="submit" name="save_epg" class="btn btn-primary">保存</button>
</form>
<div class="blue-title">分组规则</div>
<form method="post">
<div class="form-row">
<input type="text" name="keyword" placeholder="关键词" required>
<input type="text" name="group" placeholder="分组名" required>
</div>
<button type="submit" name="add_rule" class="btn btn-primary">添加规则</button>
</form>
<?php if (!empty($currentRules)): ?>
<table>
<thead>
<tr>
<th>关键词</th>
<th>分组名</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php foreach ($currentRules as $index => $rule): ?>
<tr>
<td><?php echo htmlspecialchars($rule['keyword']); ?></td>
<td><?php echo htmlspecialchars($rule['group']); ?></td>
<td>
<form method="post" style="margin:0">
<input type="hidden" name="rule_index" value="<?php echo $index; ?>">
<button type="submit" name="delete_rule" class="btn btn-danger">删除</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<p style="color:#6c757d;margin-top:15px;">暂无分组规则,请添加规则。</p>
<?php endif; ?>
</div>
<!-- TXT转M3U标签内容 -->
<div id="txt2m3u" class="tab-content <?php echo $activeTab == 'txt2m3u' ? 'active' : ''; ?>">
<form method="post" enctype="multipart/form-data">
<div class="file-input-container">
<label for="txt_file" class="file-input-label">选择文件</label>
<input type="file" id="txt_file" name="txt_file" accept=".txt" class="file-input" onchange="updateFileName(this, 'txt_filename')">
<span id="txt_filename" class="file-name">未选择任何文件</span>
</div>
<div class="blue-title">TXT 内容</div>
<textarea name="txt_content" id="txt_content" placeholder="频道名称,链接 或 分组名,#genre#"></textarea>
<div class="button-group">
<button type="submit" name="convert_txt_m3u" class="btn btn-primary">转换为 M3U</button>
<button type="button" onclick="clearForm('txt')" class="btn">清除</button>
<button type="button" onclick="copyResult('txt2m3u_result')" class="btn">复制结果</button>
<button type="button" onclick="saveFile('txt2m3u_result', 'm3u')" class="btn">保存文件</button>
</div>
<div class="search-box">
<input type="text" id="filter_txt" placeholder="输入关键字过滤频道" oninput="filterContent(this.value, 'txt_content')">
</div>
<?php if ($txtToM3uResult): ?>
<div class="blue-title">转换结果</div>
<textarea id="txt2m3u_result" class="result-textarea" readonly><?php echo htmlspecialchars($txtToM3uResult); ?></textarea>
<?php endif; ?>
</form>
</div>
<!-- M3U转TXT标签内容 -->
<div id="m3u2txt" class="tab-content <?php echo $activeTab == 'm3u2txt' ? 'active' : ''; ?>">
<form method="post" enctype="multipart/form-data">
<div class="file-input-container">
<label for="m3u_file" class="file-input-label">选择文件</label>
<input type="file" id="m3u_file" name="m3u_file" accept=".m3u,.m3u8" class="file-input" onchange="updateFileName(this, 'm3u_filename')">
<span id="m3u_filename" class="file-name">未选择任何文件</span>
</div>
<div class="blue-title">M3U 内容</div>
<textarea name="m3u_content" id="m3u_content" placeholder="#EXTINF:-1 tvg-name="频道名称" group-title="分组名称",频道名称"></textarea>
<div class="button-group">
<button type="submit" name="convert_m3u_txt" class="btn btn-primary">转换为 TXT</button>
<button type="button" onclick="clearForm('m3u')" class="btn">清除</button>
<button type="button" onclick="copyResult('m3u2txt_result')" class="btn">复制结果</button>
<button type="button" onclick="saveFile('m3u2txt_result', 'txt')" class="btn">保存文件</button>
</div>
<div class="search-box">
<input type="text" id="filter_m3u" placeholder="输入关键字过滤频道" oninput="filterContent(this.value, 'm3u_content')">
</div>
<?php if ($m3uToTxtResult): ?>
<div class="blue-title">转换结果</div>
<textarea id="m3u2txt_result" class="result-textarea" readonly><?php echo htmlspecialchars($m3uToTxtResult); ?></textarea>
<?php endif; ?>
</form>
</div>
</div>
<div class="footer">
© 2025 Catvod.com - 所有权利保留
</div>
</div>
<script>
// 标签切换
function switchTab(tabId) {
// 隐藏所有标签内容
document.querySelectorAll('.tab-content').forEach(function(tab) {
tab.classList.remove('active');
});
// 取消所有标签激活状态
document.querySelectorAll('.tab-link').forEach(function(link) {
link.classList.remove('active');
});
// 激活选中的标签
document.getElementById(tabId).classList.add('active');
document.querySelector(`.tab-link[href="#${tabId}"]`).classList.add('active');
// 阻止默认行为
event.preventDefault();
}
// 文件上传预览
document.addEventListener('DOMContentLoaded', function() {
const txtFileInput = document.getElementById('txt_file');
const m3uFileInput = document.getElementById('m3u_file');
if (txtFileInput) {
txtFileInput.addEventListener('change', function() {
const file = this.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
document.getElementById('txt_content').value = e.target.result;
};
reader.readAsText(file);
}
});
}
if (m3uFileInput) {
m3uFileInput.addEventListener('change', function() {
const file = this.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
document.getElementById('m3u_content').value = e.target.result;
};
reader.readAsText(file);
}
});
}
// 如果页面加载后有消息,5秒后自动隐藏
const messageBox = document.querySelector('.message');
if (messageBox) {
setTimeout(() => {
messageBox.style.display = 'none';
}, 5000);
}
});
// 更新文件名显示
function updateFileName(fileInput, spanId) {
const span = document.getElementById(spanId);
if (fileInput.files.length > 0) {
span.textContent = fileInput.files[0].name;
} else {
span.textContent = '未选择任何文件';
}
}
// 清除表单内容
function clearForm(type) {
if (type === 'txt') {
document.getElementById('txt_content').value = '';
document.getElementById('txt_file').value = '';
document.getElementById('txt_filename').textContent = '未选择任何文件';
const resultArea = document.getElementById('txt2m3u_result');
if (resultArea) resultArea.value = '';
} else if (type === 'm3u') {
document.getElementById('m3u_content').value = '';
document.getElementById('m3u_file').value = '';
document.getElementById('m3u_filename').textContent = '未选择任何文件';
const resultArea = document.getElementById('m3u2txt_result');
if (resultArea) resultArea.value = '';
}
}
// 复制结果
function copyResult(resultId) {
const resultArea = document.getElementById(resultId);
if (!resultArea || !resultArea.value) {
alert('没有可复制的内容');
return;
}
resultArea.select();
document.execCommand('copy');
// 显示临时提示
const originalBg = resultArea.style.backgroundColor;
resultArea.style.backgroundColor = '#d4edda';
setTimeout(() => {
resultArea.style.backgroundColor = originalBg;
}, 300);
alert('内容已复制到剪贴板');
}
// 保存为文件
function saveFile(resultId, type) {
const resultArea = document.getElementById(resultId);
if (!resultArea || !resultArea.value) {
alert('没有可保存的内容');
return;
}
const blob = new Blob([resultArea.value], {type: 'text/plain'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = type === 'm3u' ? 'playlist.m3u' : 'channels.txt';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// 过滤内容
function filterContent(keyword, textareaId) {
const textarea = document.getElementById(textareaId);
if (!textarea || !textarea.value) return;
if (!textarea.getAttribute('data-original')) {
textarea.setAttribute('data-original', textarea.value);
}
const originalContent = textarea.getAttribute('data-original');
if (!keyword) {
textarea.value = originalContent;
return;
}
const lines = originalContent.split('\n');
const filteredLines = lines.filter(line =>
line.toLowerCase().includes(keyword.toLowerCase())
);
textarea.value = filteredLines.join('\n');
}
</script>
</body>
</html>
functions.php
<?php
/**
* 将TXT格式转换为M3U格式
*/
function convertTXTToM3U($content, $epgUrl = '', $rules = []) {
$lines = explode("\n", $content);
$m3u = "#EXTM3U\n";
if (!empty($epgUrl)) {
$m3u .= "#EXTM3U x-tvg-url=\"" . $epgUrl . "\"\n";
}
$currentGroup = "未分组";
foreach ($lines as $line) {
$line = trim($line);
if (empty($line)) continue;
// 处理分组标记行
if (strpos($line, '#genre#') !== false) {
$parts = explode(',', $line);
if (count($parts) > 0) {
$currentGroup = trim($parts[0]);
}
continue;
}
$parts = explode(',', $line, 2);
if (count($parts) < 2) continue;
$channelName = trim($parts[0]);
$url = trim($parts[1]);
// 应用分组规则
$groupTitle = $currentGroup;
if (!empty($rules)) {
foreach ($rules as $rule) {
if (stripos($channelName, $rule['keyword']) !== false) {
$groupTitle = $rule['group'];
break;
}
}
}
$m3u .= "#EXTINF:-1 tvg-name=\"" . $channelName . "\" group-title=\"" . $groupTitle . "\"," . $channelName . "\n";
$m3u .= $url . "\n";
}
return $m3u;
}
/**
* 将M3U格式转换为TXT格式
*/
function convertM3UToTXT($content) {
$lines = explode("\n", $content);
$txt = "";
$currentChannel = "";
$groups = array();
// 第一次遍历,收集所有分组和频道
for ($i = 0; $i < count($lines); $i++) {
$line = trim($lines[$i]);
if (empty($line) || $line == "#EXTM3U" || strpos($line, "#EXTM3U x-tvg-url=") === 0) continue;
if (strpos($line, "#EXTINF") === 0) {
// 获取分组信息
preg_match('/group-title="([^"]+)"/', $line, $groupMatches);
$groupName = isset($groupMatches[1]) ? $groupMatches[1] : "未分组";
// 获取频道名称
preg_match('/,(.*)$/', $line, $channelMatches);
$channelName = isset($channelMatches[1]) ? trim($channelMatches[1]) : "";
// 获取URL
if ($i + 1 < count($lines)) {
$url = trim($lines[$i + 1]);
if (preg_match('/^(http|rtmp|rtsp)/', $url)) {
if (!isset($groups[$groupName])) {
$groups[$groupName] = array();
}
$groups[$groupName][] = array(
'name' => $channelName,
'url' => $url
);
}
}
}
}
// 第二次处理,按分组生成TXT
foreach ($groups as $groupName => $channels) {
$txt .= $groupName . ",#genre#\n";
foreach ($channels as $channel) {
$txt .= $channel['name'] . "," . $channel['url'] . "\n";
}
$txt .= "\n"; // 每个分组后添加空行
}
return $txt;
}