215 lines
13 KiB
Plaintext
215 lines
13 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); background-size: cover; background-position: center; background-attachment: fixed; background-repeat: no-repeat; }
|
|
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; display: flex; flex-direction: column; background: rgba(0,0,0,0.35); }
|
|
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: rgba(10,10,10,0.35); border: 1px solid rgba(255,255,255,0.14); }
|
|
.player-frame video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 1; background:transparent; }
|
|
.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; }
|
|
.bot-inner { width: min(900px, 100%); margin: 0 auto; 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 style="background-image: url('<%= bg %>');">
|
|
<div class="wrap">
|
|
<header class="top">
|
|
<div class="top-inner">
|
|
<img class="logo" src="/brand/folx-logo-transparent.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">
|
|
<video id="v" autoplay muted playsinline tabindex="-1"
|
|
x-webkit-airplay="allow"
|
|
controlsList="nodownload 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>
|
|
<google-cast-launcher id="castBtn" title="Auf TV streamen" style="--connected-color:var(--akzent); --disconnected-color:#fff; width:26px; height:26px; cursor:pointer; display:none;"></google-cast-launcher>
|
|
<button class="ic" id="btnAirplay" title="AirPlay" aria-label="AirPlay" style="display:none;">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"><path d="M5 17H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h18a1 1 0 0 1 1 1v11a1 1 0 0 1-1 1h-2"/><path d="M12 15l5 6H7l5-6Z" fill="currentColor"/></svg>
|
|
</button>
|
|
<button class="ic" id="btnFs" title="Vollbild" aria-label="Vollbild">⛶</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<footer class="bot">
|
|
<div class="bot-inner">
|
|
<span>Folx — Music Television</span>
|
|
<span><a href="https://folx.live" style="color:#B8B8B8;">folx.live</a> · © 2026 Folx Network</span>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
|
|
<script src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>
|
|
<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 hls = null;
|
|
function hideMsg(){ msg.classList.add('hidden'); }
|
|
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();
|
|
}
|
|
});
|
|
|
|
// AirPlay (Safari/iOS): pokazi gumb ce je na voljo
|
|
var btnAP = document.getElementById('btnAirplay');
|
|
if (window.WebKitPlaybackTargetAvailabilityEvent) {
|
|
video.addEventListener('webkitplaybacktargetavailabilitychanged', function(e){
|
|
btnAP.style.display = (e.availability === 'available') ? 'flex' : 'none';
|
|
});
|
|
btnAP.addEventListener('click', function(){ try { video.webkitShowPlaybackTargetPicker(); } catch(err){} });
|
|
}
|
|
|
|
// Chromecast: SDK klic ob nalozitvi
|
|
window.__onGCastApiAvailable = function(isAvailable){
|
|
if (!isAvailable) return;
|
|
var ctx = cast.framework.CastContext.getInstance();
|
|
ctx.setOptions({ receiverApplicationId: chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID, autoJoinPolicy: chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED });
|
|
var castBtn = document.getElementById('castBtn');
|
|
castBtn.style.display = 'inline-block';
|
|
// absoluten URL do streama za cast napravo
|
|
var streamAbs = location.origin + HLS_URL;
|
|
ctx.addEventListener(cast.framework.CastContextEventType.SESSION_STATE_CHANGED, function(ev){
|
|
if (ev.sessionState === cast.framework.SessionState.SESSION_STARTED || ev.sessionState === cast.framework.SessionState.SESSION_RESUMED) {
|
|
var session = ctx.getCurrentSession();
|
|
if (!session) return;
|
|
var mediaInfo = new chrome.cast.media.MediaInfo(streamAbs, 'application/x-mpegURL');
|
|
mediaInfo.streamType = chrome.cast.media.StreamType.LIVE;
|
|
mediaInfo.metadata = new chrome.cast.media.GenericMediaMetadata();
|
|
mediaInfo.metadata.title = 'Folx Music Television — Live';
|
|
var request = new chrome.cast.media.LoadRequest(mediaInfo);
|
|
session.loadMedia(request).catch(function(){});
|
|
}
|
|
});
|
|
};
|
|
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>
|