Initial: folxplay.tv clone with JW promos, no AdSense
6
.dockerignore
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
README.md
|
||||||
|
node_modules
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
node_modules/
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
.env
|
||||||
13
Dockerfile
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
FROM nginx:1.27-alpine
|
||||||
|
|
||||||
|
# Replace default nginx config
|
||||||
|
RUN rm /etc/nginx/conf.d/default.conf
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
# Copy site
|
||||||
|
COPY public/ /usr/share/nginx/html/
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
|
||||||
|
CMD wget -qO- http://127.0.0.1/health || exit 1
|
||||||
69
README.md
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# folxplay-clone
|
||||||
|
|
||||||
|
Clean rebuild of folxplay.tv — same look as the original, but **without
|
||||||
|
Google AdSense, Google Analytics, Funding Choices, or any third-party
|
||||||
|
tracking**. Channel videos play promo clips served from JW Player Cloud.
|
||||||
|
|
||||||
|
- **Live:** https://folxplay.biba.live
|
||||||
|
- **Original:** https://folxplay.tv
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
|
||||||
|
- Vanilla JS SPA (no build step, no framework)
|
||||||
|
- Shaka Player 4.x for HLS playback (loaded from jsDelivr)
|
||||||
|
- nginx static hosting (Docker)
|
||||||
|
- JW Player Cloud (site `KR6L8XQG`) for the channel promos
|
||||||
|
|
||||||
|
## Channels and JW media IDs
|
||||||
|
|
||||||
|
| # | Channel | JW media ID | Duration |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | One Music TV | `CBM2fSnV` | 71 s |
|
||||||
|
| 2 | Zwei Music TV | `LZVUn3Qy` | 76 s |
|
||||||
|
| 3 | Folx Music TV | `UYU6dx3Q` | 60 s |
|
||||||
|
| 4 | Adria Music TV | `lI4SC7Do` | 53 s |
|
||||||
|
| 5 | Folx Slovenija | `aoCWwwSg` | — |
|
||||||
|
| 6 | One Adria Music TV | `IGOSgmvW` | 71 s |
|
||||||
|
|
||||||
|
To swap a promo for a live stream later, edit
|
||||||
|
`public/app.js → CHANNELS[].jw` (or replace the `JW_HLS()` helper with
|
||||||
|
the production stream URL).
|
||||||
|
|
||||||
|
## Local dev
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd public
|
||||||
|
python3 -m http.server 8080
|
||||||
|
# open http://localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build & run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t folxplay-clone .
|
||||||
|
docker run -p 8080:80 folxplay-clone
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
Coolify reads this repo, builds the Docker image, and serves it on
|
||||||
|
`folxplay.biba.live` via the wildcard `*.biba.live` (already pointed at
|
||||||
|
openclaw1).
|
||||||
|
|
||||||
|
## What was removed compared to the original
|
||||||
|
|
||||||
|
- `<script src="googletagmanager.com/gtag/js?id=G-1HF0NT0FBR">` — GA4 tag
|
||||||
|
- `<script src="pagead2.googlesyndication.com/.../adsbygoogle.js">` — AdSense
|
||||||
|
- The `<ins class="adsbygoogle">` ad slot (slot id `8928661853`)
|
||||||
|
- Funding Choices / TCF consent dialog
|
||||||
|
- Cloudflare Insights beacon
|
||||||
|
- The `Top 10 this week` widget (depended on `console.folxplay.tv`)
|
||||||
|
- The Artists list (depended on `console.folxplay.tv`)
|
||||||
|
- The Channel PlayLog (depended on `console.folxplay.tv`)
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The site has `Channels` widget instead of `Top 10 this week` — it lists
|
||||||
|
the six channels and clicking any of them plays its promo.
|
||||||
|
- The Channel PlayLog page shows a small note pointing back to the
|
||||||
|
production site.
|
||||||
52
nginx.conf
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
server {
|
||||||
|
listen 80 default_server;
|
||||||
|
listen [::]:80 default_server;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# Gzip
|
||||||
|
gzip on;
|
||||||
|
gzip_types text/plain text/css application/javascript application/json image/svg+xml;
|
||||||
|
gzip_min_length 256;
|
||||||
|
|
||||||
|
# Long cache for static assets
|
||||||
|
location ~* \.(?:png|jpg|jpeg|gif|ico|webp|svg|woff2?|ttf|eot|otf)$ {
|
||||||
|
expires 30d;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~* \.(?:js|css)$ {
|
||||||
|
expires 7d;
|
||||||
|
add_header Cache-Control "public";
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Don't cache HTML / app shell
|
||||||
|
location = / {
|
||||||
|
add_header Cache-Control "no-store" always;
|
||||||
|
try_files /index.html =404;
|
||||||
|
}
|
||||||
|
location = /index.html {
|
||||||
|
add_header Cache-Control "no-store" always;
|
||||||
|
}
|
||||||
|
|
||||||
|
# SPA fallback: any unknown path -> index.html
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
|
|
||||||
|
# Healthcheck
|
||||||
|
location = /health {
|
||||||
|
access_log off;
|
||||||
|
return 200 "ok\n";
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
}
|
||||||
|
}
|
||||||
348
public/app.js
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
// FolxPlay clone — vanilla JS SPA
|
||||||
|
// Each channel plays its JW Player promo (HLS from cdn.jwplayer.com).
|
||||||
|
// No tracking, no AdSense, no external API.
|
||||||
|
|
||||||
|
const JW_HLS = id => `https://cdn.jwplayer.com/manifests/${id}.m3u8`;
|
||||||
|
const JW_POSTER = id => `https://cdn.jwplayer.com/v2/media/${id}/poster.jpg?width=1280`;
|
||||||
|
|
||||||
|
const CHANNELS = [
|
||||||
|
{ key: 'one', name: 'One Music TV', color: '#a84d7e', logo: '/logos/one_mt_neg_b.png', alt: 'logo of One', jw: 'CBM2fSnV' },
|
||||||
|
{ key: 'zwei', name: 'Zwei Music TV', color: '#f36700', logo: '/logos/zwei_neg_b.png', alt: 'logo of Zwei', jw: 'LZVUn3Qy' },
|
||||||
|
{ key: 'mt', name: 'Folx Music TV', color: '#dc1d1d', logo: '/logos/mt_neg_b.png', alt: 'logo of Folx Music',jw: 'UYU6dx3Q' },
|
||||||
|
{ key: 'adria', name: 'Adria Music TV', color: '#30a6d1', logo: '/logos/adria_neg_b.png', alt: 'logo of Adria', jw: 'lI4SC7Do' },
|
||||||
|
{ key: 'folx-slo', name: 'Folx Slovenija', color: '#a8b539', logo: '/logos/folx_slo_neg_b.png', alt: 'logo of Folx Slovenija', jw: 'aoCWwwSg' },
|
||||||
|
{ key: 'one-adria', name: 'One Adria Music TV', color: '#a84d7e', logo: '/logos/one_neg_b.png', alt: 'logo of One Adria', jw: 'IGOSgmvW' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const ABOUT = [
|
||||||
|
{ title: 'FOLX MUSIC TELEVISION', text: 'the whole area of folk music is covered with exclusive content. Over 90% of the content is created by our own FOLX NETWORK production.' },
|
||||||
|
{ title: 'ONE MUSIC TELEVISION', text: 'covers the area of the new German-speaking and world-famous pop and rock sound production and is additionally enriched with old German and world-famous classics of the 80s, 90s and 00s.' },
|
||||||
|
{ title: 'ZWEI MUSIC TELEVISION', text: 'covers the area of current pop music as well as old, well-known hits. Daily playlists are enriched with the current pop hits, Latin American music with a touch of reggaeton provides daily variety.' },
|
||||||
|
{ title: 'ADRIA MUSIC TELEVISION', text: 'is aimed at residents of Germany, Austria and Switzerland with Slovenian, Croatian, Bosnian, Herzegovinian, Serbian, Macedonian or Montenegrin roots. The traditional Dalmatian music, Klape, the most famous pop and rock hits are able to inspire everyone from the Adriatic region.' },
|
||||||
|
{ title: 'FOLX SLOVENIJA MUSIC TELEVISION',text: 'covers the area of European folk music with exclusive content, 90% of the broadcast content was created or is created in our production FOLX NETWORK.' },
|
||||||
|
{ title: 'ONE ADRIA MUSIC TELEVISION', text: 'covers the area of new fresh music from the Adriatic (SLO, CRO, BIH, SRB, MKD, MG), world pop and rock production and adds world hits of the 80s, 90s and 00s.' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const PLAY_SVG = `<svg aria-hidden="true" viewBox="0 0 448 512"><path fill="currentColor" d="M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z"/></svg>`;
|
||||||
|
|
||||||
|
// pick a random background on each load
|
||||||
|
const bgs = ['bg_01','bg_02','bg_03','bg_04','bg_05','bg_06','bg_07'];
|
||||||
|
const BG_URL = `/backgrounds/${bgs[Math.floor(Math.random() * bgs.length)]}.jpg`;
|
||||||
|
|
||||||
|
// ---------- helpers ----------
|
||||||
|
function el(html) {
|
||||||
|
const t = document.createElement('template');
|
||||||
|
t.innerHTML = html.trim();
|
||||||
|
return t.content.firstElementChild;
|
||||||
|
}
|
||||||
|
function escapeHtml(s) {
|
||||||
|
return String(s ?? '').replace(/[&<>"']/g, c => ({
|
||||||
|
'&':'&','<':'<','>':'>','"':'"',"'":'''
|
||||||
|
}[c]));
|
||||||
|
}
|
||||||
|
async function api() { return null; /* no backend */ }
|
||||||
|
|
||||||
|
// ---------- shell ----------
|
||||||
|
function renderShell() {
|
||||||
|
document.getElementById('root').innerHTML = `
|
||||||
|
<div class="mainBackground" style="background-image: url('${BG_URL}');">
|
||||||
|
<div class="nav">
|
||||||
|
<div class="nav__width">
|
||||||
|
<div class="logo">
|
||||||
|
<a href="/" data-link><img alt="Folx.network logo" src="/logos/folxnetwork.png"></a>
|
||||||
|
</div>
|
||||||
|
<div class="links">
|
||||||
|
<a href="/" data-link data-route="/">Home</a>
|
||||||
|
<a href="/artists" data-link data-route="/artists">Artists</a>
|
||||||
|
<a href="/folxnetwork" data-link data-route="/folxnetwork">About</a>
|
||||||
|
<a href="/policies" data-link data-route="/policies">Terms of service</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<main id="main"></main>
|
||||||
|
<footer class="siteFooter">© FOLX NETWORK · folxplay.biba.live</footer>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
// intercept <a data-link> for client-side navigation
|
||||||
|
document.body.addEventListener('click', (e) => {
|
||||||
|
const a = e.target.closest('a[data-link]');
|
||||||
|
if (!a) return;
|
||||||
|
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button !== 0) return;
|
||||||
|
e.preventDefault();
|
||||||
|
navigate(a.getAttribute('href'));
|
||||||
|
});
|
||||||
|
window.addEventListener('popstate', () => render(location.pathname));
|
||||||
|
}
|
||||||
|
|
||||||
|
function navigate(path) {
|
||||||
|
if (location.pathname !== path) history.pushState({}, '', path);
|
||||||
|
render(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- pages ----------
|
||||||
|
const pages = {
|
||||||
|
'/': renderHome,
|
||||||
|
'/artists': renderArtists,
|
||||||
|
'/folxnetwork': renderAbout,
|
||||||
|
'/policies': renderPolicies,
|
||||||
|
'/log': renderLog,
|
||||||
|
};
|
||||||
|
|
||||||
|
function render(path) {
|
||||||
|
const handler = pages[path] || renderNotFound;
|
||||||
|
// mark active link
|
||||||
|
document.querySelectorAll('.nav .links a').forEach(a => {
|
||||||
|
a.classList.toggle('active', a.getAttribute('data-route') === path);
|
||||||
|
});
|
||||||
|
handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== HOME =====
|
||||||
|
let shakaInstance = null;
|
||||||
|
let currentChannel = null;
|
||||||
|
|
||||||
|
function renderHome() {
|
||||||
|
const main = document.getElementById('main');
|
||||||
|
main.innerHTML = `
|
||||||
|
<div class="pageHome">
|
||||||
|
<div class="split">
|
||||||
|
<div class="split__video">
|
||||||
|
<div id="player-area"></div>
|
||||||
|
<div id="currently-playing" class="currentlyPlaying" style="display:none;"></div>
|
||||||
|
<div class="playingStats">
|
||||||
|
<div class="playingStats__title">Channels</div>
|
||||||
|
<ol id="channel-list">
|
||||||
|
${CHANNELS.map(c => `
|
||||||
|
<li><a href="#" data-channel-link="${c.key}" style="color:#eee;border:none;">
|
||||||
|
${escapeHtml(c.name)}
|
||||||
|
</a></li>
|
||||||
|
`).join('')}
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="split__channels">
|
||||||
|
<div class="channelList">
|
||||||
|
${CHANNELS.map(c => `
|
||||||
|
<a class="channelList__channel" href="#" data-channel="${c.key}">
|
||||||
|
<div class="channelList__channelIcon" style="--var-bgColor: ${c.color};">
|
||||||
|
${PLAY_SVG}
|
||||||
|
</div>
|
||||||
|
<div class="channelList__channelLogo">
|
||||||
|
<img src="${c.logo}" alt="${escapeHtml(c.alt)}">
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
`).join('')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
showSelectChannelBox();
|
||||||
|
|
||||||
|
// channel click handlers (sidebar + bottom list)
|
||||||
|
main.querySelectorAll('[data-channel], [data-channel-link]').forEach(a => {
|
||||||
|
a.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const key = a.getAttribute('data-channel') || a.getAttribute('data-channel-link');
|
||||||
|
playChannel(key);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSelectChannelBox() {
|
||||||
|
document.getElementById('player-area').innerHTML = `
|
||||||
|
<div class="selectChannelBox">
|
||||||
|
<div>
|
||||||
|
<div class="lineHeader">FolxPlay Live TV</div>
|
||||||
|
<div class="lineSelectChannel">Select channel on right</div>
|
||||||
|
<div class="lineRandom" id="random-btn">Or ${PLAY_SVG} random channel</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.getElementById('random-btn')?.addEventListener('click', () => {
|
||||||
|
const ch = CHANNELS[Math.floor(Math.random() * CHANNELS.length)];
|
||||||
|
playChannel(ch.key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showVideoPlayer(ch) {
|
||||||
|
const poster = ch ? ` poster="${JW_POSTER(ch.jw)}"` : '';
|
||||||
|
document.getElementById('player-area').innerHTML = `
|
||||||
|
<div class="split__video_ratio">
|
||||||
|
<video id="video" controls autoplay playsinline${poster}></video>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function playChannel(key) {
|
||||||
|
const ch = CHANNELS.find(c => c.key === key);
|
||||||
|
if (!ch) return;
|
||||||
|
currentChannel = ch;
|
||||||
|
|
||||||
|
// mark active in sidebar
|
||||||
|
document.querySelectorAll('.channelList__channelIcon').forEach(el => {
|
||||||
|
el.classList.remove('channelList__channelIconActive');
|
||||||
|
});
|
||||||
|
const link = document.querySelector(`.channelList__channel[data-channel="${key}"] .channelList__channelIcon`);
|
||||||
|
if (link) link.classList.add('channelList__channelIconActive');
|
||||||
|
|
||||||
|
showVideoPlayer(ch);
|
||||||
|
|
||||||
|
// Try Shaka first (HLS); fall back to native <video src=mp4-fallback>
|
||||||
|
await waitForShaka();
|
||||||
|
const video = document.getElementById('video');
|
||||||
|
const hlsUrl = JW_HLS(ch.jw);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (shakaInstance) {
|
||||||
|
try { await shakaInstance.destroy(); } catch (_) {}
|
||||||
|
shakaInstance = null;
|
||||||
|
}
|
||||||
|
if (window.shaka && shaka.Player.isBrowserSupported()) {
|
||||||
|
shakaInstance = new shaka.Player(video);
|
||||||
|
await shakaInstance.load(hlsUrl);
|
||||||
|
} else {
|
||||||
|
// Safari / native HLS
|
||||||
|
video.src = hlsUrl;
|
||||||
|
}
|
||||||
|
video.play().catch(() => { /* autoplay may be blocked, user can press play */ });
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('HLS load failed, falling back to native src:', e?.message);
|
||||||
|
video.src = hlsUrl;
|
||||||
|
video.play().catch(() => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show now playing
|
||||||
|
const now = document.getElementById('currently-playing');
|
||||||
|
if (now) {
|
||||||
|
now.style.display = '';
|
||||||
|
now.innerHTML = `Now playing: <b>${escapeHtml(ch.name)}</b> — promo`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForShaka() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
if (window.shaka) return resolve();
|
||||||
|
const i = setInterval(() => {
|
||||||
|
if (window.shaka) { clearInterval(i); resolve(); }
|
||||||
|
}, 50);
|
||||||
|
setTimeout(() => { clearInterval(i); resolve(); }, 4000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadCurrentlyPlaying() { /* no-op (no backend) */ }
|
||||||
|
async function loadTop10() { /* no-op (no backend) */ }
|
||||||
|
|
||||||
|
// ===== ARTISTS =====
|
||||||
|
function renderArtists() {
|
||||||
|
const main = document.getElementById('main');
|
||||||
|
main.innerHTML = `
|
||||||
|
<div class="box">
|
||||||
|
<h1>Artists on FolxPlay</h1>
|
||||||
|
<p>Six channels of original folk, schlager and pop production from FOLX NETWORK. Pick a channel below to watch the promo.</p>
|
||||||
|
<div class="artistsList">
|
||||||
|
${CHANNELS.map(c => `
|
||||||
|
<a href="#" data-channel-link="${c.key}">
|
||||||
|
<strong style="color:${c.color};">●</strong> ${escapeHtml(c.name)}
|
||||||
|
</a>
|
||||||
|
`).join('')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
main.querySelectorAll('[data-channel-link]').forEach(a => {
|
||||||
|
a.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
navigate('/');
|
||||||
|
// wait one tick so home renders, then play
|
||||||
|
setTimeout(() => playChannel(a.getAttribute('data-channel-link')), 50);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== ABOUT (folxnetwork) =====
|
||||||
|
function renderAbout() {
|
||||||
|
const main = document.getElementById('main');
|
||||||
|
main.innerHTML = `
|
||||||
|
<div class="box">
|
||||||
|
<div>
|
||||||
|
${ABOUT.map(c => `
|
||||||
|
<div class="channel">
|
||||||
|
<div class="channel__title">${escapeHtml(c.title)}</div>
|
||||||
|
<p>${escapeHtml(c.text)}</p>
|
||||||
|
</div>
|
||||||
|
`).join('')}
|
||||||
|
<hr>
|
||||||
|
<a href="/log" data-link>Channel PlayLog</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== POLICIES =====
|
||||||
|
function renderPolicies() {
|
||||||
|
const main = document.getElementById('main');
|
||||||
|
main.innerHTML = `
|
||||||
|
<div class="box">
|
||||||
|
<h1>Terms of service</h1>
|
||||||
|
<p>These Terms of Service ("Account Terms") govern your use of the FolxPlay platform and services, and certain purchases and rentals made through the FolxPlay Channel Store, including videos, audio, graphics, photographs, text and product information ("Original Content") provided by publishers ("Publishers").</p>
|
||||||
|
|
||||||
|
<h3>Definitions</h3>
|
||||||
|
<p><strong>"Channel"</strong> constitutes the entirety of content published, marketed and made available to you by Publishers or on a Publisher's behalf.</p>
|
||||||
|
<p><strong>"Original Content"</strong> includes video, audio, graphics, photographs, text and product information.</p>
|
||||||
|
<p><strong>"Publisher Service"</strong> means a service Publishers make available through the FolxPlay platform.</p>
|
||||||
|
<p><strong>"Member"</strong> refers to anyone that uses the service, also referred to herein as "you".</p>
|
||||||
|
|
||||||
|
<h3>Changes to Terms of Service</h3>
|
||||||
|
<p>FolxPlay may amend the Terms of Service at any time by posting the amended Terms on the FolxPlay website or via the FolxPlay platform, whichever occurs first. By continuing to use the service after the amended Terms are posted, you agree to the amended Terms.</p>
|
||||||
|
|
||||||
|
<h3>Privacy Policy and Use of Data</h3>
|
||||||
|
<p>The FolxPlay Privacy Policy explains how the service collects, uses, transmits, and discloses information you provide.</p>
|
||||||
|
|
||||||
|
<h3>Fees and Charges</h3>
|
||||||
|
<p>FolxPlay does not take part in any commercial activity between Members and Publishers. Fees required to access content will be charged to the payment method on file (credit card or PayPal).</p>
|
||||||
|
|
||||||
|
<h3>Content Availability</h3>
|
||||||
|
<p>The content viewed through the FolxPlay service is solely for your personal and non-commercial enjoyment. Content is protected by copyright and other intellectual property laws and treaties.</p>
|
||||||
|
<p>You only have access to channels and content authorized for the country of your residence. Channels and content will vary by geographic location or country.</p>
|
||||||
|
|
||||||
|
<h3>Children and Age-restricted Content</h3>
|
||||||
|
<p>The service contains content which may not be appropriate for children. You are responsible for ensuring that any age-restricted content is not viewed by any person not meeting the applicable age limits without consent from a parent or guardian.</p>
|
||||||
|
<p>Age ratings consist of five categories: Kids (All), Older Kids (7+), Teens (13+), Young Adults (16+), Adults (18+).</p>
|
||||||
|
|
||||||
|
<h3>Service Updates</h3>
|
||||||
|
<p>FolxPlay reserves the right to update the service, including bug fixes and updates, at any time and without notice.</p>
|
||||||
|
|
||||||
|
<h3>Limitation of Liability</h3>
|
||||||
|
<p>The FolxPlay service is provided "as is" and "as available" with all faults and without warranty of any kind. To the maximum extent permissible by law, FolxPlay does not guarantee, represent, or warrant that the service will be uninterrupted, secure, virus-free or error-free.</p>
|
||||||
|
|
||||||
|
<h3>Contact Information</h3>
|
||||||
|
<p>If you wish to contact us, please send your correspondence to <a href="mailto:office@folx.tv">office@folx.tv</a>.</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== PLAYLOG =====
|
||||||
|
function renderLog() {
|
||||||
|
const main = document.getElementById('main');
|
||||||
|
main.innerHTML = `
|
||||||
|
<div class="box">
|
||||||
|
<h1>Channel PlayLog</h1>
|
||||||
|
<p>The full per-channel play history is available on the production site at
|
||||||
|
<a href="https://folxplay.tv/log">folxplay.tv/log</a>.</p>
|
||||||
|
<p>This preview at <strong>folxplay.biba.live</strong> ships the look and feel of the site together with the channel promos served from JW Player Cloud.</p>
|
||||||
|
<hr>
|
||||||
|
<p><a href="/" data-link>← Back to Home</a></p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderNotFound() {
|
||||||
|
document.getElementById('main').innerHTML = `
|
||||||
|
<div class="box"><h1>Page not found</h1><p>That page does not exist.</p></div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- boot ----------
|
||||||
|
renderShell();
|
||||||
|
render(location.pathname);
|
||||||
BIN
public/backgrounds/bg_01.jpg
Normal file
|
After Width: | Height: | Size: 360 KiB |
BIN
public/backgrounds/bg_02.jpg
Normal file
|
After Width: | Height: | Size: 364 KiB |
BIN
public/backgrounds/bg_03.jpg
Normal file
|
After Width: | Height: | Size: 402 KiB |
BIN
public/backgrounds/bg_04.jpg
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
public/backgrounds/bg_05.jpg
Normal file
|
After Width: | Height: | Size: 101 KiB |
BIN
public/backgrounds/bg_06.jpg
Normal file
|
After Width: | Height: | Size: 206 KiB |
BIN
public/backgrounds/bg_07.jpg
Normal file
|
After Width: | Height: | Size: 190 KiB |
BIN
public/icons/android-icon-144x144.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
public/icons/android-icon-192x192.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
public/icons/android-icon-36x36.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
public/icons/android-icon-48x48.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
public/icons/android-icon-72x72.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
public/icons/android-icon-96x96.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
public/icons/apple-icon-114x114.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
public/icons/apple-icon-120x120.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
public/icons/apple-icon-144x144.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
public/icons/apple-icon-152x152.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
public/icons/apple-icon-180x180.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
public/icons/apple-icon-57x57.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
public/icons/apple-icon-60x60.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
public/icons/apple-icon-72x72.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
public/icons/apple-icon-76x76.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
55
public/icons/favicon-144x144.png
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<link rel="shortcut icon" type="image/ico" href="/assets/favicon-fNqEDBCF.ico" />
|
||||||
|
<title>FolxPlay.tv</title>
|
||||||
|
<meta name="description" content="Folx Music | Zwei Music | One music | Adria Music | One Adria Music | Folx Slovenija" />
|
||||||
|
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-1HF0NT0FBR"></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config', 'G-1HF0NT0FBR');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5738844327989815"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<!-- <script defer src="js/mux.6.0.0.js"></script>-->
|
||||||
|
<!-- <script defer src="js/shaka-3.2.1/shaka-player.ui.min.js"></script>-->
|
||||||
|
<!-- <link rel="stylesheet" href="js/shaka-3.2.1/controls.min.css">-->
|
||||||
|
|
||||||
|
<link rel="apple-touch-icon" sizes="57x57" href="icons/apple-icon-57x57.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="60x60" href="icons/apple-icon-60x60.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="72x72" href="icons/apple-icon-72x72.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="76x76" href="icons/apple-icon-76x76.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="114x114" href="icons/apple-icon-114x114.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="icons/apple-icon-120x120.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="144x144" href="icons/apple-icon-144x144.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="icons/apple-icon-152x152.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="icons/apple-icon-180x180.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="192x192" href="icons/android-icon-192x192.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png">
|
||||||
|
<link rel="manifest" href="/manifest.json">
|
||||||
|
<meta name="msapplication-TileColor" content="#000000">
|
||||||
|
<meta name="msapplication-TileImage" content="icons/ms-icon-144x144.png">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
|
||||||
|
<script type="module" crossorigin src="/assets/index-BHECMraK.js"></script>
|
||||||
|
<link rel="stylesheet" crossorigin href="/assets/index-XemGjmsU.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
55
public/icons/favicon-152x152.png
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<link rel="shortcut icon" type="image/ico" href="/assets/favicon-fNqEDBCF.ico" />
|
||||||
|
<title>FolxPlay.tv</title>
|
||||||
|
<meta name="description" content="Folx Music | Zwei Music | One music | Adria Music | One Adria Music | Folx Slovenija" />
|
||||||
|
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-1HF0NT0FBR"></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config', 'G-1HF0NT0FBR');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5738844327989815"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<!-- <script defer src="js/mux.6.0.0.js"></script>-->
|
||||||
|
<!-- <script defer src="js/shaka-3.2.1/shaka-player.ui.min.js"></script>-->
|
||||||
|
<!-- <link rel="stylesheet" href="js/shaka-3.2.1/controls.min.css">-->
|
||||||
|
|
||||||
|
<link rel="apple-touch-icon" sizes="57x57" href="icons/apple-icon-57x57.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="60x60" href="icons/apple-icon-60x60.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="72x72" href="icons/apple-icon-72x72.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="76x76" href="icons/apple-icon-76x76.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="114x114" href="icons/apple-icon-114x114.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="icons/apple-icon-120x120.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="144x144" href="icons/apple-icon-144x144.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="icons/apple-icon-152x152.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="icons/apple-icon-180x180.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="192x192" href="icons/android-icon-192x192.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png">
|
||||||
|
<link rel="manifest" href="/manifest.json">
|
||||||
|
<meta name="msapplication-TileColor" content="#000000">
|
||||||
|
<meta name="msapplication-TileImage" content="icons/ms-icon-144x144.png">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
|
||||||
|
<script type="module" crossorigin src="/assets/index-BHECMraK.js"></script>
|
||||||
|
<link rel="stylesheet" crossorigin href="/assets/index-XemGjmsU.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
BIN
public/icons/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
55
public/icons/favicon-180x180.png
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<link rel="shortcut icon" type="image/ico" href="/assets/favicon-fNqEDBCF.ico" />
|
||||||
|
<title>FolxPlay.tv</title>
|
||||||
|
<meta name="description" content="Folx Music | Zwei Music | One music | Adria Music | One Adria Music | Folx Slovenija" />
|
||||||
|
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-1HF0NT0FBR"></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config', 'G-1HF0NT0FBR');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5738844327989815"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<!-- <script defer src="js/mux.6.0.0.js"></script>-->
|
||||||
|
<!-- <script defer src="js/shaka-3.2.1/shaka-player.ui.min.js"></script>-->
|
||||||
|
<!-- <link rel="stylesheet" href="js/shaka-3.2.1/controls.min.css">-->
|
||||||
|
|
||||||
|
<link rel="apple-touch-icon" sizes="57x57" href="icons/apple-icon-57x57.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="60x60" href="icons/apple-icon-60x60.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="72x72" href="icons/apple-icon-72x72.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="76x76" href="icons/apple-icon-76x76.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="114x114" href="icons/apple-icon-114x114.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="icons/apple-icon-120x120.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="144x144" href="icons/apple-icon-144x144.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="icons/apple-icon-152x152.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="icons/apple-icon-180x180.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="192x192" href="icons/android-icon-192x192.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png">
|
||||||
|
<link rel="manifest" href="/manifest.json">
|
||||||
|
<meta name="msapplication-TileColor" content="#000000">
|
||||||
|
<meta name="msapplication-TileImage" content="icons/ms-icon-144x144.png">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
|
||||||
|
<script type="module" crossorigin src="/assets/index-BHECMraK.js"></script>
|
||||||
|
<link rel="stylesheet" crossorigin href="/assets/index-XemGjmsU.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
55
public/icons/favicon-192x192.png
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<link rel="shortcut icon" type="image/ico" href="/assets/favicon-fNqEDBCF.ico" />
|
||||||
|
<title>FolxPlay.tv</title>
|
||||||
|
<meta name="description" content="Folx Music | Zwei Music | One music | Adria Music | One Adria Music | Folx Slovenija" />
|
||||||
|
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id=G-1HF0NT0FBR"></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config', 'G-1HF0NT0FBR');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5738844327989815"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<!-- <script defer src="js/mux.6.0.0.js"></script>-->
|
||||||
|
<!-- <script defer src="js/shaka-3.2.1/shaka-player.ui.min.js"></script>-->
|
||||||
|
<!-- <link rel="stylesheet" href="js/shaka-3.2.1/controls.min.css">-->
|
||||||
|
|
||||||
|
<link rel="apple-touch-icon" sizes="57x57" href="icons/apple-icon-57x57.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="60x60" href="icons/apple-icon-60x60.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="72x72" href="icons/apple-icon-72x72.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="76x76" href="icons/apple-icon-76x76.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="114x114" href="icons/apple-icon-114x114.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="icons/apple-icon-120x120.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="144x144" href="icons/apple-icon-144x144.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="icons/apple-icon-152x152.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="icons/apple-icon-180x180.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="192x192" href="icons/android-icon-192x192.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png">
|
||||||
|
<link rel="manifest" href="/manifest.json">
|
||||||
|
<meta name="msapplication-TileColor" content="#000000">
|
||||||
|
<meta name="msapplication-TileImage" content="icons/ms-icon-144x144.png">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
|
||||||
|
<script type="module" crossorigin src="/assets/index-BHECMraK.js"></script>
|
||||||
|
<link rel="stylesheet" crossorigin href="/assets/index-XemGjmsU.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
BIN
public/icons/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
public/icons/favicon-96x96.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
42
public/index.html
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<link rel="shortcut icon" type="image/x-icon" href="/icons/favicon-32x32.png" />
|
||||||
|
<title>FolxPlay.tv</title>
|
||||||
|
<meta name="description" content="Folx Music | Zwei Music | One music | Adria Music | One Adria Music | Folx Slovenija" />
|
||||||
|
|
||||||
|
<link rel="apple-touch-icon" sizes="57x57" href="/icons/apple-icon-57x57.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="60x60" href="/icons/apple-icon-60x60.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="72x72" href="/icons/apple-icon-72x72.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="76x76" href="/icons/apple-icon-76x76.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="114x114" href="/icons/apple-icon-114x114.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="/icons/apple-icon-120x120.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="144x144" href="/icons/apple-icon-144x144.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="/icons/apple-icon-152x152.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-icon-180x180.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="192x192" href="/icons/android-icon-192x192.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="96x96" href="/icons/favicon-96x96.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/icons/favicon-16x16.png">
|
||||||
|
<link rel="manifest" href="/manifest.json">
|
||||||
|
<meta name="msapplication-TileColor" content="#000000">
|
||||||
|
<meta name="msapplication-TileImage" content="/icons/ms-icon-144x144.png">
|
||||||
|
|
||||||
|
<!-- Shaka Player for HLS playback -->
|
||||||
|
<script defer src="https://cdn.jsdelivr.net/npm/shaka-player@4.7.11/dist/shaka-player.compiled.js"></script>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/styles.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root">
|
||||||
|
<!-- background loader fallback -->
|
||||||
|
<div class="mainBackground" style="background-image: url('/backgrounds/bg_01.jpg');"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="module" src="/app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
BIN
public/logos/adria_neg_b.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
public/logos/folx_slo_neg_b.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
public/logos/folxnetwork.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/logos/mt_neg_b.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
public/logos/one_mt_neg_b.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
public/logos/one_neg_b.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/logos/zwei_neg_b.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
16
public/manifest.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "FolxPlay.tv",
|
||||||
|
"short_name": "FolxPlay",
|
||||||
|
"start_url": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#000000",
|
||||||
|
"icons": [
|
||||||
|
{ "src": "/icons/android-icon-36x36.png", "sizes": "36x36", "type": "image/png", "density": "0.75" },
|
||||||
|
{ "src": "/icons/android-icon-48x48.png", "sizes": "48x48", "type": "image/png", "density": "1.0" },
|
||||||
|
{ "src": "/icons/android-icon-72x72.png", "sizes": "72x72", "type": "image/png", "density": "1.5" },
|
||||||
|
{ "src": "/icons/android-icon-96x96.png", "sizes": "96x96", "type": "image/png", "density": "2.0" },
|
||||||
|
{ "src": "/icons/android-icon-144x144.png","sizes": "144x144","type": "image/png", "density": "3.0" },
|
||||||
|
{ "src": "/icons/android-icon-192x192.png","sizes": "192x192","type": "image/png", "density": "4.0" }
|
||||||
|
]
|
||||||
|
}
|
||||||
2
public/robots.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
User-agent: *
|
||||||
|
Disallow:
|
||||||
279
public/styles.css
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
/* ---------- base ---------- */
|
||||||
|
html, body {
|
||||||
|
background: #333;
|
||||||
|
color: #fff;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
|
||||||
|
Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
|
||||||
|
}
|
||||||
|
html { height: 100%; }
|
||||||
|
body { min-height: 100%; margin: 0; }
|
||||||
|
html, body { box-sizing: border-box; }
|
||||||
|
*, *:before, *:after { box-sizing: inherit; }
|
||||||
|
|
||||||
|
a { color: #000; text-decoration: none; border-bottom: 1px dotted #313131; }
|
||||||
|
a:hover { border-bottom: 1px solid #313131; }
|
||||||
|
|
||||||
|
.box {
|
||||||
|
margin: 20px 50px;
|
||||||
|
padding: 20px;
|
||||||
|
background: #eee;
|
||||||
|
color: #333;
|
||||||
|
border-radius: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- navigation ---------- */
|
||||||
|
.nav {
|
||||||
|
background: #0000004d;
|
||||||
|
-webkit-backdrop-filter: blur(15px);
|
||||||
|
backdrop-filter: blur(15px);
|
||||||
|
}
|
||||||
|
.nav__width {
|
||||||
|
padding: 20px 50px;
|
||||||
|
display: flex;
|
||||||
|
max-width: 1500px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.nav .logo { margin-right: 20px; }
|
||||||
|
.nav .logo img { height: 15px; width: auto; display: block; }
|
||||||
|
.nav .links {
|
||||||
|
display: flex;
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border-radius: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.nav .links a {
|
||||||
|
color: #eee;
|
||||||
|
text-decoration: none;
|
||||||
|
border: none;
|
||||||
|
margin-right: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.nav .links a:hover { text-decoration: underline; }
|
||||||
|
.nav .links a.active { font-weight: 700; }
|
||||||
|
|
||||||
|
/* ---------- main background ---------- */
|
||||||
|
.mainBackground {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-size: cover;
|
||||||
|
background-position: left top;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- home page ---------- */
|
||||||
|
.pageHome {
|
||||||
|
padding: 20px 50px;
|
||||||
|
max-width: 1500px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.split {
|
||||||
|
--channelsLeftMargin: 20px;
|
||||||
|
--channelsTopMargin: 0;
|
||||||
|
--channelsWidth: 200px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.split__video { flex: 1; }
|
||||||
|
.split__video_ratio {
|
||||||
|
position: relative;
|
||||||
|
padding-bottom: 56.25%;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
.split__video_ratio video {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: #000;
|
||||||
|
border-radius: 15px;
|
||||||
|
}
|
||||||
|
.split__channels {
|
||||||
|
width: var(--channelsWidth);
|
||||||
|
margin-left: var(--channelsLeftMargin);
|
||||||
|
}
|
||||||
|
@media all and (max-width: 800px) {
|
||||||
|
.split {
|
||||||
|
flex-direction: column;
|
||||||
|
--channelsLeftMargin: 0;
|
||||||
|
--channelsTopMargin: 20px;
|
||||||
|
--channelsWidth: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- currently playing / select ---------- */
|
||||||
|
.currentlyPlaying {
|
||||||
|
background: #0009;
|
||||||
|
padding: 15px;
|
||||||
|
margin-top: 10px;
|
||||||
|
border-radius: 15px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.currentlyPlaying b { font-weight: 700; }
|
||||||
|
|
||||||
|
.selectChannelBox {
|
||||||
|
background: #000;
|
||||||
|
height: 400px;
|
||||||
|
display: flex;
|
||||||
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 15px;
|
||||||
|
color: #b4b4b4;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #000000e6;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
@media all and (max-width: 800px) {
|
||||||
|
.selectChannelBox { height: 150px; }
|
||||||
|
}
|
||||||
|
.selectChannelBox svg { width: 10px; height: 10px; }
|
||||||
|
.selectChannelBox .lineHeader { font-weight: 700; }
|
||||||
|
.selectChannelBox .lineSelectChannel { margin: 5px 0; font-size: 20px; }
|
||||||
|
.selectChannelBox .lineRandom {
|
||||||
|
--initialScale: 1;
|
||||||
|
--endScale: 1.2;
|
||||||
|
cursor: pointer;
|
||||||
|
animation: pulseScale 3s ease infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- playing stats / Top10 ---------- */
|
||||||
|
.playingStats {
|
||||||
|
color: #eee;
|
||||||
|
background: #0009;
|
||||||
|
padding: 15px;
|
||||||
|
margin-top: 10px;
|
||||||
|
border-radius: 15px;
|
||||||
|
}
|
||||||
|
.playingStats__title {
|
||||||
|
font-size: 18px;
|
||||||
|
border-bottom: 1px solid #727272;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.playingStats ol {
|
||||||
|
list-style-position: inside;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.playingStats li {
|
||||||
|
padding: 6px 0;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
.playingStats li:last-child { border-bottom: none; }
|
||||||
|
|
||||||
|
/* ---------- channel list ---------- */
|
||||||
|
.channelList { margin-top: var(--channelsTopMargin); }
|
||||||
|
.channelList__channel {
|
||||||
|
cursor: pointer;
|
||||||
|
height: 64px;
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
text-decoration: none;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.channelList__channel:hover { transform: scale(1.1); }
|
||||||
|
.channelList__channelIcon {
|
||||||
|
--initialWidth: 20px;
|
||||||
|
--endWidth: 25px;
|
||||||
|
width: 64px;
|
||||||
|
background-color: var(--var-bgColor);
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom-left-radius: 10px;
|
||||||
|
border-top-left-radius: 10px;
|
||||||
|
}
|
||||||
|
.channelList__channelIcon svg {
|
||||||
|
opacity: .8;
|
||||||
|
width: var(--initialWidth);
|
||||||
|
height: auto;
|
||||||
|
fill: #fff;
|
||||||
|
}
|
||||||
|
.channelList__channelIconActive svg {
|
||||||
|
opacity: 1;
|
||||||
|
animation: pulseWidth 1s linear infinite;
|
||||||
|
}
|
||||||
|
.channelList__channelLogo {
|
||||||
|
flex: 1;
|
||||||
|
background: #333;
|
||||||
|
border-bottom-right-radius: 10px;
|
||||||
|
border-top-right-radius: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.channelList__channelLogo img {
|
||||||
|
margin-left: 10px;
|
||||||
|
height: 30px;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulseWidth {
|
||||||
|
0% { width: var(--initialWidth); }
|
||||||
|
50% { width: var(--endWidth); }
|
||||||
|
100% { width: var(--initialWidth); }
|
||||||
|
}
|
||||||
|
@keyframes pulseScale {
|
||||||
|
0% { transform: scale(var(--initialScale)); }
|
||||||
|
50% { transform: scale(var(--endScale)); }
|
||||||
|
100% { transform: scale(var(--initialScale)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- about / channel descriptions ---------- */
|
||||||
|
.channel { margin-bottom: 15px; }
|
||||||
|
.channel:last-child { margin-bottom: 0; }
|
||||||
|
.channel__title { font-weight: 700; }
|
||||||
|
.channel p { margin: 0; }
|
||||||
|
|
||||||
|
/* ---------- artists & playlog ---------- */
|
||||||
|
.artistsList {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||||
|
gap: 8px 24px;
|
||||||
|
margin-top: 15px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.artistsList a {
|
||||||
|
color: #ddd;
|
||||||
|
border: none;
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
.artistsList a:hover { color: #fff; text-decoration: underline; }
|
||||||
|
|
||||||
|
.playlogControls {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.playlogControls select,
|
||||||
|
.playlogControls input[type="date"] {
|
||||||
|
padding: 6px 10px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
background: #fff;
|
||||||
|
color: #000;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
.playlogList { font-size: 14px; }
|
||||||
|
.playlogList .row {
|
||||||
|
display: flex;
|
||||||
|
padding: 6px 0;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, .07);
|
||||||
|
}
|
||||||
|
.playlogList .row:last-child { border-bottom: none; }
|
||||||
|
.playlogList .time { width: 80px; color: #666; flex-shrink: 0; }
|
||||||
|
.playlogList .title { flex: 1; font-weight: 500; }
|
||||||
|
.playlogList .artist { width: 240px; color: #666; text-align: right; }
|
||||||
|
@media all and (max-width: 700px) {
|
||||||
|
.playlogList .artist { display: none; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- footer ---------- */
|
||||||
|
.siteFooter {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
padding: 30px 50px;
|
||||||
|
}
|
||||||