180 lines
10 KiB
Plaintext
180 lines
10 KiB
Plaintext
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4465464714854276" crossorigin="anonymous"></script>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>FOLX Music Television — Live | Volksmusik & Schlager</title>
|
|
<meta name="description" content="FOLX TV Live Stream. Oberkrainer, Bayrische und Zillertaler Musik rund um die Uhr. Volksmusik & Schlager Fernsehsender Nr. 1 in DE, AT und CH." />
|
|
<meta name="robots" content="index, follow" />
|
|
<link rel="canonical" href="https://folx.live/" />
|
|
<meta property="og:title" content="FOLX Music Television — Live" />
|
|
<meta property="og:description" content="24/7 Volksmusik & Schlager Live-Stream." />
|
|
<meta property="og:type" content="video.other" />
|
|
<meta property="og:url" content="https://folx.live/" />
|
|
<meta property="og:image" content="https://folx.live/brand/folx-with-tagline-neg.png" />
|
|
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
|
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png" />
|
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
|
<link rel="stylesheet" href="/ds/fonts/fonts.css" />
|
|
<style>
|
|
:root { --akzent: #DC1D4D; --font-display: 'Geograph', system-ui, sans-serif; --font-mono: 'JetBrains Mono', ui-monospace, monospace; }
|
|
* { box-sizing: border-box; }
|
|
html, body { margin: 0; background: #000; }
|
|
body { color: #fff; font-family: var(--font-display); }
|
|
a { color: var(--akzent); text-decoration: none; }
|
|
a:hover { color: #FF4A73; }
|
|
@keyframes fx-pulse { 50% { opacity: .3; } }
|
|
@keyframes fx-vu { from { transform: scaleY(.15); } to { transform: scaleY(1); } }
|
|
input[type="range"].fxvol { accent-color: var(--akzent); }
|
|
::selection { background: var(--akzent); color: #fff; }
|
|
.wrap { min-height: 100vh; background: #000; display: flex; flex-direction: column; }
|
|
header.top { padding: 14px 24px; border-bottom: 1px solid rgba(255,255,255,0.14); }
|
|
.top-inner { width: min(900px, 100%); margin: 0 auto; display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
|
|
header.top .logo { height: 64px; width: auto; display: block; margin: -14px 0; }
|
|
.uhr { font-family: var(--font-mono); font-size: 13px; color: #B8B8B8; letter-spacing: 0.06em; }
|
|
main.stage-wrap { width: min(1240px, 100%); margin: 0 auto; padding: clamp(16px,3vw,40px) clamp(12px,2.5vw,24px) 0; flex: 1; }
|
|
.player-col { width: min(900px, 100%); margin: 0 auto; }
|
|
.player-frame { position: relative; aspect-ratio: 16/9; overflow: hidden; isolation: isolate; background: #0A0A0A; }
|
|
.player-frame video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 1; background:#000; }
|
|
.stage-poster { position: absolute; inset: 0; width:100%; height:100%; object-fit: cover; z-index: 0; }
|
|
.scrim { position: absolute; inset: 0; z-index: 1; background: rgba(10,10,10,0.28); pointer-events:none; }
|
|
.msg { position:absolute; inset:0; z-index:20; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:14px; background:rgba(10,10,10,0.55); color:#fff; font-size:13px; letter-spacing:0.1em; text-transform:uppercase; }
|
|
.msg.hidden { display:none; }
|
|
.spinner { width:38px; height:38px; border:3px solid rgba(255,255,255,0.25); border-top-color:var(--akzent); border-radius:50%; animation: spin 0.9s linear infinite; }
|
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
.ctrl { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; background: #0A0A0A; border: 1px solid rgba(255,255,255,0.14); border-top: 0; padding: 12px 16px; }
|
|
.vu { display: flex; gap: 3px; align-items: flex-end; height: 30px; flex: none; }
|
|
.vu span { width: 3px; height: 100%; background: var(--akzent); transform-origin: bottom; animation: fx-vu 0.7s infinite ease-in-out alternate; }
|
|
.now { flex: 1; min-width: 180px; }
|
|
.now .lbl { font-size: 10px; font-weight: 600; letter-spacing: 0.2em; text-transform: uppercase; color: #6B6B6B; }
|
|
.now .t { font-weight: 700; font-size: 15px; text-transform: uppercase; letter-spacing: -0.005em; margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
.ic { width: 40px; height: 40px; border: 0; background: transparent; color: #fff; cursor: pointer; display: flex; align-items: center; justify-content: center; }
|
|
.ic:hover { color: #B8B8B8; }
|
|
.quality { font-size: 10px; font-weight: 700; letter-spacing: 0.18em; color: #6B6B6B; border: 1px solid rgba(255,255,255,0.25); padding: 4px 8px; white-space: nowrap; }
|
|
footer.bot { margin-top: clamp(32px,6vw,64px); border-top: 1px solid rgba(255,255,255,0.14); padding: 18px 24px; display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
|
|
footer.bot span { font-size: 10px; font-weight: 600; letter-spacing: 0.2em; text-transform: uppercase; color: #6B6B6B; }
|
|
:fullscreen .player-frame, .player-frame:fullscreen { aspect-ratio: auto; width:100%; height:100%; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="wrap">
|
|
<header class="top">
|
|
<div class="top-inner">
|
|
<img class="logo" src="/brand/folx-logo-neg.png" alt="FOLX Music Television" />
|
|
<div class="uhr" id="uhr"></div>
|
|
</div>
|
|
</header>
|
|
|
|
<main class="stage-wrap">
|
|
<div class="player-col">
|
|
<div class="player-frame" id="playerFrame">
|
|
<img class="stage-poster" id="poster" src="/assets/stage_folx_v2.png" alt="" />
|
|
<div class="scrim"></div>
|
|
<video id="v" autoplay muted playsinline tabindex="-1" disablepictureinpicture
|
|
controlsList="nodownload noremoteplayback noplaybackrate"></video>
|
|
|
|
<div class="msg" id="msg"><div class="spinner"></div><div>Stream wird geladen…</div></div>
|
|
</div>
|
|
|
|
<div class="ctrl">
|
|
<div class="vu" id="vu"><span style="animation-delay:0ms"></span><span style="animation-delay:120ms"></span><span style="animation-delay:240ms"></span><span style="animation-delay:60ms"></span><span style="animation-delay:300ms"></span><span style="animation-delay:180ms"></span><span style="animation-delay:90ms"></span></div>
|
|
<div class="now">
|
|
<div class="lbl">Jetzt läuft</div>
|
|
<div class="t" id="nowTitle">Folx Music Television</div>
|
|
</div>
|
|
<div style="display:flex; align-items:center; gap:12px; flex:none;">
|
|
<button class="ic" id="btnAudio" title="Ton umschalten" aria-label="Ton umschalten">🔇</button>
|
|
<span class="quality">AUTO · HD</span>
|
|
<button class="ic" id="btnFs" title="Vollbild" aria-label="Vollbild">⛶</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<footer class="bot">
|
|
<span>Folx — Music Television</span>
|
|
<span><a href="https://folx.live" style="color:#B8B8B8;">folx.live</a> · © 2026 Folx Network</span>
|
|
</footer>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/hls.js@1.5.17/dist/hls.min.js"></script>
|
|
<script>
|
|
(function(){
|
|
var HLS_URL = <%- JSON.stringify(hlsUrl) %>;
|
|
var video = document.getElementById('v');
|
|
var msg = document.getElementById('msg');
|
|
var poster = document.getElementById('poster');
|
|
var hls = null;
|
|
function hideMsg(){ msg.classList.add('hidden'); poster.style.display='none'; }
|
|
function showMsg(){ msg.classList.remove('hidden'); }
|
|
|
|
function start(){
|
|
try { hls && hls.destroy(); } catch(e){}
|
|
hls = null;
|
|
if (window.Hls && window.Hls.isSupported()) {
|
|
hls = new Hls({ liveSyncDurationCount: 3, maxBufferLength: 10, manifestLoadingMaxRetry: 6, levelLoadingMaxRetry: 6, fragLoadingMaxRetry: 6 });
|
|
hls.loadSource(HLS_URL);
|
|
hls.attachMedia(video);
|
|
hls.on(Hls.Events.MANIFEST_PARSED, function(){ hideMsg(); video.play().catch(function(){ setTimeout(function(){ video.play().catch(function(){}); }, 200); }); });
|
|
hls.on(Hls.Events.ERROR, function(_e, data){
|
|
if (data && data.fatal) {
|
|
if (data.type === Hls.ErrorTypes.NETWORK_ERROR) { showMsg(); try { hls.startLoad(); } catch(e){ setTimeout(start, 2000); } }
|
|
else if (data.type === Hls.ErrorTypes.MEDIA_ERROR) { try { hls.recoverMediaError(); } catch(e){ setTimeout(start, 2000); } }
|
|
else { setTimeout(start, 2500); }
|
|
}
|
|
});
|
|
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
|
|
video.src = HLS_URL;
|
|
video.addEventListener('playing', hideMsg, { once:true });
|
|
video.play().catch(function(){});
|
|
}
|
|
}
|
|
start();
|
|
|
|
// Twitch-style: no pause; resume to live edge
|
|
video.addEventListener('pause', function(){ if (!document.hidden) { setTimeout(function(){ video.play().catch(function(){}); }, 60); } });
|
|
video.addEventListener('contextmenu', function(e){ e.preventDefault(); });
|
|
|
|
// Uhr
|
|
function tickUhr(){ var d=new Date(); document.getElementById('uhr').textContent = d.toLocaleTimeString('de-DE',{hour:'2-digit',minute:'2-digit'}) + ' Uhr'; }
|
|
tickUhr(); setInterval(tickUhr, 10000);
|
|
|
|
// Audio toggle
|
|
var btnA = document.getElementById('btnAudio');
|
|
btnA.addEventListener('click', function(){ video.muted = !video.muted; if(!video.muted && video.volume===0) video.volume=0.8; btnA.textContent = video.muted ? '🔇' : '🔊'; });
|
|
// Fullscreen
|
|
document.getElementById('btnFs').addEventListener('click', function(){
|
|
var f = document.getElementById('playerFrame');
|
|
if (document.fullscreenElement || document.webkitFullscreenElement) {
|
|
(document.exitFullscreen || document.webkitExitFullscreen).call(document);
|
|
} else if (f.requestFullscreen) {
|
|
f.requestFullscreen();
|
|
} else if (f.webkitRequestFullscreen) {
|
|
f.webkitRequestFullscreen();
|
|
} else if (video.webkitEnterFullscreen) {
|
|
// iOS Safari: fullscreen deluje samo na video elementu
|
|
video.webkitEnterFullscreen();
|
|
}
|
|
});
|
|
|
|
// Now-Playing poll
|
|
var nowT = document.getElementById('nowTitle');
|
|
var lastSong = null;
|
|
function applyNP(np){
|
|
var label = null;
|
|
if (np.kind === 'song') { label = np.label; lastSong = np.label; }
|
|
else if (np.kind === 'genius') { label = 'GENIUS'; }
|
|
else if (np.kind === 'werbung') { label = 'Werbung'; }
|
|
else { label = lastSong; } // skip (jingle/pasica) -> obdrzi zadnji komad
|
|
nowT.textContent = label || 'Folx Music Television';
|
|
}
|
|
function pollNP(){
|
|
fetch('/api/now-playing', { cache:'no-store' }).then(function(r){ return r.json(); }).then(function(d){ if(d && d.ok) applyNP(d); }).catch(function(){});
|
|
}
|
|
pollNP(); setInterval(pollNP, 8000);
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|