Nova folx.live stran (Folx TV): brand dizajn, now-playing iz master.biba.live (song/GENIUS/Werbung)
This commit is contained in:
parent
e0ab925d49
commit
b14fbc5d5f
BIN
public/assets/stage_folx_v2.png
Normal file
BIN
public/assets/stage_folx_v2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 980 KiB |
BIN
public/ds/fonts/Geog-BdIt.otf
Normal file
BIN
public/ds/fonts/Geog-BdIt.otf
Normal file
Binary file not shown.
BIN
public/ds/fonts/Geog-LgIt.otf
Normal file
BIN
public/ds/fonts/Geog-LgIt.otf
Normal file
Binary file not shown.
BIN
public/ds/fonts/Geog-MdIt.otf
Normal file
BIN
public/ds/fonts/Geog-MdIt.otf
Normal file
Binary file not shown.
BIN
public/ds/fonts/Geog-RgIt.otf
Normal file
BIN
public/ds/fonts/Geog-RgIt.otf
Normal file
Binary file not shown.
BIN
public/ds/fonts/Geog-SbIt.otf
Normal file
BIN
public/ds/fonts/Geog-SbIt.otf
Normal file
Binary file not shown.
BIN
public/ds/fonts/Geog-ThIt.otf
Normal file
BIN
public/ds/fonts/Geog-ThIt.otf
Normal file
Binary file not shown.
BIN
public/ds/fonts/Geog-UlIt.otf
Normal file
BIN
public/ds/fonts/Geog-UlIt.otf
Normal file
Binary file not shown.
BIN
public/ds/fonts/Geogtq-Bd.otf
Normal file
BIN
public/ds/fonts/Geogtq-Bd.otf
Normal file
Binary file not shown.
BIN
public/ds/fonts/Geogtq-Lg.otf
Normal file
BIN
public/ds/fonts/Geogtq-Lg.otf
Normal file
Binary file not shown.
BIN
public/ds/fonts/Geogtq-Md.otf
Normal file
BIN
public/ds/fonts/Geogtq-Md.otf
Normal file
Binary file not shown.
BIN
public/ds/fonts/Geogtq-Rg.otf
Normal file
BIN
public/ds/fonts/Geogtq-Rg.otf
Normal file
Binary file not shown.
BIN
public/ds/fonts/Geogtq-Sb.otf
Normal file
BIN
public/ds/fonts/Geogtq-Sb.otf
Normal file
Binary file not shown.
BIN
public/ds/fonts/Geogtq-Th.otf
Normal file
BIN
public/ds/fonts/Geogtq-Th.otf
Normal file
Binary file not shown.
BIN
public/ds/fonts/Geogtq-Ul.otf
Normal file
BIN
public/ds/fonts/Geogtq-Ul.otf
Normal file
Binary file not shown.
16
public/ds/fonts/fonts.css
Normal file
16
public/ds/fonts/fonts.css
Normal file
@ -0,0 +1,16 @@
|
||||
/* Geograph (Geogtq) — official brand typeface */
|
||||
@font-face { font-family: 'Geograph'; src: url('Geogtq-Ul.otf') format('opentype'); font-weight: 200; font-style: normal; font-display: swap; }
|
||||
@font-face { font-family: 'Geograph'; src: url('Geogtq-Th.otf') format('opentype'); font-weight: 300; font-style: normal; font-display: swap; }
|
||||
@font-face { font-family: 'Geograph'; src: url('Geogtq-Lg.otf') format('opentype'); font-weight: 400; font-style: normal; font-display: swap; }
|
||||
@font-face { font-family: 'Geograph'; src: url('Geogtq-Rg.otf') format('opentype'); font-weight: 500; font-style: normal; font-display: swap; }
|
||||
@font-face { font-family: 'Geograph'; src: url('Geogtq-Md.otf') format('opentype'); font-weight: 600; font-style: normal; font-display: swap; }
|
||||
@font-face { font-family: 'Geograph'; src: url('Geogtq-Sb.otf') format('opentype'); font-weight: 700; font-style: normal; font-display: swap; }
|
||||
@font-face { font-family: 'Geograph'; src: url('Geogtq-Bd.otf') format('opentype'); font-weight: 800; font-style: normal; font-display: swap; }
|
||||
|
||||
@font-face { font-family: 'Geograph'; src: url('Geog-UlIt.otf') format('opentype'); font-weight: 200; font-style: italic; font-display: swap; }
|
||||
@font-face { font-family: 'Geograph'; src: url('Geog-ThIt.otf') format('opentype'); font-weight: 300; font-style: italic; font-display: swap; }
|
||||
@font-face { font-family: 'Geograph'; src: url('Geog-LgIt.otf') format('opentype'); font-weight: 400; font-style: italic; font-display: swap; }
|
||||
@font-face { font-family: 'Geograph'; src: url('Geog-RgIt.otf') format('opentype'); font-weight: 500; font-style: italic; font-display: swap; }
|
||||
@font-face { font-family: 'Geograph'; src: url('Geog-MdIt.otf') format('opentype'); font-weight: 600; font-style: italic; font-display: swap; }
|
||||
@font-face { font-family: 'Geograph'; src: url('Geog-SbIt.otf') format('opentype'); font-weight: 700; font-style: italic; font-display: swap; }
|
||||
@font-face { font-family: 'Geograph'; src: url('Geog-BdIt.otf') format('opentype'); font-weight: 800; font-style: italic; font-display: swap; }
|
||||
@ -116,6 +116,46 @@ app.get('/stream/:n/variant/:filename', (req, res) => {
|
||||
res.redirect(302, url);
|
||||
});
|
||||
|
||||
// Now-Playing proxy: reads master.biba.live AirPlay monitor, maps Folx TV current item.
|
||||
// song -> title (artist - title)
|
||||
// spot+GENIUS-> "GENIUS"
|
||||
// spot -> "Werbung"
|
||||
// jingle/animation -> skipped (label null; client keeps last song)
|
||||
const NOWPLAYING_URL = process.env.NOWPLAYING_URL || 'https://master.biba.live/api/live';
|
||||
const NOWPLAYING_STATION = process.env.NOWPLAYING_STATION || 'folx_tv';
|
||||
|
||||
function mapNowPlaying(np) {
|
||||
if (!np) return { kind: 'none', label: null };
|
||||
const type = (np.play_type || '').toLowerCase();
|
||||
const camp = (np.campaign || '').toUpperCase();
|
||||
if (type === 'song') {
|
||||
const artist = (np.artist || '').trim();
|
||||
const title = (np.display_title || np.title || '').trim();
|
||||
const label = artist ? `${artist} — ${title}` : title;
|
||||
return { kind: 'song', label, artist, title, started_at: np.started_at };
|
||||
}
|
||||
if (type === 'spot') {
|
||||
if (camp === 'GENIUS') return { kind: 'genius', label: 'GENIUS', started_at: np.started_at };
|
||||
return { kind: 'werbung', label: 'Werbung', started_at: np.started_at };
|
||||
}
|
||||
// jingle, animation (PASICA), contribution -> skip
|
||||
return { kind: 'skip', label: null };
|
||||
}
|
||||
|
||||
app.get('/api/now-playing', async (_req, res) => {
|
||||
try {
|
||||
const r = await fetch(NOWPLAYING_URL, { headers: { 'User-Agent': 'folx-live/1.0' } });
|
||||
if (!r.ok) return res.status(502).json({ ok: false, error: `origin ${r.status}` });
|
||||
const arr = await r.json();
|
||||
const row = Array.isArray(arr) ? arr.find(x => x.station_slug === NOWPLAYING_STATION) : null;
|
||||
const mapped = mapNowPlaying(row && row.now_playing);
|
||||
res.set('Cache-Control', 'no-cache, no-store, must-revalidate');
|
||||
res.json({ ok: true, station: NOWPLAYING_STATION, ...mapped });
|
||||
} catch (err) {
|
||||
res.status(502).json({ ok: false, error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/health', (_req, res) => {
|
||||
res.json({
|
||||
ok: true,
|
||||
|
||||
899
views/index.ejs
899
views/index.ejs
@ -1,794 +1,187 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de" class="dark">
|
||||
<html lang="de">
|
||||
<head>
|
||||
<!-- Google AdSense -->
|
||||
<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>
|
||||
|
||||
<!-- SEO -->
|
||||
<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="keywords" content="Volksmusik, Schlager, Folx TV Live, Folx Music Television, Oberkrainer, Bayrische Musik, Zillertaler Musik, Live Stream, Musiksender, Alpenmusik, Volksmusik Live" />
|
||||
<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/" />
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:title" content="FOLX Music Television — Live" />
|
||||
<meta property="og:description" content="24/7 Volksmusik & Schlager Live-Stream. Oberkrainer, Bayrische und Zillertaler Musik." />
|
||||
<meta property="og:description" content="24/7 Volksmusik & Schlager Live-Stream." />
|
||||
<meta property="og:type" content="video.other" />
|
||||
<meta property="og:site_name" content="FOLX Music Television" />
|
||||
<meta property="og:url" content="https://folx.live/" />
|
||||
<meta property="og:image" content="https://folx.live/brand/folx-with-tagline-neg.png" />
|
||||
<meta property="og:image:width" content="1241" />
|
||||
<meta property="og:image:height" content="875" />
|
||||
<meta property="og:locale" content="de_DE" />
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="FOLX Music Television — Live" />
|
||||
<meta name="twitter:description" content="24/7 Volksmusik & Schlager Live-Stream." />
|
||||
<meta name="twitter:image" content="https://folx.live/brand/folx-with-tagline-neg.png" />
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16.png" />
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="/favicon-192.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
|
||||
<!-- Fonts: Druk (Archivo Black proxy) for display + Inter for body -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Archivo+Black&family=Archivo:wght@400;500;600;700;800;900&display=swap" rel="stylesheet" />
|
||||
|
||||
<link rel="stylesheet" href="/ds/fonts/fonts.css" />
|
||||
<style>
|
||||
:root {
|
||||
--folx-magenta: #dc1c4c;
|
||||
--folx-magenta-bright: #ff2e63;
|
||||
--folx-magenta-deep: #9d0f32;
|
||||
--bg-0: #000000;
|
||||
--bg-1: #0a0a0a;
|
||||
--bg-2: #141414;
|
||||
--bg-3: #1e1e1e;
|
||||
--border: #2a2a2a;
|
||||
--text: #f5f5f5;
|
||||
--text-muted: #888888;
|
||||
--text-faint: #555555;
|
||||
}
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
html, body {
|
||||
background: var(--bg-0);
|
||||
color: var(--text);
|
||||
font-family: 'Archivo', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
font-weight: 500;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
line-height: 1.5;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* ======================= NAVIGATION ======================= */
|
||||
.topnav {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 50;
|
||||
background: rgba(0, 0, 0, 0.92);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.topnav-inner {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 12px 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
min-height: 70px;
|
||||
}
|
||||
.brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
.brand-logo {
|
||||
height: 48px;
|
||||
width: auto;
|
||||
display: block;
|
||||
}
|
||||
@media (max-width: 520px) {
|
||||
.brand-logo { height: 36px; }
|
||||
.topnav-inner { padding: 10px 16px; min-height: 56px; }
|
||||
}
|
||||
.brand-sep {
|
||||
width: 1px;
|
||||
height: 24px;
|
||||
background: var(--border);
|
||||
}
|
||||
.brand-tagline {
|
||||
font-family: 'Archivo Black', sans-serif;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.22em;
|
||||
color: var(--text-muted);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.live-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 10px 6px 8px;
|
||||
border-radius: 3px;
|
||||
background: var(--folx-magenta);
|
||||
color: white;
|
||||
font-family: 'Archivo Black', sans-serif;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.live-dot {
|
||||
display: inline-block;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: white;
|
||||
animation: livePulse 1.6s infinite;
|
||||
}
|
||||
@keyframes livePulse {
|
||||
0%, 100% { opacity: 1; transform: scale(1); }
|
||||
50% { opacity: 0.4; transform: scale(0.85); }
|
||||
}
|
||||
|
||||
/* ======================= HERO PLAYER ======================= */
|
||||
.hero {
|
||||
padding: 12px 24px 24px;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.hero-header {
|
||||
margin: 6px 0 14px;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 14px;
|
||||
}
|
||||
.hero-title {
|
||||
font-family: 'Archivo Black', sans-serif;
|
||||
font-size: clamp(28px, 4vw, 52px);
|
||||
line-height: 0.95;
|
||||
letter-spacing: -0.02em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.hero-title .accent { color: var(--folx-magenta); }
|
||||
.hero-meta {
|
||||
font-size: 13px;
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.hero-meta .dot {
|
||||
color: var(--folx-magenta);
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
/* Player wrapper — always 16:9 */
|
||||
.player-frame {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
background: #000;
|
||||
border: 1px solid var(--border);
|
||||
overflow: hidden;
|
||||
}
|
||||
.player-frame video {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #000;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Overlay layer for the controls + GO LIVE pill */
|
||||
.player-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 3;
|
||||
background: linear-gradient(180deg, rgba(0,0,0,0.4) 0%, rgba(0,0,0,0) 18%, rgba(0,0,0,0) 70%, rgba(0,0,0,0.55) 100%);
|
||||
opacity: 0;
|
||||
transition: opacity 0.25s;
|
||||
}
|
||||
.player-frame:hover .player-overlay,
|
||||
.player-frame:focus-within .player-overlay,
|
||||
.player-frame.touched .player-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
.player-controls {
|
||||
position: absolute;
|
||||
bottom: 14px;
|
||||
right: 14px;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.pctl {
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
backdrop-filter: blur(8px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
color: white;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
transition: all 0.15s;
|
||||
padding: 0;
|
||||
}
|
||||
.pctl:hover {
|
||||
background: var(--folx-magenta);
|
||||
border-color: var(--folx-magenta);
|
||||
}
|
||||
.pctl {
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
backdrop-filter: blur(8px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
color: white;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
transition: all 0.15s;
|
||||
padding: 0;
|
||||
}
|
||||
.pctl:hover {
|
||||
background: var(--folx-magenta);
|
||||
border-color: var(--folx-magenta);
|
||||
}
|
||||
|
||||
/* Status & loading */
|
||||
.player-msg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
color: var(--text-muted);
|
||||
font-size: 14px;
|
||||
letter-spacing: 0.05em;
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
background: rgba(0,0,0,0.4);
|
||||
}
|
||||
.player-msg.visible { opacity: 1; }
|
||||
.spinner {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border: 2px solid var(--border);
|
||||
border-top-color: var(--folx-magenta);
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
: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 { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; padding: 14px 24px; border-bottom: 1px solid rgba(255,255,255,0.14); }
|
||||
header.top .logo { height: 64px; width: auto; display: block; margin: -14px 0; }
|
||||
.badge-live { display: inline-flex; align-items: center; gap: 8px; background: var(--akzent); color: #fff; padding: 8px 14px; font-size: 11px; font-weight: 700; letter-spacing: 0.22em; text-transform: uppercase; }
|
||||
.badge-live .d { width: 7px; height: 7px; border-radius: 50%; background: #fff; animation: fx-pulse 1s infinite; }
|
||||
.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; width:100%; }
|
||||
.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; }
|
||||
.bug-live { position: absolute; top: 18px; left: 18px; z-index: 10; display: inline-flex; align-items: center; gap: 8px; background: rgba(10,10,10,0.78); color: #fff; padding: 7px 12px; font-size: 10px; font-weight: 700; letter-spacing: 0.24em; text-transform: uppercase; }
|
||||
.bug-live .d { width: 7px; height: 7px; border-radius: 50%; background: var(--akzent); animation: fx-pulse 1s infinite; }
|
||||
.bug-right { position: absolute; top: 18px; right: 18px; z-index: 10; background: rgba(10,10,10,0.78); color: #D0D0D0; padding: 7px 12px; font-size: 10px; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase; }
|
||||
.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); } }
|
||||
|
||||
/* ======================= SECTIONS ======================= */
|
||||
section {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 48px 24px;
|
||||
}
|
||||
|
||||
/* Feature row */
|
||||
.features {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 2px;
|
||||
background: var(--border);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
.feature {
|
||||
background: var(--bg-1);
|
||||
padding: 28px 24px;
|
||||
}
|
||||
.feature-num {
|
||||
font-family: 'Archivo Black', sans-serif;
|
||||
font-size: 11px;
|
||||
color: var(--folx-magenta);
|
||||
letter-spacing: 0.2em;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.feature-title {
|
||||
font-family: 'Archivo Black', sans-serif;
|
||||
font-size: 20px;
|
||||
letter-spacing: -0.01em;
|
||||
margin-bottom: 8px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.feature-desc {
|
||||
color: var(--text-muted);
|
||||
font-size: 14px;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
/* Programm section — editorial list */
|
||||
.section-label {
|
||||
font-family: 'Archivo Black', sans-serif;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.24em;
|
||||
color: var(--folx-magenta);
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.section-head {
|
||||
font-family: 'Archivo Black', sans-serif;
|
||||
font-size: clamp(28px, 4vw, 48px);
|
||||
line-height: 1;
|
||||
letter-spacing: -0.02em;
|
||||
margin-bottom: 8px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.section-intro {
|
||||
max-width: 720px;
|
||||
color: var(--text-muted);
|
||||
font-size: 15px;
|
||||
line-height: 1.65;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
/* Info blocks (3-column under "Was läuft bei FOLX") */
|
||||
.info-blocks {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 2px;
|
||||
background: var(--border);
|
||||
border: 1px solid var(--border);
|
||||
margin-top: 32px;
|
||||
}
|
||||
.info-block {
|
||||
background: var(--bg-1);
|
||||
padding: 32px 28px;
|
||||
}
|
||||
.info-num {
|
||||
font-family: 'Archivo Black', sans-serif;
|
||||
font-size: 11px;
|
||||
color: var(--folx-magenta);
|
||||
letter-spacing: 0.2em;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.info-title {
|
||||
font-family: 'Archivo Black', sans-serif;
|
||||
font-size: 18px;
|
||||
letter-spacing: -0.01em;
|
||||
margin-bottom: 8px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.info-desc {
|
||||
color: var(--text-muted);
|
||||
font-size: 14.5px;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
border-top: 1px solid var(--border);
|
||||
padding: 48px 24px 32px;
|
||||
margin-top: 32px;
|
||||
}
|
||||
.footer-inner {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1.2fr 1.4fr 1.2fr;
|
||||
gap: 40px;
|
||||
}
|
||||
.foot-label {
|
||||
font-family: 'Archivo Black', sans-serif;
|
||||
font-size: 11px;
|
||||
color: var(--folx-magenta);
|
||||
letter-spacing: 0.22em;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.foot-title {
|
||||
font-family: 'Archivo Black', sans-serif;
|
||||
font-size: 20px;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.foot-text {
|
||||
color: var(--text-muted);
|
||||
font-size: 13.5px;
|
||||
line-height: 1.65;
|
||||
}
|
||||
.foot-text a {
|
||||
color: var(--text);
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid var(--text-faint);
|
||||
transition: border-color 0.15s;
|
||||
}
|
||||
.foot-text a:hover { border-color: var(--folx-magenta); }
|
||||
.foot-socials {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
}
|
||||
.foot-socials a {
|
||||
border-bottom: none;
|
||||
padding: 0;
|
||||
display: inline-block;
|
||||
width: fit-content;
|
||||
line-height: 1.65;
|
||||
}
|
||||
.foot-socials a:hover { color: var(--folx-magenta); }
|
||||
.foot-divider {
|
||||
display: block;
|
||||
width: 24px;
|
||||
height: 1px;
|
||||
background: var(--border);
|
||||
margin: 10px 0 6px;
|
||||
}
|
||||
.foot-copy {
|
||||
max-width: 1400px;
|
||||
margin: 32px auto 0;
|
||||
padding-top: 24px;
|
||||
border-top: 1px solid var(--border);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: var(--text-faint);
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
.foot-mark {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.foot-mark img {
|
||||
height: 18px;
|
||||
width: auto;
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 900px) {
|
||||
.features { grid-template-columns: repeat(2, 1fr); }
|
||||
.info-blocks { grid-template-columns: 1fr; }
|
||||
.footer-inner { grid-template-columns: 1fr; gap: 32px; }
|
||||
.foot-copy { flex-direction: column; gap: 12px; }
|
||||
}
|
||||
@media (max-width: 520px) {
|
||||
.features { grid-template-columns: 1fr; }
|
||||
.brand-tagline, .brand-sep { display: none; }
|
||||
.hero { padding: 16px; }
|
||||
section { padding: 32px 16px; }
|
||||
}
|
||||
.bauchbinde { position: absolute; left: 18px; right: 18px; bottom: 18px; z-index: 10; display: flex; align-items: stretch; }
|
||||
.bauchbinde.hidden { display:none; }
|
||||
.bb-tag { background: var(--akzent); color: #fff; padding: 12px 18px; font-weight: 800; font-size: clamp(13px,1.6vw,17px); letter-spacing: 0.04em; text-transform: uppercase; display: flex; align-items: center; white-space: nowrap; }
|
||||
.bb-body { flex: 1; min-width: 0; background: rgba(10,10,10,0.78); padding: 10px 16px 9px; }
|
||||
.bb-title { font-weight: 700; font-size: clamp(15px,2.4vw,24px); line-height: 1.05; text-transform: uppercase; letter-spacing: -0.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.bb-sub { margin-top: 5px; font-size: 10px; font-weight: 500; letter-spacing: 0.22em; text-transform: uppercase; color: #D0D0D0; }
|
||||
.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>
|
||||
|
||||
<nav class="topnav">
|
||||
<div class="topnav-inner">
|
||||
<a href="/" class="brand">
|
||||
<img src="/brand/folx-logo-transparent.png?v=2" alt="FOLX Music Television" class="brand-logo" />
|
||||
</a>
|
||||
<span class="live-pill">
|
||||
<span class="live-dot"></span>
|
||||
Jetzt live
|
||||
</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="hero">
|
||||
<div class="hero-header">
|
||||
<h1 class="hero-title">
|
||||
Volksmusik<br/>
|
||||
<span class="accent">Rund um die Uhr.</span>
|
||||
</h1>
|
||||
<div class="hero-meta">
|
||||
<span>24 / 7 Live</span>
|
||||
<span class="dot">•</span>
|
||||
<span>HD</span>
|
||||
<span class="dot">•</span>
|
||||
<span>DE · AT · CH</span>
|
||||
<span class="dot">•</span>
|
||||
<span>Worldwide</span>
|
||||
</div>
|
||||
<div class="wrap">
|
||||
<header class="top">
|
||||
<img class="logo" src="/brand/folx-logo-neg.png" alt="FOLX Music Television" />
|
||||
<div style="display:flex; align-items:center; gap:12px;">
|
||||
<div class="badge-live"><span class="d"></span> Auf Sendung</div>
|
||||
<div class="uhr" id="uhr"></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="stage-wrap">
|
||||
<div class="player-frame" id="playerFrame">
|
||||
<video id="v" autoplay muted playsinline tabindex="-1"
|
||||
disablepictureinpicture
|
||||
<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 nofullscreen noremoteplayback noplaybackrate"></video>
|
||||
|
||||
<div class="player-msg visible" id="msg">
|
||||
<div class="spinner"></div>
|
||||
<div>Stream wird geladen…</div>
|
||||
</div>
|
||||
<div class="bug-live"><span class="d"></span> Live</div>
|
||||
<div class="bug-right">folx.live</div>
|
||||
|
||||
<div class="player-overlay">
|
||||
<div class="player-controls">
|
||||
<button class="pctl" id="btnAudio" aria-label="Ton umschalten" title="Ton umschalten">🔇</button>
|
||||
<button class="pctl" id="btnFs" aria-label="Vollbild" title="Vollbild">⛶</button>
|
||||
<div class="msg" id="msg"><div class="spinner"></div><div>Stream wird geladen…</div></div>
|
||||
|
||||
<div class="bauchbinde hidden" id="bauchbinde">
|
||||
<div class="bb-tag">Folx</div>
|
||||
<div class="bb-body">
|
||||
<div class="bb-title" id="bbTitle">Folx Music Television</div>
|
||||
<div class="bb-sub" id="bbSub">Live · Folx Music Television</div>
|
||||
</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>
|
||||
</main>
|
||||
|
||||
<section>
|
||||
<div class="features">
|
||||
<div class="feature">
|
||||
<div class="feature-num">01</div>
|
||||
<div class="feature-title">Oberkrainer</div>
|
||||
<div class="feature-desc">Die Wurzeln der alpenländischen Volksmusik — Polka, Walzer und unvergessliche Klänge der Oberkrainer-Tradition.</div>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="feature-num">02</div>
|
||||
<div class="feature-title">Bayrisch</div>
|
||||
<div class="feature-desc">Bayerische und österreichische Volksmusik in ihrer ganzen Vielfalt — von der Blaskapelle bis zum Alpenrock.</div>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="feature-num">03</div>
|
||||
<div class="feature-title">Zillertaler</div>
|
||||
<div class="feature-desc">Pure alpine Lebensfreude aus dem Herzen der Tiroler Berge — Ländler, Polka und Tiroler Klassiker.</div>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="feature-num">04</div>
|
||||
<div class="feature-title">Schlager</div>
|
||||
<div class="feature-desc">Vom Klassiker bis zum Sommerhit — die größten Schlagerstars und neuen Talente aus DE, AT und CH.</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="section-label">Über FOLX</div>
|
||||
<h2 class="section-head">Was läuft bei FOLX</h2>
|
||||
<p class="section-intro">
|
||||
FOLX Music Television ist der private Fernsehsender für Volksmusik, Schlager und alpine Klänge
|
||||
in Deutschland, Österreich und der Schweiz. Wir senden das Beste aus der volkstümlichen
|
||||
Musikszene — von Andreas Gabalier bis Helene Fischer, von Andrea Berg bis zu den jungen
|
||||
Talenten der Alpen.
|
||||
</p>
|
||||
<p class="section-intro">
|
||||
Zwischen den Musikvideos bieten wir Tele-Shopping mit ausgewählten Produkten sowie Berichte
|
||||
aus der Volksmusik- und Schlagerszene. Kein festes Programm — einfach die Musik, die Sie
|
||||
lieben, jederzeit.
|
||||
</p>
|
||||
|
||||
<div class="info-blocks">
|
||||
<div class="info-block">
|
||||
<div class="info-num">A</div>
|
||||
<div class="info-title">Musik</div>
|
||||
<div class="info-desc">Das Beste aus Volksmusik & Schlagerszene — Oberkrainer, Bayrisch, Zillertaler und mehr.</div>
|
||||
</div>
|
||||
<div class="info-block">
|
||||
<div class="info-num">B</div>
|
||||
<div class="info-title">Tele-Shopping</div>
|
||||
<div class="info-desc">Premium Produkte aus dem Sortiment von Genius TV — Innovation, Qualität und das Beste für Ihren Alltag.</div>
|
||||
</div>
|
||||
<div class="info-block">
|
||||
<div class="info-num">C</div>
|
||||
<div class="info-title">Rund um die Uhr</div>
|
||||
<div class="info-desc">24 Stunden Live — weltweit empfangbar über folx.live.</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<div class="footer-inner">
|
||||
<div>
|
||||
<div class="foot-label">Sender</div>
|
||||
<div class="foot-title">FOLX Music Television</div>
|
||||
<div class="foot-text">
|
||||
Der private Fernsehsender für Musikvideos und Entertainment aus der Volksmusik-
|
||||
und Schlagerszene in Deutschland, Österreich und der Schweiz.
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="foot-label">Herausgeber</div>
|
||||
<div class="foot-text">
|
||||
BoldFrame Productions d.o.o.<br/>
|
||||
Sokolska ulica 46<br/>
|
||||
2000 Maribor · Slowenien
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="foot-label">Kontakt</div>
|
||||
<div class="foot-text">
|
||||
BoldFrame Productions d.o.o.<br/><br/>
|
||||
<a href="tel:+491725675800">+49 172 5675800</a><br/>
|
||||
<a href="mailto:office@folx.tv">office@folx.tv</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="foot-label">Folgt uns</div>
|
||||
<div class="foot-text foot-socials">
|
||||
<a href="https://www.facebook.com/folxtv/" target="_blank" rel="noopener">Facebook</a>
|
||||
<a href="https://www.instagram.com/folxtv/" target="_blank" rel="noopener">Instagram</a>
|
||||
<a href="https://www.youtube.com/@folxmtv" target="_blank" rel="noopener">YouTube</a>
|
||||
<a href="https://www.tiktok.com/@folx.tv" target="_blank" rel="noopener">TikTok</a>
|
||||
<span class="foot-divider"></span>
|
||||
<a href="https://folx.tv">folx.tv</a> — Nachrichten & Videos
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="foot-copy">
|
||||
<div class="foot-mark">
|
||||
<img src="/brand/folx-wordmark-transparent.png" alt="FOLX" />
|
||||
<span>© <%= new Date().getFullYear() %> FOLX Music Television</span>
|
||||
</div>
|
||||
<div>Jederzeit, überall — folx.live</div>
|
||||
</div>
|
||||
<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 () {
|
||||
const HLS_URL = <%- JSON.stringify(hlsUrl) %>;
|
||||
const video = document.getElementById('v');
|
||||
const msg = document.getElementById('msg');
|
||||
const playerFrame = document.getElementById('playerFrame');
|
||||
let hls = null;
|
||||
<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 showMsg(html) { msg.innerHTML = html; msg.classList.add('visible'); }
|
||||
function hideMsg() { msg.classList.remove('visible'); }
|
||||
|
||||
function attach() {
|
||||
try { hls && hls.destroy(); } catch (e) {}
|
||||
hls = null;
|
||||
|
||||
// PRIORITY 1: hls.js (works in Chrome, Firefox, Edge — most browsers)
|
||||
// We test isSupported() FIRST because some Chrome versions now report
|
||||
// canPlayType('application/vnd.apple.mpegurl') === 'maybe' but native
|
||||
// playback actually fails silently (black screen). hls.js works.
|
||||
if (window.Hls && window.Hls.isSupported()) {
|
||||
hls = new Hls({
|
||||
liveDurationInfinity: true,
|
||||
liveSyncDurationCount: 4,
|
||||
backBufferLength: 30,
|
||||
maxBufferLength: 60,
|
||||
manifestLoadingTimeOut: 10000,
|
||||
manifestLoadingMaxRetry: 6,
|
||||
fragLoadingTimeOut: 20000,
|
||||
fragLoadingMaxRetry: 6,
|
||||
// ABR — adaptive bitrate switching
|
||||
startLevel: -1,
|
||||
capLevelToPlayerSize: false,
|
||||
abrEwmaDefaultEstimate: 3000000,
|
||||
testBandwidth: true,
|
||||
abrBandWidthFactor: 0.95,
|
||||
abrBandWidthUpFactor: 0.7,
|
||||
});
|
||||
hls.loadSource(HLS_URL);
|
||||
hls.attachMedia(video);
|
||||
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||
hideMsg();
|
||||
video.play().catch(() => setTimeout(() => video.play().catch(() => {}), 200));
|
||||
});
|
||||
hls.on(Hls.Events.ERROR, (_e, data) => {
|
||||
if (!data.fatal) return;
|
||||
showMsg('<div class="spinner"></div><div>Verbindung wird wiederhergestellt…</div>');
|
||||
setTimeout(() => { attach(); }, 3000);
|
||||
});
|
||||
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
|
||||
// FALLBACK: iOS Safari native HLS (hls.js not supported on iOS)
|
||||
video.src = HLS_URL;
|
||||
video.play().catch(() => {});
|
||||
video.addEventListener('playing', hideMsg, { once: true });
|
||||
} else {
|
||||
showMsg('<div>Stream wird in diesem Browser nicht unterstützt</div>');
|
||||
}
|
||||
}
|
||||
|
||||
attach();
|
||||
|
||||
// Twitch-style: pause is disabled. If anything pauses the video, resume + seek to live edge.
|
||||
function seekToLive() {
|
||||
try {
|
||||
if (hls && hls.liveSyncPosition) {
|
||||
video.currentTime = hls.liveSyncPosition;
|
||||
} else if (video.seekable && video.seekable.length > 0) {
|
||||
video.currentTime = video.seekable.end(video.seekable.length - 1);
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
video.addEventListener('pause', () => {
|
||||
setTimeout(() => {
|
||||
seekToLive();
|
||||
video.play().catch(() => {});
|
||||
}, 80);
|
||||
});
|
||||
video.addEventListener('seeked', () => {
|
||||
if (!hls) return;
|
||||
if (hls.liveSyncPosition && video.currentTime < hls.liveSyncPosition - 10) {
|
||||
video.currentTime = hls.liveSyncPosition;
|
||||
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); }
|
||||
}
|
||||
});
|
||||
video.addEventListener('contextmenu', (e) => e.preventDefault());
|
||||
video.disablePictureInPicture = true;
|
||||
video.setAttribute('disablePictureInPicture', 'true');
|
||||
try { video.disableRemotePlayback = true; } catch (e) {}
|
||||
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
|
||||
video.src = HLS_URL;
|
||||
video.addEventListener('playing', hideMsg, { once:true });
|
||||
video.play().catch(function(){});
|
||||
}
|
||||
}
|
||||
start();
|
||||
|
||||
// Block keyboard shortcuts that pause
|
||||
document.addEventListener('keydown', (e) => {
|
||||
const tag = (e.target && e.target.tagName) || '';
|
||||
if (tag === 'INPUT' || tag === 'TEXTAREA') return;
|
||||
if (e.key === ' ' || e.key === 'Spacebar' || e.code === 'Space' ||
|
||||
e.key === 'k' || e.key === 'K' ||
|
||||
e.key === 'MediaPlayPause' || e.key === 'MediaPause') {
|
||||
e.preventDefault();
|
||||
if (video.paused) {
|
||||
seekToLive();
|
||||
video.play().catch(() => {});
|
||||
}
|
||||
}
|
||||
});
|
||||
// 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(); });
|
||||
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (!document.hidden) {
|
||||
seekToLive();
|
||||
video.play().catch(() => {});
|
||||
}
|
||||
});
|
||||
// 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 (custom button)
|
||||
const btnAudio = document.getElementById('btnAudio');
|
||||
const btnFs = document.getElementById('btnFs');
|
||||
function updateAudio() {
|
||||
btnAudio.textContent = video.muted ? '🔇' : '🔊';
|
||||
btnAudio.title = video.muted ? 'Ton einschalten' : 'Stummschalten';
|
||||
}
|
||||
updateAudio();
|
||||
btnAudio.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
video.muted = !video.muted;
|
||||
video.volume = 1.0;
|
||||
updateAudio();
|
||||
});
|
||||
// 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.exitFullscreen(); } else { (f.requestFullscreen||f.webkitRequestFullscreen).call(f); } });
|
||||
|
||||
// Fullscreen — handle iOS Safari separately
|
||||
function enterFullscreen() {
|
||||
if (typeof video.webkitEnterFullscreen === 'function' && !document.fullscreenEnabled) {
|
||||
try { video.webkitEnterFullscreen(); return; } catch (e) {}
|
||||
}
|
||||
if (video.requestFullscreen) { video.requestFullscreen().catch(() => {}); return; }
|
||||
if (video.webkitRequestFullscreen) { video.webkitRequestFullscreen(); return; }
|
||||
if (playerFrame.requestFullscreen) { playerFrame.requestFullscreen().catch(() => {}); return; }
|
||||
if (playerFrame.webkitRequestFullscreen) { playerFrame.webkitRequestFullscreen(); return; }
|
||||
if (typeof video.webkitEnterFullscreen === 'function') {
|
||||
try { video.webkitEnterFullscreen(); } catch (e) {}
|
||||
}
|
||||
}
|
||||
function exitFullscreen() {
|
||||
if (document.exitFullscreen) { document.exitFullscreen().catch(() => {}); return; }
|
||||
if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); return; }
|
||||
if (typeof video.webkitExitFullscreen === 'function') { try { video.webkitExitFullscreen(); } catch (e) {} }
|
||||
}
|
||||
function isFullscreen() {
|
||||
return !!(document.fullscreenElement || document.webkitFullscreenElement || video.webkitDisplayingFullscreen);
|
||||
}
|
||||
btnFs.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
if (isFullscreen()) exitFullscreen();
|
||||
else enterFullscreen();
|
||||
});
|
||||
video.addEventListener('click', () => { video.muted = !video.muted; video.volume = 1.0; updateAudio(); });
|
||||
video.addEventListener('dblclick', (e) => { e.preventDefault(); btnFs.click(); });
|
||||
})();
|
||||
</script>
|
||||
// Now-Playing poll
|
||||
var bb = document.getElementById('bauchbinde'), bbT = document.getElementById('bbTitle'), bbS = document.getElementById('bbSub'), nowT = document.getElementById('nowTitle');
|
||||
var lastSong = null;
|
||||
function applyNP(np){
|
||||
var label = null, sub = 'Live · Folx Music Television';
|
||||
if (np.kind === 'song') { label = np.label; lastSong = np.label; sub = 'Musikvideo · Folx'; }
|
||||
else if (np.kind === 'genius') { label = 'GENIUS'; sub = 'Teleshopping · Folx'; }
|
||||
else if (np.kind === 'werbung') { label = 'Werbung'; sub = 'Werbung · Folx'; }
|
||||
else { label = lastSong; sub = 'Live · Folx Music Television'; } // skip -> keep last song
|
||||
if (label) { bbT.textContent = label; bbS.textContent = sub; nowT.textContent = label; bb.classList.remove('hidden'); }
|
||||
else { bb.classList.add('hidden'); nowT.textContent = '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>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user