release: bump version to 0.38.0

This commit is contained in:
linkong
2026-04-23 17:57:35 +08:00
parent 195a8bf71c
commit d5f3784ffb
39 changed files with 4958 additions and 146 deletions

View File

@@ -36,6 +36,7 @@ const tabPanelState = {
live: null,
news: null,
};
let mobileMetaCollapsed = false;
const META_AUTO_COLLAPSE_DELAY = 2500;
const PROBE_INTERVAL_MS = 2 * 60 * 1000;
@@ -72,8 +73,8 @@ function getElements() {
empty: document.getElementById(isMobile ? "mobile-tv-empty-state" : "tv-empty-state"),
refreshBtn: document.getElementById(isMobile ? "mobile-tv-refresh" : "tv-refresh"),
openBtn: document.getElementById(isMobile ? "mobile-tv-open-external" : "tv-open-external"),
metaWrap: document.getElementById("tv-meta-wrap"),
metaToggle: document.getElementById("tv-meta-toggle"),
metaWrap: document.getElementById(isMobile ? "mobile-tv-meta-wrap" : "tv-meta-wrap"),
metaToggle: document.getElementById(isMobile ? "mobile-tv-meta-toggle" : "tv-meta-toggle"),
liveHeaderControls: document.getElementById("tv-header-controls-live"),
newsHeaderControls: document.getElementById("tv-header-controls-news"),
liveTabBtn: document.getElementById("tv-tab-live"),
@@ -84,8 +85,43 @@ function getElements() {
};
}
function isMobileLayout() {
return document.body.classList.contains("layout-mode-mobile");
}
function syncMetaToggleState(collapsed) {
const mobileOverviewBar = document.getElementById("mobile-tv-overview-bar");
if (mobileOverviewBar instanceof HTMLElement) {
mobileOverviewBar.setAttribute("aria-expanded", collapsed ? "false" : "true");
}
const desktopToggle = document.getElementById("tv-meta-toggle");
if (desktopToggle instanceof HTMLButtonElement) {
desktopToggle.setAttribute("aria-expanded", collapsed ? "false" : "true");
desktopToggle.setAttribute(
"aria-label",
collapsed ? "展开新闻直播内容" : "折叠新闻直播内容",
);
desktopToggle.title = collapsed ? "展开新闻直播内容" : "折叠新闻直播内容";
}
}
function isMetaCollapsed() {
if (isMobileLayout()) {
return mobileMetaCollapsed;
}
return mediaPanel?.isCollapsed() ?? false;
}
function setMetaCollapsed(collapsed) {
if (isMobileLayout()) {
const { metaWrap } = getElements();
mobileMetaCollapsed = Boolean(collapsed);
metaWrap?.classList.toggle("is-collapsed", mobileMetaCollapsed);
syncMetaToggleState(mobileMetaCollapsed);
return;
}
mediaPanel?.setCollapsed(collapsed);
syncMetaToggleState(Boolean(collapsed));
}
function syncPanelActiveTab(tab = activeTab) {
@@ -120,6 +156,10 @@ function syncNewsDefaultMaxHeight() {
}
function autoExpandMeta() {
if (isMobileLayout()) {
clearTimeout(metaAutoCollapseTimer);
return;
}
clearTimeout(metaAutoCollapseTimer);
setMetaCollapsed(false);
metaAutoCollapseTimer = setTimeout(() => setMetaCollapsed(true), META_AUTO_COLLAPSE_DELAY);
@@ -154,7 +194,7 @@ function captureTabState(tab = activeTab) {
tabPanelState[tab] = {
layout: readPanelLayoutState(panel),
metaCollapsed:
tab === "live" ? (mediaPanel?.isCollapsed() ?? false) : null,
tab === "live" ? isMetaCollapsed() : null,
};
}
@@ -749,15 +789,59 @@ function getExternalUrl(source) {
}
function updateOpenButton(source) {
const { openBtn } = getElements();
if (!openBtn) return;
const targetUrl = getExternalUrl(source);
openBtn.disabled = !targetUrl;
openBtn.onclick = targetUrl
? () => {
window.open(targetUrl, "_blank", "noopener,noreferrer");
}
: null;
[
document.getElementById("mobile-tv-open-external"),
document.getElementById("tv-open-external"),
].forEach((button) => {
if (!(button instanceof HTMLButtonElement)) return;
button.disabled = !targetUrl;
button.onclick = targetUrl
? () => {
window.open(targetUrl, "_blank", "noopener,noreferrer");
}
: null;
});
}
function syncMobileOverviewSummary(source) {
const headline = document.getElementById("mobile-tv-overview-headline");
const summary = document.getElementById("mobile-tv-overview-summary");
const tags = document.getElementById("mobile-tv-overview-tags");
if (headline instanceof HTMLElement) {
headline.textContent = source?.name || "暂无可用频道";
}
if (summary instanceof HTMLElement) {
if (!source) {
summary.textContent = "点击查看当前频道来源、目录和补充说明";
} else {
const parts = [
source.provider,
source.region,
source.language,
].filter(Boolean);
summary.textContent = parts.length
? parts.join(" · ")
: "点击查看完整频道信息";
}
}
if (tags instanceof HTMLElement) {
const tagValues = source
? [
{ label: source.source_type || "频道", kind: "status" },
source.collector_source ? { label: `采集:${source.collector_source}`, kind: "" } : { label: "内置源", kind: "" },
source.region ? { label: source.region, kind: "" } : null,
].filter(Boolean).slice(0, 3)
: [{ label: "待加载", kind: "status" }];
tags.replaceChildren(
...tagValues.map(({ label, kind }) => {
const chip = document.createElement("span");
chip.className = `earth-mobile-tv-overview-tag${kind ? ` earth-mobile-tv-overview-tag--${kind}` : ""}`;
chip.textContent = label;
return chip;
}),
);
}
}
function findSourceById(sourceId) {
@@ -926,6 +1010,7 @@ function renderSource(source) {
if (notes) {
notes.textContent = source?.notes || "支持后台配置默认源与采集器补充源。";
}
syncMobileOverviewSummary(source);
if (!source || (!embeddedUrl && !videoUrl)) {
destroyHlsPlayer();
@@ -1031,6 +1116,7 @@ export function initTVPanel() {
liveTabBtn,
newsTabBtn,
} = getElements();
const mobileOverviewBar = document.getElementById("mobile-tv-overview-bar");
if (panel && metaToggle) {
mediaPanel = createHUDPanel({
@@ -1045,6 +1131,10 @@ export function initTVPanel() {
});
}
mobileMetaCollapsed = true;
syncMetaToggleState(isMetaCollapsed());
setMetaCollapsed(isMetaCollapsed());
updateToggleButton(mediaPanel?.isVisible() ?? !panel?.classList.contains("hud-panel-hidden"));
syncSettingsToggle(mediaPanel?.isVisible() ?? !panel?.classList.contains("hud-panel-hidden"));
@@ -1084,10 +1174,31 @@ export function initTVPanel() {
});
});
metaToggle?.addEventListener("click", () => {
[metaToggle, document.getElementById("tv-meta-toggle")]
.filter((element, index, array) => element && array.indexOf(element) === index)
.forEach((toggleEl) => {
toggleEl?.addEventListener("click", () => {
clearTimeout(metaAutoCollapseTimer);
const isNowCollapsed = !isMetaCollapsed();
setMetaCollapsed(isNowCollapsed);
});
});
const toggleMobileMeta = (event) => {
const interactiveTarget = event.target instanceof Element
? event.target.closest("button, a, select, input, textarea, video, iframe")
: null;
if (interactiveTarget) return;
clearTimeout(metaAutoCollapseTimer);
const isNowCollapsed = !(mediaPanel?.isCollapsed() ?? false);
const isNowCollapsed = !isMetaCollapsed();
setMetaCollapsed(isNowCollapsed);
};
mobileOverviewBar?.addEventListener("click", toggleMobileMeta);
mobileOverviewBar?.addEventListener("keydown", (event) => {
if (event.key !== "Enter" && event.key !== " ") return;
event.preventDefault();
toggleMobileMeta(event);
});
[refreshBtn, document.getElementById("mobile-tv-refresh"), document.getElementById("tv-refresh")]