commit d148152e0b2db7887dc0dd7c2b60e2f995a0e0bd Author: OpenClaw Agent Date: Wed Apr 29 11:17:21 2026 +0200 Initial: folxplay.tv clone with JW promos, no AdSense diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e0c5784 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +.git +.gitignore +README.md +node_modules +*.log +.DS_Store diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3f00ede --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +*.log +.DS_Store +.env diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8404080 --- /dev/null +++ b/Dockerfile @@ -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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..a772c3e --- /dev/null +++ b/README.md @@ -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 + +- ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + diff --git a/public/icons/favicon-152x152.png b/public/icons/favicon-152x152.png new file mode 100644 index 0000000..db3bedc --- /dev/null +++ b/public/icons/favicon-152x152.png @@ -0,0 +1,55 @@ + + + + + + + + FolxPlay.tv + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + diff --git a/public/icons/favicon-16x16.png b/public/icons/favicon-16x16.png new file mode 100644 index 0000000..626812d Binary files /dev/null and b/public/icons/favicon-16x16.png differ diff --git a/public/icons/favicon-180x180.png b/public/icons/favicon-180x180.png new file mode 100644 index 0000000..db3bedc --- /dev/null +++ b/public/icons/favicon-180x180.png @@ -0,0 +1,55 @@ + + + + + + + + FolxPlay.tv + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + diff --git a/public/icons/favicon-192x192.png b/public/icons/favicon-192x192.png new file mode 100644 index 0000000..db3bedc --- /dev/null +++ b/public/icons/favicon-192x192.png @@ -0,0 +1,55 @@ + + + + + + + + FolxPlay.tv + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + diff --git a/public/icons/favicon-32x32.png b/public/icons/favicon-32x32.png new file mode 100644 index 0000000..16c891a Binary files /dev/null and b/public/icons/favicon-32x32.png differ diff --git a/public/icons/favicon-96x96.png b/public/icons/favicon-96x96.png new file mode 100644 index 0000000..ef58a08 Binary files /dev/null and b/public/icons/favicon-96x96.png differ diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..704685e --- /dev/null +++ b/public/index.html @@ -0,0 +1,42 @@ + + + + + + + + FolxPlay.tv + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ + + + diff --git a/public/logos/adria_neg_b.png b/public/logos/adria_neg_b.png new file mode 100644 index 0000000..fa8b75e Binary files /dev/null and b/public/logos/adria_neg_b.png differ diff --git a/public/logos/folx_slo_neg_b.png b/public/logos/folx_slo_neg_b.png new file mode 100644 index 0000000..46e6430 Binary files /dev/null and b/public/logos/folx_slo_neg_b.png differ diff --git a/public/logos/folxnetwork.png b/public/logos/folxnetwork.png new file mode 100644 index 0000000..5f8a412 Binary files /dev/null and b/public/logos/folxnetwork.png differ diff --git a/public/logos/mt_neg_b.png b/public/logos/mt_neg_b.png new file mode 100644 index 0000000..491cd58 Binary files /dev/null and b/public/logos/mt_neg_b.png differ diff --git a/public/logos/one_mt_neg_b.png b/public/logos/one_mt_neg_b.png new file mode 100644 index 0000000..8d7c6a6 Binary files /dev/null and b/public/logos/one_mt_neg_b.png differ diff --git a/public/logos/one_neg_b.png b/public/logos/one_neg_b.png new file mode 100644 index 0000000..6635764 Binary files /dev/null and b/public/logos/one_neg_b.png differ diff --git a/public/logos/zwei_neg_b.png b/public/logos/zwei_neg_b.png new file mode 100644 index 0000000..82e4c42 Binary files /dev/null and b/public/logos/zwei_neg_b.png differ diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..a7a60e5 --- /dev/null +++ b/public/manifest.json @@ -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" } + ] +} diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..eb05362 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/public/styles.css b/public/styles.css new file mode 100644 index 0000000..3af200f --- /dev/null +++ b/public/styles.css @@ -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; +}