Feat: Added support for new tags in XMLTV
This commit is contained in:
+12
-1
@@ -17,8 +17,19 @@
|
|||||||
font-size: 20px; font-weight: 700; color: var(--text);
|
font-size: 20px; font-weight: 700; color: var(--text);
|
||||||
margin-bottom: 8px; line-height: 1.2;
|
margin-bottom: 8px; line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
.pm-subtitle {
|
||||||
|
font-size: 13px; color: var(--text); margin-bottom: 10px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
.pm-time {
|
.pm-time {
|
||||||
font-size: 12px; color: var(--accent); margin-bottom: 14px;
|
font-size: 12px; color: var(--accent); margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.pm-meta {
|
||||||
|
font-size: 12px; color: var(--text-muted); margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.pm-genres {
|
||||||
|
font-size: 12px; color: var(--text); margin-bottom: 12px;
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
.pm-desc {
|
.pm-desc {
|
||||||
font-size: 13px; color: var(--text-muted); line-height: 1.6;
|
font-size: 13px; color: var(--text-muted); line-height: 1.6;
|
||||||
|
|||||||
@@ -4,12 +4,20 @@ function openProgramModal(ch, p) {
|
|||||||
const isLive = p.start <= now && p.stop > now;
|
const isLive = p.start <= now && p.stop > now;
|
||||||
const dur = Math.round((p.stop - p.start) / 60000);
|
const dur = Math.round((p.stop - p.start) / 60000);
|
||||||
const ep = fmtEpisode(p.season, p.episode);
|
const ep = fmtEpisode(p.season, p.episode);
|
||||||
|
const genres = formatCategories(p.categories);
|
||||||
|
const meta = [p.airDate, p.rating].filter(Boolean).join(' · ');
|
||||||
|
|
||||||
document.getElementById('pm-title').textContent = p.title;
|
document.getElementById('pm-title').textContent = p.title;
|
||||||
document.getElementById('pm-channel').textContent = ch.name;
|
document.getElementById('pm-channel').textContent = ch.name;
|
||||||
|
document.getElementById('pm-subtitle').textContent = p.subtitle || '';
|
||||||
|
document.getElementById('pm-subtitle').style.display = p.subtitle ? 'block' : 'none';
|
||||||
document.getElementById('pm-time').textContent =
|
document.getElementById('pm-time').textContent =
|
||||||
`${fmtTime(p.start)} — ${fmtTime(p.stop)} · ${dur} ${L.duration_min}` +
|
`${fmtTime(p.start)} — ${fmtTime(p.stop)} · ${dur} ${L.duration_min}` +
|
||||||
(ep ? ` · ${ep}` : '');
|
(ep ? ` · ${ep}` : '');
|
||||||
|
document.getElementById('pm-meta').textContent = meta;
|
||||||
|
document.getElementById('pm-meta').style.display = meta ? 'block' : 'none';
|
||||||
|
document.getElementById('pm-genres').textContent = genres;
|
||||||
|
document.getElementById('pm-genres').style.display = genres ? 'block' : 'none';
|
||||||
document.getElementById('pm-desc').textContent = p.desc || '';
|
document.getElementById('pm-desc').textContent = p.desc || '';
|
||||||
document.getElementById('pm-desc').style.display = p.desc ? 'block' : 'none';
|
document.getElementById('pm-desc').style.display = p.desc ? 'block' : 'none';
|
||||||
|
|
||||||
|
|||||||
+9
-2
@@ -49,6 +49,7 @@ function applySearch(q) {
|
|||||||
if (p.stop < today || p.start > tomorrow) return false;
|
if (p.stop < today || p.start > tomorrow) return false;
|
||||||
if (p.title.toLowerCase().includes(q)) return true;
|
if (p.title.toLowerCase().includes(q)) return true;
|
||||||
if (p.desc && p.desc.toLowerCase().includes(q)) return true;
|
if (p.desc && p.desc.toLowerCase().includes(q)) return true;
|
||||||
|
if ((p.categories || []).some(cat => cat.toLowerCase().includes(q))) return true;
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -110,7 +111,10 @@ function renderFiltered(filteredChannels, q) {
|
|||||||
block.className = 'program-block';
|
block.className = 'program-block';
|
||||||
|
|
||||||
// Highlight programs that match the current search query.
|
// Highlight programs that match the current search query.
|
||||||
const matchesProg = p.title.toLowerCase().includes(q) || (p.desc && p.desc.toLowerCase().includes(q));
|
const matchesProg =
|
||||||
|
p.title.toLowerCase().includes(q) ||
|
||||||
|
(p.desc && p.desc.toLowerCase().includes(q)) ||
|
||||||
|
(p.categories || []).some(cat => cat.toLowerCase().includes(q));
|
||||||
if (matchesProg) block.classList.add('search-match');
|
if (matchesProg) block.classList.add('search-match');
|
||||||
|
|
||||||
if (p.stop < now) block.classList.add('is-past');
|
if (p.stop < now) block.classList.add('is-past');
|
||||||
@@ -168,7 +172,10 @@ function renderMobileFiltered(filteredChannels, q) {
|
|||||||
const isLive = p.start <= now && p.stop > now;
|
const isLive = p.start <= now && p.stop > now;
|
||||||
const isPast = p.stop < now;
|
const isPast = p.stop < now;
|
||||||
const dur = Math.round((p.stop - p.start) / 60000);
|
const dur = Math.round((p.stop - p.start) / 60000);
|
||||||
const matchesProg = p.title.toLowerCase().includes(q) || (p.desc && p.desc.toLowerCase().includes(q));
|
const matchesProg =
|
||||||
|
p.title.toLowerCase().includes(q) ||
|
||||||
|
(p.desc && p.desc.toLowerCase().includes(q)) ||
|
||||||
|
(p.categories || []).some(cat => cat.toLowerCase().includes(q));
|
||||||
const item = document.createElement('div');
|
const item = document.createElement('div');
|
||||||
item.className = 'mobile-program' + (isLive ? ' is-live' : '') + (isPast ? ' is-past' : '') + (matchesProg ? ' search-match' : '');
|
item.className = 'mobile-program' + (isLive ? ' is-live' : '') + (isPast ? ' is-past' : '') + (matchesProg ? ' search-match' : '');
|
||||||
|
|
||||||
|
|||||||
+7
-4
@@ -8,8 +8,13 @@ function getNowPlaying(chId) {
|
|||||||
// ── TOOLTIP ───────────────────────────────────────────────────────────────────
|
// ── TOOLTIP ───────────────────────────────────────────────────────────────────
|
||||||
function showTooltip(e, p) {
|
function showTooltip(e, p) {
|
||||||
const ep = fmtEpisode(p.season, p.episode);
|
const ep = fmtEpisode(p.season, p.episode);
|
||||||
document.getElementById('tt-title').textContent = p.title + (ep ? ' ' + ep : '');
|
const genres = formatCategories(p.categories);
|
||||||
document.getElementById('tt-time').textContent = `${fmtTime(p.start)} — ${fmtTime(p.stop)} · ${Math.round((p.stop-p.start)/60000)} min`;
|
const title = p.title + (p.subtitle ? ` - ${p.subtitle}` : '') + (ep ? ' ' + ep : '');
|
||||||
|
const meta = [p.airDate, p.rating, genres].filter(Boolean).join(' · ');
|
||||||
|
document.getElementById('tt-title').textContent = title;
|
||||||
|
document.getElementById('tt-time').textContent =
|
||||||
|
`${fmtTime(p.start)} — ${fmtTime(p.stop)} · ${Math.round((p.stop-p.start)/60000)} min` +
|
||||||
|
(meta ? ` · ${meta}` : '');
|
||||||
document.getElementById('tt-desc').textContent = p.desc || '—';
|
document.getElementById('tt-desc').textContent = p.desc || '—';
|
||||||
document.getElementById('tooltip').classList.add('visible');
|
document.getElementById('tooltip').classList.add('visible');
|
||||||
moveTooltip(e);
|
moveTooltip(e);
|
||||||
@@ -23,5 +28,3 @@ function moveTooltip(e) {
|
|||||||
function hideTooltip() { document.getElementById('tooltip').classList.remove('visible'); }
|
function hideTooltip() { document.getElementById('tooltip').classList.remove('visible'); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+33
-1
@@ -106,6 +106,34 @@ function fmtEpisode(season, episode) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseCategories(p) {
|
||||||
|
const seen = new Set();
|
||||||
|
const categories = [];
|
||||||
|
p.querySelectorAll('category').forEach(cat => {
|
||||||
|
const value = (cat.textContent || '').trim();
|
||||||
|
const key = value.toLowerCase();
|
||||||
|
if (!value || seen.has(key)) return;
|
||||||
|
seen.add(key);
|
||||||
|
categories.push(value);
|
||||||
|
});
|
||||||
|
return categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatCategories(categories) {
|
||||||
|
return Array.isArray(categories) && categories.length ? categories.join(' · ') : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseAirDate(value) {
|
||||||
|
if (!value) return null;
|
||||||
|
const match = String(value).trim().match(/^(\d{4})/);
|
||||||
|
return match ? match[1] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseRatingValue(p) {
|
||||||
|
const rating = p.querySelector('rating > value')?.textContent || '';
|
||||||
|
return rating.trim() || null;
|
||||||
|
}
|
||||||
|
|
||||||
function parseAndRender(doc) {
|
function parseAndRender(doc) {
|
||||||
channels = []; programs = {};
|
channels = []; programs = {};
|
||||||
doc.querySelectorAll('channel').forEach(ch => {
|
doc.querySelectorAll('channel').forEach(ch => {
|
||||||
@@ -124,9 +152,13 @@ function parseAndRender(doc) {
|
|||||||
const start = parseXMLTVDate(p.getAttribute('start'));
|
const start = parseXMLTVDate(p.getAttribute('start'));
|
||||||
const stop = parseXMLTVDate(p.getAttribute('stop'));
|
const stop = parseXMLTVDate(p.getAttribute('stop'));
|
||||||
const title = p.querySelector('title')?.textContent || '';
|
const title = p.querySelector('title')?.textContent || '';
|
||||||
|
const subtitle = p.querySelector('sub-title')?.textContent || '';
|
||||||
const desc = p.querySelector('desc')?.textContent || '';
|
const desc = p.querySelector('desc')?.textContent || '';
|
||||||
|
const categories = parseCategories(p);
|
||||||
|
const airDate = parseAirDate(p.querySelector('date')?.textContent || '');
|
||||||
|
const rating = parseRatingValue(p);
|
||||||
const epInfo = parseEpisode(p);
|
const epInfo = parseEpisode(p);
|
||||||
if (start && stop) programs[chId].push({ start, stop, title, desc, ...epInfo });
|
if (start && stop) programs[chId].push({ start, stop, title, subtitle, desc, categories, airDate, rating, ...epInfo });
|
||||||
});
|
});
|
||||||
Object.keys(programs).forEach(id => programs[id].sort((a,b) => a.start - b.start));
|
Object.keys(programs).forEach(id => programs[id].sort((a,b) => a.start - b.start));
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,10 @@
|
|||||||
<button class="pm-close" onclick="closeProgramModal()">✕</button>
|
<button class="pm-close" onclick="closeProgramModal()">✕</button>
|
||||||
<div class="pm-channel" id="pm-channel"></div>
|
<div class="pm-channel" id="pm-channel"></div>
|
||||||
<div class="pm-title" id="pm-title"></div>
|
<div class="pm-title" id="pm-title"></div>
|
||||||
|
<div class="pm-subtitle" id="pm-subtitle"></div>
|
||||||
<div class="pm-time" id="pm-time"></div>
|
<div class="pm-time" id="pm-time"></div>
|
||||||
|
<div class="pm-meta" id="pm-meta"></div>
|
||||||
|
<div class="pm-genres" id="pm-genres"></div>
|
||||||
<div class="pm-desc" id="pm-desc"></div>
|
<div class="pm-desc" id="pm-desc"></div>
|
||||||
<div class="pm-actions">
|
<div class="pm-actions">
|
||||||
<button class="pm-btn pm-watch" id="pm-watch">▶ <?= htmlspecialchars($L["watch_now"]) ?></button>
|
<button class="pm-btn pm-watch" id="pm-watch">▶ <?= htmlspecialchars($L["watch_now"]) ?></button>
|
||||||
|
|||||||
Reference in New Issue
Block a user