Initial: FOLX live landing page
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
node_modules/
|
||||
*.log
|
||||
.DS_Store
|
||||
.env
|
||||
*.swp
|
||||
25
Dockerfile
Normal file
@ -0,0 +1,25 @@
|
||||
FROM node:20-alpine
|
||||
|
||||
RUN apk add --no-cache curl
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install deps first (cache layer)
|
||||
COPY package.json ./
|
||||
RUN npm install --omit=dev && npm cache clean --force
|
||||
|
||||
# Copy sources
|
||||
COPY src ./src
|
||||
COPY views ./views
|
||||
COPY public ./public
|
||||
|
||||
# Non-root user
|
||||
RUN addgroup -g 1001 app && adduser -D -u 1001 -G app app && chown -R app:app /app
|
||||
USER app
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
|
||||
CMD curl -fsS http://127.0.0.1:3000/api/health || exit 1
|
||||
|
||||
CMD ["node", "src/server.js"]
|
||||
17
package.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "folx-live",
|
||||
"version": "1.0.0",
|
||||
"description": "FOLX Music Television — live stream landing page (folx.live)",
|
||||
"main": "src/server.js",
|
||||
"scripts": {
|
||||
"start": "node src/server.js",
|
||||
"dev": "node --watch src/server.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"dependencies": {
|
||||
"ejs": "^3.1.10",
|
||||
"express": "^4.21.0"
|
||||
}
|
||||
}
|
||||
BIN
public/brand/folx-full-neg.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
90
public/brand/folx-full-neg.svg
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
public/brand/folx-full-poz.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
88
public/brand/folx-full-poz.svg
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
public/brand/folx-logo-neg.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
public/brand/folx-logo-poz.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
public/brand/folx-mark-only.pdf
Normal file
BIN
public/brand/folx-mark.pdf
Normal file
BIN
public/brand/folx-with-tagline-neg.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
96
public/brand/folx-with-tagline-neg.svg
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
public/brand/folx-with-tagline-poz.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
94
public/brand/folx-with-tagline-poz.svg
Normal file
|
After Width: | Height: | Size: 14 KiB |
28
src/server.js
Normal file
@ -0,0 +1,28 @@
|
||||
const path = require('path');
|
||||
const express = require('express');
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
||||
const STREAM_URL = process.env.HLS_URL || 'https://folxplay.b-cdn.net/live/stream1_master.m3u8';
|
||||
|
||||
app.set('view engine', 'ejs');
|
||||
app.set('views', path.join(__dirname, '..', 'views'));
|
||||
|
||||
app.use(express.static(path.join(__dirname, '..', 'public'), {
|
||||
maxAge: '1h',
|
||||
etag: true,
|
||||
}));
|
||||
|
||||
app.get('/', (_req, res) => {
|
||||
res.render('index', { hlsUrl: STREAM_URL });
|
||||
});
|
||||
|
||||
app.get('/api/health', (_req, res) => {
|
||||
res.json({ ok: true, hls: STREAM_URL, ts: Date.now() });
|
||||
});
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`[folx-live] listening on :${PORT}`);
|
||||
console.log(`[folx-live] HLS source: ${STREAM_URL}`);
|
||||
});
|
||||
712
views/index.ejs
Normal file
@ -0,0 +1,712 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de" class="dark">
|
||||
<head>
|
||||
<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" />
|
||||
<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: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/png" href="/brand/folx-full-poz.png" />
|
||||
<link rel="apple-touch-icon" href="/brand/folx-full-poz.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" />
|
||||
|
||||
<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.88);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.topnav-inner {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 14px 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
.brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
.brand-logo {
|
||||
height: 34px;
|
||||
width: auto;
|
||||
display: block;
|
||||
}
|
||||
.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: 24px;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.hero-header {
|
||||
margin: 24px 0 18px;
|
||||
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(32px, 5vw, 64px);
|
||||
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 — live badge + controls */
|
||||
.player-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 3;
|
||||
background: linear-gradient(180deg, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0) 18%, rgba(0,0,0,0) 70%, rgba(0,0,0,0.65) 100%);
|
||||
opacity: 0;
|
||||
transition: opacity 0.25s;
|
||||
}
|
||||
.player-frame:hover .player-overlay,
|
||||
.player-frame:focus-within .player-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
.player-top {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
left: 14px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
.onair {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
background: var(--folx-magenta);
|
||||
color: white;
|
||||
padding: 5px 10px 5px 8px;
|
||||
font-family: 'Archivo Black', sans-serif;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.18em;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.onair::before {
|
||||
content: '';
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: white;
|
||||
animation: livePulse 1.6s infinite;
|
||||
}
|
||||
.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);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
@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;
|
||||
}
|
||||
|
||||
.programm-list {
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
.programm-item {
|
||||
display: grid;
|
||||
grid-template-columns: 200px 1fr 180px;
|
||||
gap: 32px;
|
||||
padding: 24px 0;
|
||||
border-bottom: 1px solid var(--border);
|
||||
align-items: start;
|
||||
}
|
||||
.programm-name {
|
||||
font-family: 'Archivo Black', sans-serif;
|
||||
font-size: 18px;
|
||||
letter-spacing: -0.01em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.programm-desc {
|
||||
color: var(--text-muted);
|
||||
font-size: 14.5px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.programm-desc strong {
|
||||
color: var(--text);
|
||||
font-weight: 700;
|
||||
}
|
||||
.programm-time {
|
||||
font-family: 'Archivo Black', sans-serif;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--folx-magenta);
|
||||
text-transform: uppercase;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* 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 1fr 1fr;
|
||||
gap: 48px;
|
||||
}
|
||||
.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-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: 20px;
|
||||
width: auto;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 900px) {
|
||||
.features { grid-template-columns: repeat(2, 1fr); }
|
||||
.programm-item { grid-template-columns: 1fr; gap: 6px; }
|
||||
.programm-time { text-align: left; }
|
||||
.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; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="topnav">
|
||||
<div class="topnav-inner">
|
||||
<a href="/" class="brand">
|
||||
<img src="/brand/folx-with-tagline-neg.png" 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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="player-frame" id="playerFrame">
|
||||
<video id="v" autoplay muted playsinline></video>
|
||||
|
||||
<div class="player-msg visible" id="msg">
|
||||
<div class="spinner"></div>
|
||||
<div>Stream wird geladen…</div>
|
||||
</div>
|
||||
|
||||
<div class="player-overlay">
|
||||
<div class="player-top">
|
||||
<span class="onair">On Air</span>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<section>
|
||||
<div class="features">
|
||||
<div class="feature">
|
||||
<div class="feature-num">01</div>
|
||||
<div class="feature-title">24 / 7 Live</div>
|
||||
<div class="feature-desc">Rund um die Uhr das Beste aus der Volksmusik- und Schlagerszene — ohne Unterbrechung.</div>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="feature-num">02</div>
|
||||
<div class="feature-title">Oberkrainer</div>
|
||||
<div class="feature-desc">Die Legenden der Oberkrainer Musik, traditionell und modern interpretiert.</div>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="feature-num">03</div>
|
||||
<div class="feature-title">Bayrisch</div>
|
||||
<div class="feature-desc">Von klassischer Blasmusik bis zeitgenössischem Schlager aus Bayern und Tirol.</div>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="feature-num">04</div>
|
||||
<div class="feature-title">Zillertaler</div>
|
||||
<div class="feature-desc">Pure alpine Lebensfreude aus dem Herzen der Zillertaler Berge.</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="section-label">Programm</div>
|
||||
<h2 class="section-head">Das läuft bei FOLX</h2>
|
||||
<p class="section-intro">
|
||||
Ob junge Menschen, Mid Ager oder Best Ager — für jeden ist etwas dabei. Die größten Stars,
|
||||
die modernsten Videos und die beste volkstümliche Musik, die das Herz jedes Fans höher schlagen lässt.
|
||||
</p>
|
||||
|
||||
<div class="programm-list">
|
||||
<div class="programm-item">
|
||||
<div class="programm-name">Morgenstund hat Gold im Mund</div>
|
||||
<div class="programm-desc">Folx TV macht das Aufstehen zum Genuss. Starten Sie Ihren Tag mit der besten Musik und den neuesten Informationen.</div>
|
||||
<div class="programm-time">Täglich ab 06:00</div>
|
||||
</div>
|
||||
<div class="programm-item">
|
||||
<div class="programm-name">Folx Interaktiv</div>
|
||||
<div class="programm-desc">Unsere Moderatoren und Zuschauer werden von den Gästen aus der Volks- und Schlagermusikszene begleitet. Täglich aktuelle News, Sport, Klatsch und Tratsch.</div>
|
||||
<div class="programm-time">Di & Do ab 14:00</div>
|
||||
</div>
|
||||
<div class="programm-item">
|
||||
<div class="programm-name">Folx Box</div>
|
||||
<div class="programm-desc">Eine unwiderstehliche Mischung. Sie kriegen nie genug von Ihren beliebtesten volkstümlichen Musikvideos — hier entscheiden SIE, was gespielt wird.</div>
|
||||
<div class="programm-time">Di & Do ab 20:00</div>
|
||||
</div>
|
||||
<div class="programm-item">
|
||||
<div class="programm-name">Folx Live</div>
|
||||
<div class="programm-desc"><strong>100 % Live.</strong> Gruppen, die sich noch wagen live zu spielen. Ein echter Ohrenschmaus.</div>
|
||||
<div class="programm-time">Freitag ab 20:15</div>
|
||||
</div>
|
||||
<div class="programm-item">
|
||||
<div class="programm-name">Folx Stadl</div>
|
||||
<div class="programm-desc">Legenden der Volksmusik- und Schlagerszene präsentieren sich im einzigartigen Hüttenambiente.</div>
|
||||
<div class="programm-time">Samstag ab 20:15</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 erste private Fernsehsender für Musikvideos und Entertainment aus der Volksmusik-
|
||||
und Schlagerszene in Deutschland, Österreich und der Schweiz — <strong style="color:var(--text);">24 Stunden, 7 Tage die Woche.</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="foot-label">Kontakt</div>
|
||||
<div class="foot-text">
|
||||
PRIME TIME Consulting GmbH<br/>
|
||||
Sigmaringer Str. 10<br/>
|
||||
72379 Hechingen · Deutschland<br/><br/>
|
||||
<a href="mailto:office@folx.network">office@folx.network</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="foot-label">Mehr</div>
|
||||
<div class="foot-text">
|
||||
<a href="https://folx.tv">folx.tv</a> — Nachrichten & Videos<br/>
|
||||
<a href="https://folx.network">folx.network</a> — Netzwerk<br/>
|
||||
<a href="https://www.youtube.com/@folxmtv">YouTube</a><br/>
|
||||
<a href="https://www.facebook.com/folxmtv">Facebook</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="foot-copy">
|
||||
<div class="foot-mark">
|
||||
<img src="/brand/folx-full-poz.png" alt="" />
|
||||
<span>© <%= new Date().getFullYear() %> FOLX Music Television</span>
|
||||
</div>
|
||||
<div>Jederzeit, überall — folx.live</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<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 btnAudio = document.getElementById('btnAudio');
|
||||
const btnFs = document.getElementById('btnFs');
|
||||
const playerFrame = document.getElementById('playerFrame');
|
||||
|
||||
let hls = null;
|
||||
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;
|
||||
|
||||
if (video.canPlayType('application/vnd.apple.mpegurl')) {
|
||||
video.src = HLS_URL;
|
||||
video.play().catch(() => {});
|
||||
video.addEventListener('playing', hideMsg, { once: true });
|
||||
return;
|
||||
}
|
||||
|
||||
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,
|
||||
startLevel: -1,
|
||||
capLevelToPlayerSize: false,
|
||||
abrEwmaDefaultEstimate: 50000000,
|
||||
testBandwidth: false,
|
||||
});
|
||||
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 {
|
||||
showMsg('<div>HLS wird von diesem Browser nicht unterstützt.</div>');
|
||||
}
|
||||
}
|
||||
attach();
|
||||
|
||||
// Stall detection
|
||||
let lastT = 0, stalls = 0;
|
||||
setInterval(() => {
|
||||
if (video.paused) { video.play().catch(() => {}); return; }
|
||||
if (video.currentTime === lastT && !video.seeking) {
|
||||
stalls++;
|
||||
if (stalls >= 3) {
|
||||
stalls = 0;
|
||||
showMsg('<div class="spinner"></div><div>Verbindung wird wiederhergestellt…</div>');
|
||||
setTimeout(() => { attach(); }, 500);
|
||||
}
|
||||
} else stalls = 0;
|
||||
lastT = video.currentTime;
|
||||
}, 5000);
|
||||
|
||||
video.addEventListener('pause', () => setTimeout(() => video.play().catch(() => {}), 100));
|
||||
document.addEventListener('visibilitychange', () => { if (!document.hidden) video.play().catch(() => {}); });
|
||||
|
||||
// Audio toggle
|
||||
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();
|
||||
});
|
||||
|
||||
// Fullscreen
|
||||
btnFs.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
if (!document.fullscreenElement) {
|
||||
(playerFrame.requestFullscreen || playerFrame.webkitRequestFullscreen).call(playerFrame);
|
||||
} else {
|
||||
(document.exitFullscreen || document.webkitExitFullscreen).call(document);
|
||||
}
|
||||
});
|
||||
video.addEventListener('click', () => { video.muted = !video.muted; video.volume = 1.0; updateAudio(); });
|
||||
video.addEventListener('dblclick', (e) => { e.preventDefault(); btnFs.click(); });
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||