Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

All notable changes to this maintained LibreTV fork are documented here.

## 1.2.8 - 2026-05-11

### Changed

- Source health checks now cover all built-in and custom sources instead of only the default selected sources.
- Renamed the settings action from `检测默认源` to `检测源`.

### Fixed

- Loaded the shared modal stylesheet on the home page so the first-run guide opens as a centered overlay instead of appearing in page flow.

## 1.2.7 - 2026-05-11

### Added
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ PWA 相关文件:

## 源健康与诊断

设置面板可以检测默认源的搜索、详情和 m3u8 可访问性,并缓存一份本地报告。诊断页位于 `/diagnostics.html`,用于检查密码保护、代理状态、PWA 状态和源状态,不显示密钥、令牌或密码。
设置面板可以检测全部内置源和自定义源的搜索、详情和 m3u8 可访问性,并缓存一份本地报告。诊断页位于 `/diagnostics.html`,用于检查密码保护、代理状态、PWA 状态和源状态,不显示密钥、令牌或密码。

配置导出格式当前为 `LibreTV-Settings` `2.0.0`。导入旧版 `1.0.0` 配置时会校验哈希、过滤未知字段,并提示迁移结果。

Expand Down
2 changes: 1 addition & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
202605112342
202605112353
4 changes: 2 additions & 2 deletions diagnostics.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ <h1>LibreTV 诊断</h1>
<section class="diagnostics-source">
<div class="diagnostics-source-header">
<div>
<h2>默认源健康</h2>
<h2>源健康</h2>
<p id="sourceHealthSummary">源健康:尚未检测</p>
<p id="sourceHealthLastChecked">最近检测:尚未检测</p>
</div>
<button id="sourceHealthButton" onclick="runDiagnosticsSourceHealth()">检测默认源</button>
<button id="sourceHealthButton" onclick="runDiagnosticsSourceHealth()">检测源</button>
</div>
<div id="sourceHealthList" class="source-health-list"></div>
</section>
Expand Down
3 changes: 2 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<link rel="manifest" href="manifest.json">

<script src="libs/tailwindcss.min.js"></script>
<link rel="stylesheet" href="css/modals.css">
<link rel="stylesheet" href="css/styles.css">
<link rel="stylesheet" href="css/index.css">
</head>
Expand Down Expand Up @@ -100,7 +101,7 @@ <h3 class="text-xl font-bold gradient-text">设置</h3>
<div class="source-health-panel mt-3">
<div class="flex justify-between items-center gap-2">
<span id="sourceHealthSummary" class="text-xs text-gray-400">源健康:尚未检测</span>
<button id="sourceHealthButton" onclick="runSourceHealthCheck()" class="px-2 py-1 bg-[#333] hover:bg-[#444] text-white text-xs rounded">检测默认源</button>
<button id="sourceHealthButton" onclick="runSourceHealthCheck()" class="px-2 py-1 bg-[#333] hover:bg-[#444] text-white text-xs rounded">检测源</button>
</div>
<div id="sourceHealthLastChecked" class="text-xs text-gray-500 mt-1">最近检测:尚未检测</div>
<div id="sourceHealthList" class="source-health-list mt-2"></div>
Expand Down
2 changes: 1 addition & 1 deletion js/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const SITE_CONFIG = {
url: '',
description: '免费在线视频搜索与观看平台',
logo: 'image/logo.png',
version: '1.2.7'
version: '1.2.8'
};

const DEFAULT_SELECTED_APIS = ['ysgc', 'jszy', 'wujin', 'maoyan'];
Expand Down
2 changes: 1 addition & 1 deletion js/diagnostics.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ function getSourceDiagnostic() {
const state = report.summary.failed > 0 ? 'warn' : 'ok';
return {
state,
detail: `${report.summary.successRate}% 成功,${report.summary.ok}/${report.summary.total} 个默认源完全可用`
detail: `${report.summary.successRate}% 成功,${report.summary.ok}/${report.summary.total} 个源完全可用`
};
}

Expand Down
2 changes: 1 addition & 1 deletion js/onboarding.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ function showFirstRunGuide(force = false) {
</div>
<div class="first-run-step">
<strong>数据源</strong>
<span>默认源会随健康检查调整,自定义源可通过导出配置备份。</span>
<span>源健康检查会覆盖全部内置源和自定义源,自定义源可通过导出配置备份。</span>
</div>
<div class="first-run-step">
<strong>PWA</strong>
Expand Down
28 changes: 23 additions & 5 deletions js/source-health.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,29 @@ const SOURCE_HEALTH_CHECKED_AT_KEY = 'sourceHealthCheckedAt';
const SOURCE_HEALTH_TTL_MS = 24 * 60 * 60 * 1000;
const SOURCE_HEALTH_PROBE_QUERY = '阿凡达';

function getDefaultHealthSourceIds() {
const defaults = Array.isArray(window.DEFAULT_SELECTED_APIS) ? window.DEFAULT_SELECTED_APIS : [];
return defaults.filter(sourceId => window.API_SITES?.[sourceId] && !window.API_SITES[sourceId].adult);
function getCustomHealthSourceIds() {
try {
const customSources = JSON.parse(localStorage.getItem('customAPIs') || '[]');
if (!Array.isArray(customSources)) return [];
return customSources
.map((source, index) => source?.url ? `custom_${index}` : '')
.filter(Boolean);
} catch (error) {
console.warn('读取自定义源失败:', error);
return [];
}
}

function getHealthSourceIds() {
const builtInSourceIds = Object.keys(window.API_SITES || {});
return [...builtInSourceIds, ...getCustomHealthSourceIds()];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bound the default source-health scope

Changing the default to getHealthSourceIds() makes a single click probe every built-in source (Object.keys(window.API_SITES)) plus customs, which scales poorly because each probeSourceHealth call uses searchByAPIAndKeyWord and that search path paginates up to API_CONFIG.search.maxPages pages in js/search.js. In practice this can trigger hundreds or thousands of proxy requests in one run, making the health check take minutes and often surface timeout-driven failures rather than real source status.

Useful? React with 👍 / 👎.

}

function getHealthSourceName(sourceId) {
if (sourceId.startsWith('custom_')) {
const customApi = window.getCustomApiInfo?.(sourceId.replace('custom_', ''));
return customApi?.name || sourceId;
}
return window.API_SITES?.[sourceId]?.name || sourceId;
}

Expand Down Expand Up @@ -165,7 +182,7 @@ function summarizeSourceHealth(sources) {
return { total, ok, degraded, failed, successRate };
}

async function runSourceHealthCheck(sourceIds = getDefaultHealthSourceIds()) {
async function runSourceHealthCheck(sourceIds = getHealthSourceIds()) {
const checkedAt = new Date().toISOString();
const sources = [];
const button = document.getElementById('sourceHealthButton');
Expand Down Expand Up @@ -193,7 +210,7 @@ async function runSourceHealthCheck(sourceIds = getDefaultHealthSourceIds()) {
} finally {
if (button) {
button.disabled = false;
button.textContent = '检测默认源';
button.textContent = '检测源';
}
}
}
Expand Down Expand Up @@ -254,6 +271,7 @@ document.addEventListener('DOMContentLoaded', () => {
window.SOURCE_HEALTH_STORAGE_KEY = SOURCE_HEALTH_STORAGE_KEY;
window.loadSourceHealthReport = loadSourceHealthReport;
window.saveSourceHealthReport = saveSourceHealthReport;
window.getHealthSourceIds = getHealthSourceIds;
window.probeSourceHealth = probeSourceHealth;
window.runSourceHealthCheck = runSourceHealthCheck;
window.renderSourceHealthSummary = renderSourceHealthSummary;
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "libretv",
"type": "module",
"version": "1.2.7",
"version": "1.2.8",
"private": true,
"description": "免费在线视频搜索与观看平台",
"author": "bestZwei",
Expand Down
66 changes: 59 additions & 7 deletions test/libretv-defaults.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -313,13 +313,14 @@ test('maintenance automation avoids direct main pushes and public preview workfl
assert.doesNotMatch(workflowText, /git push origin main|target_sync_branch:\s*main|pull_request_target/);
});

test('source health checks probe default sources and expose a UI report', async () => {
test('source health checks probe sources and expose a UI report', async () => {
const sourceHealth = await readProjectFile('js/source-health.js');
const index = await readProjectFile('index.html');
const sw = await readProjectFile('service-worker.js');

assert.match(sourceHealth, /SOURCE_HEALTH_STORAGE_KEY/);
assert.match(sourceHealth, /runSourceHealthCheck/);
assert.match(sourceHealth, /getHealthSourceIds/);
assert.match(sourceHealth, /probeSourceHealth/);
assert.match(sourceHealth, /handleApiRequest/);
assert.match(sourceHealth, /searchOk/);
Expand All @@ -332,10 +333,56 @@ test('source health checks probe default sources and expose a UI report', async
assert.match(index, /sourceHealthSummary/);
assert.match(index, /sourceHealthList/);
assert.match(index, /runSourceHealthCheck/);
assert.match(index, />检测源</);
assert.doesNotMatch(index, /检测默认源/);
assert.match(index, /js\/source-health\.js/);
assert.match(sw, /js\/source-health\.js/);
});

test('source health selection includes all built-in and custom sources', async () => {
const sandbox = {
console,
URL,
window: {},
localStorage: {
getItem(key) {
if (key === 'customAPIs') {
return JSON.stringify([
{ name: '自定义一', url: 'https://custom-one.example/api.php/provide/vod' },
{ name: '自定义二', url: 'https://custom-two.example/api.php/provide/vod' }
]);
}
return null;
},
setItem() {}
},
document: {
addEventListener() {},
getElementById() {
return null;
}
},
performance: {
now() {
return 0;
}
},
setTimeout,
clearTimeout,
AbortController
};
vm.createContext(sandbox);
vm.runInContext(await readProjectFile('js/config.js'), sandbox);
vm.runInContext(await readProjectFile('js/customer_site.js'), sandbox);
vm.runInContext(await readProjectFile('js/source-health.js'), sandbox);

const builtInSourceIds = Object.keys(sandbox.window.API_SITES);
const sourceIds = Array.from(sandbox.window.getHealthSourceIds());

assert.deepEqual(sourceIds, [...builtInSourceIds, 'custom_0', 'custom_1']);
assert.ok(sourceIds.some(sourceId => sandbox.window.API_SITES[sourceId]?.adult));
});

test('playback errors are classified and offer one-click source switching', async () => {
const playerErrors = await readProjectFile('js/player-errors.js');
const player = await readProjectFile('js/player.js');
Expand Down Expand Up @@ -368,10 +415,15 @@ test('first-run guidance and diagnostics page support public self-hosting', asyn
assert.match(onboarding, /PWA/);
assert.match(onboarding, /导出配置/);
assert.match(index, /js\/onboarding\.js/);
assert.match(index, /css\/modals\.css/);
assert.ok(index.indexOf('css/modals.css') < index.indexOf('css/styles.css'));
assert.match(index, /showFirstRunGuide\(true\)/);

assert.match(diagnosticsHtml, /diagnostics-root/);
assert.match(diagnosticsHtml, /js\/diagnostics\.js/);
assert.match(diagnosticsHtml, /源健康/);
assert.match(diagnosticsHtml, />检测源</);
assert.doesNotMatch(diagnosticsHtml, /默认源健康|检测默认源/);
assert.match(diagnostics, /password-status/);
assert.match(diagnostics, /proxy-status/);
assert.match(diagnostics, /pwa-status/);
Expand Down Expand Up @@ -427,12 +479,12 @@ test('release metadata is bumped for this update', async () => {

const changelog = await readProjectFile('CHANGELOG.md');

assert.equal(packageJson.version, '1.2.7');
assert.equal(lockJson.version, '1.2.7');
assert.equal(lockJson.packages[''].version, '1.2.7');
assert.match(config, /version:\s*'1\.2\.7'/);
assert.match(changelog, /1\.2\.7/);
assert.match(changelog, /migration intake/);
assert.equal(packageJson.version, '1.2.8');
assert.equal(lockJson.version, '1.2.8');
assert.equal(lockJson.packages[''].version, '1.2.8');
assert.match(config, /version:\s*'1\.2\.8'/);
assert.match(changelog, /1\.2\.8/);
assert.match(changelog, /检测源|Source health checks/);
assert.match(versionTxt, /^\d{12}$/);
assert.ok(Number(versionTxt) > 202508060117);
});
Loading