Skip to content

Commit

Permalink
feat: 直播源停用、保持修改,token修改、生效范围
Browse files Browse the repository at this point in the history
1. ✨新增:停用部分直播源
2. ✨新增:保持直播源修改
3. ✨新增:使用 token 访问 EPG 服务
4. ✨新增:修改 token 页面
5. 🐛修复:设置默认台标后,生成台标数据异常
6. 🐛修复:服务器日志时区异常
7. 🐛修复:数据库更新日志日期未换行
8. 🐛修复:猫接口补充数据时输出异常
  • Loading branch information
taksssss committed Dec 3, 2024
1 parent 17ffaeb commit 7ed3f89
Show file tree
Hide file tree
Showing 12 changed files with 497 additions and 297 deletions.
325 changes: 168 additions & 157 deletions CHANGELOG.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ RUN apk --no-cache --update \
apache2-ssl \
curl \
memcached \
tzdata \
php83-apache2 \
php83-bcmath \
php83-bz2 \
Expand Down
5 changes: 0 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
> [!IMPORTANT]
> **访问路径**`http://{服务器IP地址}:5678/epg/` 修改为 `http://{服务器IP地址}:5678/`(兼容旧路径)
>
> **映射路径**`-v ./data:/htdocs/epg/data` 修改为 `-v ./data:/htdocs/data`
![EPG-Server](https://socialify.git.ci/taksssss/EPG-Server/image?description=1&descriptionEditable=Docker%F0%9F%90%B3%E9%83%A8%E7%BD%B2%EF%BC%8C%E5%B8%A6%E8%AE%BE%E7%BD%AE%E7%95%8C%E9%9D%A2%E3%80%81%E5%8F%B0%E6%A0%87%E7%AE%A1%E7%90%86%EF%BC%8C%E6%94%AF%E6%8C%81DIYP%E3%80%81%E8%B6%85%E7%BA%A7%E7%9B%B4%E6%92%AD%E5%8F%8Axmltv%E3%80%82&font=Inter&forks=1&issues=1&language=1&owner=1&pattern=Circuit%20Board&pulls=1&stargazers=1&theme=Auto)

# 📺 EPG-Server
Expand Down
9 changes: 3 additions & 6 deletions epg/assets/css/manage.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ textarea {
word-break: break-all; /* 允许单词内断行 */
}
textarea[id="gen_list_text"] {
height: 157px;
height: 140px;
}
.form-row {
display: flex;
Expand Down Expand Up @@ -289,11 +289,8 @@ input[type="text"] {
.row {
display: flex;
flex-wrap: nowrap; /* 禁止换行 */
gap: 35px;
margin-bottom: 15px;
}
.row[id="db_setting"] {
gap: 15px;
gap: 45px;
margin-bottom: 12px;
}
.column {
display: flex;
Expand Down
3 changes: 2 additions & 1 deletion epg/assets/defaultConfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
},
"live_source_auto_sync": 0,
"live_channel_name_process": 0,
"live_token": "",
"token": "",
"token_range": 1,
"default_icon": "",
"check_update": 1,
"manage_password": ""
Expand Down
51 changes: 34 additions & 17 deletions epg/assets/html/manage.html
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ <h2>频道匹配结果</h2>
<textarea id="sourceUrlTextarea" placeholder="支持 .txt 和 .m3u 格式,光标离开后自动保存。
地址前 # 临时停用,后 # 备注并作为分组前缀。快捷键:Ctrl+/ 。
示例:https://xxx.xx/xx.m3u #前缀1:
可通过 <?php echo $serverUrl . '/?live&token=' . $Config['live_token']; ?>&url=xxx 访问单个直播源地址。" style="height: 102px;"></textarea>
可通过 <?php echo $serverUrl . '/?token=' . $Config['token']; ?>&live&url=xxx 访问单个直播源地址。" style="height: 102px;"></textarea>
</div>
<br>

Expand All @@ -291,8 +291,8 @@ <h2>频道匹配结果</h2>
<input type="file" name="liveSourceFile" id="liveSourceFile" style="display: none;" accept=".m3u, .txt">
<button id="uploadSourceBtn" onclick="document.getElementById('liveSourceFile').click()">上传源</button>
<button id="parseSourceInfoBtn" onclick="parseSourceInfo()">解析源</button>
<button id="copyM3UBtn" onclick="copyText(`<?php echo $serverUrl . '/?live=m3u&token=' . $Config['live_token']; ?>`)">m3u地址</button>
<button id="copyTXTBtn" onclick="copyText(`<?php echo $serverUrl . '/?live=txt&token=' . $Config['live_token']; ?>`)">txt地址</button>
<button id="copyM3UBtn" onclick="copyText(`<?php echo $serverUrl . '/?token=' . $Config['token'] . '&live=m3u'; ?>`)">m3u地址</button>
<button id="copyTXTBtn" onclick="copyText(`<?php echo $serverUrl . '/?token=' . $Config['token'] . '&live=txt'; ?>`)">txt地址</button>
<div class="tooltip" style="width: 120%;">
<button id="toggleLiveSourceSyncBtn" onclick="toggleStatus('toggleLiveSourceSyncBtn')">
同步: <?php echo (isset($Config['live_source_auto_sync']) && $Config['live_source_auto_sync'] == 1 ? '是' : '否'); ?>
Expand Down Expand Up @@ -325,6 +325,8 @@ <h2>频道匹配结果</h2>
<th style='width: 20%'>台标地址</th>
<th style='width: 8%'>tvg-id</th>
<th style='width: 10%'>tvg-name</th>
<th style='width: 30px'>停用</th>
<th style='width: 30px'>保持</th>
</tr>
</thead>
<tbody>
Expand Down Expand Up @@ -405,13 +407,6 @@ <h2>更多设置</h2>

<!-- 第三行 -->
<div class="row">
<div class="column">
<label for="check_update">检查版本更新:</label>
<select id="check_update" name="check_update" required>
<option value="1" <?php if (!isset($Config['check_update']) || $Config['check_update'] == 1) echo 'selected'; ?>></option>
<option value="0" <?php if (isset($Config['check_update']) && $Config['check_update'] == 0) echo 'selected'; ?>></option>
</select>
</div>
<div class="column">
<div class="tooltip">
<label for="cache_time">缓存时间<span style="vertical-align: super;">*</span>(小时):</label>
Expand All @@ -429,14 +424,14 @@ <h2>更多设置</h2>
<option value="mysql" <?php if (isset($Config['db_type']) && $Config['db_type'] == 'mysql') echo 'selected'; ?>>MySQL</option>
</select>
</div>
</div>

<!-- 第四行 -->
<div class="row" id="db_setting">
<div class="column">
<label for="mysql_host">地址:</label>
<textarea id="mysql_host" style="width: 145px; margin-right: 20px;"><?php echo htmlspecialchars($Config['mysql']['host'] ?? ''); ?></textarea>
<textarea id="mysql_host"><?php echo htmlspecialchars($Config['mysql']['host'] ?? ''); ?></textarea>
</div>
</div>

<!-- 第四行 -->
<div class="row">
<div class="column">
<label for="mysql_dbname">库名:</label>
<textarea id="mysql_dbname"><?php echo htmlspecialchars($Config['mysql']['dbname'] ?? ''); ?></textarea>
Expand All @@ -451,6 +446,28 @@ <h2>更多设置</h2>
</div>
</div>

<!-- 第五行 -->
<div class="row">
<div class="column">
<label for="check_update">检查版本更新:</label>
<select id="check_update" name="check_update" required>
<option value="1" <?php if (!isset($Config['check_update']) || $Config['check_update'] == 1) echo 'selected'; ?>></option>
<option value="0" <?php if (isset($Config['check_update']) && $Config['check_update'] == 0) echo 'selected'; ?>></option>
</select>
</div>
<div class="column">
<label for="token_range"><span id="change_token_span" onclick="changeToken(`<?php echo $Config['token']; ?>`)" style="color: blue; cursor: pointer;">token</span> 范围:</label>
<select id="token_range" name="token_range" required onchange="showTokenRangeMessage(`<?php echo $Config['token']; ?>`, `<?php echo $serverUrl; ?>`)">
<option value="0" <?php if (isset($Config['token_range']) && $Config['token_range'] == 0) echo 'selected'; ?>></option>
<option value="1" <?php if (!isset($Config['token_range']) || $Config['token_range'] == 1) echo 'selected'; ?>>直播源</option>
<option value="2" <?php if (isset($Config['token_range']) && $Config['token_range'] == 2) echo 'selected'; ?>>EPG</option>
<option value="3" <?php if (isset($Config['token_range']) && $Config['token_range'] == 3) echo 'selected'; ?>>全部</option>
</select>
</div>
<div class="column">
</div>
</div>

<!-- 其他设置 -->
<label for="gen_list_text">仅生成以下频道数据:</label>
<select id="gen_list_enable" name="gen_list_enable" style="width: 50px; margin-right: 0px;" required>
Expand All @@ -461,8 +478,8 @@ <h2>更多设置</h2>
(粘贴m3u、txt地址或内容,<span onclick="parseSource()" style="color: blue; cursor: pointer; text-decoration: underline;">解析</span>
<span onclick="showModal('channelmatch')" style="color: blue; cursor: pointer; text-decoration: underline;">查看匹配</span>
</span>
<br><br>
<textarea id="gen_list_text"></textarea><br><br>
<br>
<textarea id="gen_list_text" style="margin-top: 12px; margin-bottom: 12px;"></textarea><br>

<button id="saveConfig" type="button" onclick="setGenListAndUpdateConfig();">保存配置</button>
</div>
Expand Down
120 changes: 112 additions & 8 deletions epg/assets/js/manage.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ document.getElementById('settingsForm').addEventListener('submit', function(even

const fields = ['update_config', 'gen_xml', 'include_future_only', 'ret_default', 'tvmao_default',
'all_chs', 'cache_time', 'db_type', 'mysql_host', 'mysql_dbname', 'mysql_username', 'mysql_password',
'gen_list_enable', 'check_update'];
'gen_list_enable', 'check_update', 'token_range'];

// 创建隐藏字段并将其添加到表单
const form = this;
Expand Down Expand Up @@ -327,7 +327,7 @@ function updateLogTable(logData) {
logData.forEach(log => {
var row = document.createElement("tr");
row.innerHTML = `
<td>${new Date(log.timestamp).toLocaleString('zh-CN')}</td>
<td>${new Date(log.timestamp).toLocaleString('zh-CN').replace(' ', '<br>')}</td>
<td>${log.log_message}</td>
`;
logTableBody.appendChild(row);
Expand Down Expand Up @@ -443,19 +443,38 @@ function displayPage(data, page) {
const end = Math.min(start + rowsPerPage, data.length);

if (data.length === 0) {
tableBody.innerHTML = '<tr><td colspan="7">暂无数据</td></tr>';
tableBody.innerHTML = '<tr><td colspan="8">暂无数据</td></tr>';
return;
}

// 列索引和对应字段的映射
const columns = ['group', 'name', 'url', 'logo', 'tvg_id', 'tvg_name'];
const columns = ['groupTitle', 'channelName', 'streamUrl', 'iconUrl', 'tvgId', 'tvgName', 'disable', 'modified'];

// 填充当前页的表格数据
data.slice(start, end).forEach((item, index) => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${start + index + 1}</td>
${columns.map(col => `<td contenteditable="true">${item[col] || ''}</td>`).join('')}
${columns.map((col, columnIndex) => {
let cellContent = item[col] || '';
let cellStyle = '';
// 处理 disable 和 modified 列
if (col === 'disable' || col === 'modified') {
cellContent = item[col] == 1 ? '是' : '否';
cellStyle = (col === 'disable' && item[col] == 1)
? 'font-weight: bold; color: red; background-color: #FFFFE0; cursor: pointer; user-select: none;'
: (col === 'modified' && item[col] == 1)
? 'font-weight: bold; color: red; background-color: #DEFAFF; cursor: pointer; user-select: none;'
: 'cursor: pointer; user-select: none;';
}
const editable = (col === 'disable' || col === 'modified') ? '' : 'contenteditable="true"';
const clickableClass = (col === 'disable' || col === 'modified') ? 'clickable' : '';
return `<td ${editable} class="${clickableClass}" style="${cellStyle}">
${cellContent}
</td>`;
}).join('')}
`;

// 为每个单元格添加事件监听器
Expand All @@ -464,10 +483,39 @@ function displayPage(data, page) {
const dataIndex = (currentPage - 1) * rowsPerPage + index;
if (dataIndex < allLiveData.length) {
allLiveData[dataIndex][columns[columnIndex]] = cell.textContent.trim();

allLiveData[dataIndex]['modified'] = 1; // 标记修改位
const lastCell = cell.closest('tr').lastElementChild;
lastCell.textContent = '是';
lastCell.style = 'font-weight: bold; color: red; background-color: #DEFAFF; cursor: pointer; user-select: none;';
}
});
});


// 为 disable 和 modified 列添加点击事件,切换 "是/否"
row.querySelectorAll('td.clickable').forEach((cell, columnIndex) => {
cell.addEventListener('click', () => {
const dataIndex = (currentPage - 1) * rowsPerPage + index;
if (dataIndex < allLiveData.length) {
const isDisable = columnIndex === 0;
const field = isDisable ? 'disable' : 'modified';
const newValue = allLiveData[dataIndex][field] == 1 ? 0 : 1;
allLiveData[dataIndex][field] = newValue;
cell.textContent = newValue == 1 ? '是' : '否';
cell.style.fontWeight = newValue == 1 ? 'bold' : 'normal';
cell.style.color = newValue == 1 ? 'red' : 'black';
cell.style.backgroundColor = newValue == 1 ? (isDisable ? '#FFFFE0' : '#DEFAFF') : '';

if(isDisable) {
allLiveData[dataIndex]['modified'] = 1; // 标记修改位
const lastCell = cell.closest('tr').lastElementChild;
lastCell.textContent = '是';
lastCell.style = 'font-weight: bold; color: red; background-color: #DEFAFF; cursor: pointer; user-select: none;';
}
}
});
});

tableBody.appendChild(row);
});
}
Expand Down Expand Up @@ -606,7 +654,7 @@ function saveLiveSourceInfo() {
})
})
.then(response => response.json())
.then(data => showMessageModal(data.success ? '保存成功<br>⚠️注意:重新解析会覆盖所有修改!' : '保存失败'))
.then(data => showMessageModal(data.success ? '保存成功<br>已生成 M3U 及 TXT 文件<br>设置保持后重新解析不变' : '保存失败'))
.catch(error => showMessageModal('保存过程中出现错误: ' + error));
}

Expand Down Expand Up @@ -1027,4 +1075,60 @@ document.getElementById('importFile').addEventListener('change', function() {
.catch(error => showMessageModal('导入过程中发生错误:' + error));

this.value = ''; // 重置文件输入框的值,确保可以连续上传相同文件
});
});

// 修改 token 对话框
function changeToken(currentToken) {
showMessageModal('');
document.getElementById('messageModalMessage').innerHTML = `
<div style="width: 180px; height: 125px;">
<h3>修改 token</h3>
<input type="text" value="${currentToken}" id="newToken" style="text-align: center; font-size: 15px; margin-bottom: 15px;" />
<button onclick="updateToken()">确认</button>
</div>
`;
}

// 更新 token 到 config.json
function updateToken() {
var newToken = document.getElementById('newToken').value;

// 内容写入 config.json 文件
fetch('manage.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
save_token: 'true',
content: newToken
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('修改成功');
window.location.href = 'manage.php';
} else {
showMessageModal('修改失败');
}
})
.catch(error => showMessageModal('保存过程中出现错误: ' + error));
}

// token_range 更变后进行提示
function showTokenRangeMessage(token, serverUrl) {
var tokenRange = document.getElementById("token_range").value;
var message = '';
var baseUrl = serverUrl + '/?token=' + token;
if (tokenRange == "1" || tokenRange == "3") {
message += '直播源地址:<br><a href="' + baseUrl + '&live=m3u" target="_blank">' + baseUrl + '&live=m3u</a><br>' +
'<a href="' + baseUrl + '&live=txt" target="_blank">' + baseUrl + '&live=txt</a>';
}
if (tokenRange == "2" || tokenRange == "3") {
if (message) message += '<br>';
message += 'EPG地址:<br><a href="' + baseUrl + '" target="_blank">' + baseUrl + '</a>';
}
if (message) {
showMessageModal('');
document.getElementById('messageModalMessage').innerHTML = `<div style="text-align: left">${message}</div>`;
}
}
Loading

0 comments on commit 7ed3f89

Please sign in to comment.