= htmlspecialchars($item['title']) ?>
@@ -39,25 +82,50 @@ $isFavoritesView = ($view ?? '') === 'favorites';= htmlspecialchars($item['description']) ?>
diff --git a/app/config/channels.json b/app/config/channels.json index 4edb1ed..f545396 100644 --- a/app/config/channels.json +++ b/app/config/channels.json @@ -225,7 +225,7 @@ "icon": "", "country": "", "language": "", - "enabled": false, + "enabled": true, "favorite": false, "number": 210, "group": "Österreich" diff --git a/app/public/assets/css/main.css b/app/public/assets/css/main.css index 4a58ab4..620026f 100644 --- a/app/public/assets/css/main.css +++ b/app/public/assets/css/main.css @@ -186,3 +186,286 @@ align-items: center; gap: 0.5rem; } + +.now-card-enhanced { + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.now-card-top { + display: flex; + align-items: center; + gap: 0.85rem; +} + +.now-channel-logo { + width: 64px; + height: 44px; + object-fit: contain; + background: var(--panel); + border: 1px solid var(--border); + border-radius: 0.55rem; + padding: 4px; + flex: 0 0 64px; +} + +.now-channel-info { + min-width: 0; +} + +.now-channel-name { + display: flex; + align-items: center; + gap: 0.4rem; + font-weight: 700; +} + +.now-time { + color: var(--muted); + font-size: 0.9rem; + margin-top: 0.15rem; +} + +.now-badges { + display: flex; + flex-wrap: wrap; + gap: 0.4rem; +} + +.now-badge { + display: inline-flex; + align-items: center; + gap: 0.25rem; + border: 1px solid var(--border); + background: var(--panel-2); + color: var(--text); + border-radius: 999px; + padding: 0.2rem 0.55rem; + font-size: 0.82rem; +} + +.now-next { + display: grid; + grid-template-columns: auto 1fr auto; + gap: 0.5rem; + align-items: center; + border-top: 1px solid var(--border); + padding-top: 0.65rem; + color: var(--muted); + font-size: 0.9rem; +} + +.now-next strong { + color: var(--text); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.now-next-label { + color: var(--muted); + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 0.04em; +} + +.now-channel-logo { + display: block; + width: 64px; + height: 44px; + min-width: 64px; + object-fit: contain; + background: var(--panel); + border: 1px solid var(--border); + border-radius: 0.55rem; + padding: 4px; +} + +.progress-wrap { + width: 100%; +} + +.progress-bar { + width: 100%; + height: 10px; + background: var(--panel-2); + border-radius: 999px; + overflow: hidden; + border: 1px solid var(--border); +} + +.progress-fill { + display: block; + height: 100%; + min-width: 2px; + background: var(--accent); + border-radius: 999px; +} + +.progress-meta { + display: flex; + justify-content: space-between; + color: var(--muted); + font-size: 0.85rem; + margin-bottom: 0.35rem; +} + +.now-grid-enhanced { + gap: 1.5rem; +} + +.now-card-enhanced { + padding: 1.35rem; + border-radius: 14px; + gap: 0.9rem; +} + +.now-card-top { + margin-bottom: 0.35rem; +} + +.now-channel-logo { + width: 76px; + height: 52px; + flex-basis: 76px; +} + +.now-channel-name { + font-size: 1rem; +} + +.now-time { + font-size: 0.92rem; +} + +.now-card-enhanced h3 { + font-size: 1.35rem; + margin: 0.1rem 0 0; +} + +.now-subtitle { + color: var(--muted); + margin-top: -0.35rem; +} + +.now-badges { + margin-top: 0.1rem; +} + +.progress-wrap { + margin-top: 0.25rem; +} + +.progress-bar { + height: 12px; +} + +.progress-meta { + margin-bottom: 0.4rem; +} + +.now-next { + margin-top: 0.45rem; + padding-top: 0.85rem; + border-top: 1px solid var(--border); + background: rgba(255, 255, 255, 0.025); + border-radius: 0 0 10px 10px; +} + +.now-next-label { + font-size: 0.75rem; +} + +.now-next strong { + font-size: 0.95rem; +} + +.now-next { + display: block; + margin-top: 0.75rem; + padding-top: 0.75rem; + border-top: 1px solid var(--border); + background: transparent; + border-radius: 0; + color: var(--muted); +} + +.now-next-programme { + display: flex; + gap: 1rem; + align-items: flex-start; + margin-top: 0.35rem; +} + +.now-next-time { + min-width: 54px; + font-weight: 700; + color: var(--accent); +} + +.now-next-info { + flex: 1; + min-width: 0; +} + +.now-next-info strong { + display: block; + color: var(--text); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.now-next-subtitle { + margin-top: 0.15rem; + font-size: 0.9rem; + color: var(--muted); +} + +.now-card-top { + display: flex; + gap: 1rem; + align-items: center; +} + +.now-channel-logo-wrap { + width: 92px; + height: 64px; + flex: 0 0 92px; + + display: flex; + align-items: center; + justify-content: center; + + background: var(--panel-2); + border: 1px solid var(--border); + border-radius: .8rem; +} + +.now-channel-logo { + width: 82px; + height: 52px; + object-fit: contain; +} + +.now-channel-info { + flex: 1; +} + +.now-channel-name { + font-size: 1.15rem; + font-weight: 700; +} + +.now-channel-number { + margin-top: .15rem; + color: var(--muted); + font-size: .9rem; +} + +.now-time { + margin-top: .25rem; + color: var(--muted); + font-size: .95rem; +} diff --git a/app/public/assets/css/timeline.css b/app/public/assets/css/timeline.css index 58d9c01..fc66913 100644 --- a/app/public/assets/css/timeline.css +++ b/app/public/assets/css/timeline.css @@ -189,3 +189,112 @@ background: var(--accent); border-color: var(--accent); } + +cat >> app/public/assets/css/timeline.css <<'CSS' + +.timeline-channel { + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; + gap: 6px; +} + +.timeline-channel .channel-logo { + width: 72px; + height: 48px; + flex: 0 0 auto; + padding: 4px; +} + +.timeline-channel-name { + font-size: 0.82rem; + line-height: 1.15; + max-width: 145px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.timeline-programme { + border-left: 5px solid rgba(148, 163, 184, 0.45); +} + +.timeline-programme.is-current { + opacity: 1; + border-color: var(--accent); + box-shadow: 0 0 0 1px var(--accent-soft), 0 8px 22px rgba(0, 0, 0, 0.45); + z-index: 10; +} + +.live-badge { + display: inline-flex; + margin-bottom: 3px; + padding: 1px 6px; + border-radius: 999px; + background: var(--accent); + color: white; + font-size: 0.65rem; + font-style: normal; + font-weight: 900; + letter-spacing: 0.04em; +} + +.genre-film, +.genre-movie, +.genre-kino { + border-left-color: #3b82f6; +} + +.genre-serie, +.genre-series { + border-left-color: #8b5cf6; +} + +.genre-sport, +.genre-fussball, +.genre-fu-ball { + border-left-color: #22c55e; +} + +.genre-news, +.genre-nachrichten, +.genre-journal { + border-left-color: #ef4444; +} + +.genre-doku, +.genre-documentary { + border-left-color: #14b8a6; +} + +.genre-kinder, +.genre-kids, +.genre-children { + border-left-color: #f97316; +} + +.genre-musik, +.genre-music { + border-left-color: #eab308; +} + +.timeline-programme:hover::after { + content: attr(title); + position: absolute; + left: 0; + top: calc(100% + 8px); + width: max-content; + max-width: 420px; + white-space: normal; + background: var(--panel-2); + border: 1px solid var(--border); + color: var(--text); + padding: 10px 12px; + border-radius: 10px; + box-shadow: 0 12px 28px rgba(0,0,0,.45); + z-index: 50; + font-size: 0.85rem; + line-height: 1.35; +} +CSS diff --git a/app/public/assets/js/live-day.js b/app/public/assets/js/live-day.js index 690d807..0b06428 100644 --- a/app/public/assets/js/live-day.js +++ b/app/public/assets/js/live-day.js @@ -15,9 +15,44 @@ function dayUpdateProgrammeStates() { document.querySelectorAll('[data-live-programme]').forEach(programme => { const start = Number(programme.dataset.liveStart); const stop = Number(programme.dataset.liveStop); + const isCurrent = now >= start && now < stop; - programme.classList.toggle('is-current', now >= start && now < stop); + programme.classList.toggle('is-current', isCurrent); programme.classList.toggle('is-past', now >= stop); + + const badge = programme.querySelector('[data-live-badge]'); + if (badge) { + badge.hidden = !isCurrent; + } + }); +} + +function daySetupZoomMemory() { + const wrap = document.querySelector('.timeline-wrap'); + const buttons = document.querySelectorAll('.timeline-zoom button'); + + if (!wrap || !buttons.length) return; + + const savedZoom = localStorage.getItem('tvguide.timelineZoom'); + + if (savedZoom) { + wrap.style.setProperty('--minute-width', `${savedZoom}px`); + + buttons.forEach(button => { + button.classList.toggle('active', button.dataset.zoom === savedZoom); + }); + } + + buttons.forEach(button => { + button.addEventListener('click', () => { + const zoom = button.dataset.zoom; + + wrap.style.setProperty('--minute-width', `${zoom}px`); + localStorage.setItem('tvguide.timelineZoom', zoom); + + buttons.forEach(b => b.classList.remove('active')); + button.classList.add('active'); + }); }); } @@ -27,6 +62,7 @@ function dayTick() { } document.addEventListener('DOMContentLoaded', () => { + daySetupZoomMemory(); dayTick(); setInterval(dayTick, 1000); }); diff --git a/app/public/assets/js/live-now.js b/app/public/assets/js/live-now.js index 5d52f90..e2aad63 100644 --- a/app/public/assets/js/live-now.js +++ b/app/public/assets/js/live-now.js @@ -1,22 +1,67 @@ +function nowEscape(value) { + return String(value ?? '').replace(/[&<>"']/g, char => ({ + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }[char])); +} + +function nowLogoUrl(channelId) { + return `/assets/logos/${encodeURIComponent(channelId)}.svg`; +} + function nowCreateCard(item) { const article = document.createElement('article'); - article.className = 'now-card'; + article.className = 'now-card now-card-enhanced'; article.innerHTML = ` -
= htmlspecialchars($item['description']) ?>