Feat: Language support via json files in the locales folder + Feat: Popup with title, channel, schedule, duration, season, episode, synopsis, Watch button (if M3U provided) and IMDb link + Fix: Date added to the timebar
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
const EPG_SOURCES = <?= json_encode(array_values($epg_sources)) ?>;
|
||||
const L = <?= json_encode($L) ?>;
|
||||
const ALLOW_PERSONAL_EPG = <?= $allow_personal_epg ? 'true' : 'false' ?>;
|
||||
|
||||
// Source active (index)
|
||||
|
||||
+12
-6
@@ -37,10 +37,18 @@ function renderRuler() {
|
||||
let t = new Date(GRID_START); t.setSeconds(0,0);
|
||||
const rem = t.getMinutes()%15; if (rem) t.setMinutes(t.getMinutes()+(15-rem));
|
||||
while (t <= GRID_END) {
|
||||
const x = msToX(t.getTime()); const isHour = t.getMinutes()===0;
|
||||
const x = msToX(t.getTime());
|
||||
const isHour = t.getMinutes() === 0;
|
||||
const isMidnight = isHour && t.getHours() === 0;
|
||||
const tick = document.createElement('div'); tick.className='time-tick'; tick.style.left=x+'px';
|
||||
const lbl = document.createElement('div'); lbl.className='time-tick-label'+(isHour?' hour':''); lbl.textContent=fmtTime(t);
|
||||
const line = document.createElement('div'); line.className='time-tick-line'+(isHour?' hour':'');
|
||||
const lbl = document.createElement('div');
|
||||
lbl.className = 'time-tick-label' + (isHour ? ' hour' : '') + (isMidnight ? ' midnight' : '');
|
||||
if (isMidnight) {
|
||||
lbl.textContent = `${L.days[t.getDay()]} ${t.getDate()} ${L.months[t.getMonth()]}`;
|
||||
} else {
|
||||
lbl.textContent = fmtTime(t);
|
||||
}
|
||||
const line = document.createElement('div'); line.className='time-tick-line'+(isHour?' hour':'')+(isMidnight?' midnight':'');
|
||||
tick.appendChild(lbl); tick.appendChild(line); ruler.appendChild(tick);
|
||||
t = new Date(t.getTime()+15*60000);
|
||||
}
|
||||
@@ -88,9 +96,7 @@ function renderPrograms() {
|
||||
block.addEventListener('mouseenter', e=>showTooltip(e,p));
|
||||
block.addEventListener('mousemove', e=>moveTooltip(e));
|
||||
block.addEventListener('mouseleave', hideTooltip);
|
||||
if (p.start <= now && p.stop > now) {
|
||||
block.addEventListener('click', () => openPip(ch.name, p.title));
|
||||
}
|
||||
block.addEventListener('click', (e) => { hideTooltip(); openProgramModal(ch, p); });
|
||||
row.appendChild(block);
|
||||
});
|
||||
inner.appendChild(row);
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// ── PROGRAM MODAL ─────────────────────────────────────────────────────────────
|
||||
function openProgramModal(ch, p) {
|
||||
const now = new Date();
|
||||
const isLive = p.start <= now && p.stop > now;
|
||||
const dur = Math.round((p.stop - p.start) / 60000);
|
||||
const ep = fmtEpisode(p.season, p.episode);
|
||||
|
||||
document.getElementById('pm-title').textContent = p.title;
|
||||
document.getElementById('pm-channel').textContent = ch.name;
|
||||
document.getElementById('pm-time').textContent =
|
||||
`${fmtTime(p.start)} — ${fmtTime(p.stop)} · ${dur} ${L.duration_min}` +
|
||||
(ep ? ` · ${ep}` : '');
|
||||
document.getElementById('pm-desc').textContent = p.desc || '';
|
||||
document.getElementById('pm-desc').style.display = p.desc ? 'block' : 'none';
|
||||
|
||||
// Bouton Watch now — seulement si live
|
||||
const watchBtn = document.getElementById('pm-watch');
|
||||
if (isLive) {
|
||||
watchBtn.style.display = 'inline-flex';
|
||||
watchBtn.onclick = () => { closeProgramModal(); openPip(ch.name, p.title); };
|
||||
} else {
|
||||
watchBtn.style.display = 'none';
|
||||
}
|
||||
|
||||
// Bouton IMDb
|
||||
document.getElementById('pm-imdb').href =
|
||||
`https://www.imdb.com/find/?q=${encodeURIComponent(p.title)}&s=tt`;
|
||||
|
||||
document.getElementById('programModal').classList.add('visible');
|
||||
}
|
||||
|
||||
function closeProgramModal() {
|
||||
document.getElementById('programModal').classList.remove('visible');
|
||||
}
|
||||
|
||||
// Fermer sur clic overlay
|
||||
document.addEventListener('click', e => {
|
||||
const modal = document.getElementById('programModal');
|
||||
if (e.target === modal) closeProgramModal();
|
||||
});
|
||||
|
||||
// Fermer sur Escape
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.key === 'Escape') closeProgramModal();
|
||||
});
|
||||
+4
-6
@@ -12,12 +12,12 @@ function copyUrl(type) {
|
||||
navigator.clipboard.writeText(url).then(() => {
|
||||
const btns = document.querySelectorAll('.copy-btn');
|
||||
btns.forEach(b => { if (b.textContent.toLowerCase() === type) {
|
||||
b.textContent = '✓ copié';
|
||||
b.textContent = L.copied;
|
||||
b.classList.add('copied');
|
||||
setTimeout(() => { b.textContent = type.toUpperCase(); b.classList.remove('copied'); }, 1800);
|
||||
}});
|
||||
}).catch(() => {
|
||||
prompt('Copiez cette URL :', url);
|
||||
prompt(url, url);
|
||||
});
|
||||
}
|
||||
function fmtTime(d) { return `${pad(d.getHours())}:${pad(d.getMinutes())}`; }
|
||||
@@ -36,10 +36,8 @@ function parseXMLTVDate(str) {
|
||||
function updateClock() {
|
||||
const now = new Date();
|
||||
document.getElementById('clock').textContent = fmtTimeFull(now);
|
||||
const days=['Dim','Lun','Mar','Mer','Jeu','Ven','Sam'];
|
||||
const months=['Jan','Fév','Mar','Avr','Mai','Jun','Jul','Aoû','Sep','Oct','Nov','Déc'];
|
||||
document.getElementById('dateLabel').textContent =
|
||||
`${days[now.getDay()]} ${now.getDate()} ${months[now.getMonth()]} ${now.getFullYear()}`;
|
||||
`${L.days[now.getDay()]} ${now.getDate()} ${L.months[now.getMonth()]} ${now.getFullYear()}`;
|
||||
}
|
||||
setInterval(updateClock, 1000);
|
||||
updateClock();
|
||||
@@ -62,7 +60,7 @@ async function loadEPG(url) {
|
||||
} catch(e) {
|
||||
document.getElementById('loading').style.display = 'none';
|
||||
document.getElementById('errorMsg').style.display = 'flex';
|
||||
document.getElementById('errorDetail').textContent = `Erreur: ${e.message}`;
|
||||
document.getElementById('errorDetail').textContent = `${L.epg_error_sub} (${e.message})`;
|
||||
document.getElementById('epgInput').value = url;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user