diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5ae09e9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +# syntax=docker/dockerfile:1 +FROM nginx:1.22.0-alpine +WORKDIR /var/www/ +COPY nginx/nginx.conf /etc/nginx/nginx.conf +RUN mkdir -p /var/www/website +COPY src /var/www/website + +EXPOSE 80 \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cab5ff3 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +login: + docker login gitea.bjornmossa.net + +start: + npx live-server ./src + +build: + docker build . -t gitea.bjornmossa.net/radioiceberg/website + +push: login + docker push gitea.bjornmossa.net/radioiceberg/website + diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..c2bb3c3 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,50 @@ +include /etc/nginx/modules_enabled/*.conf; + +events { + worker_connections 1024; +} + +http { + server { + listen 80; + server_name radioiceberg.net www.radioiceberg.net localhost 127.0.0.1; + rewrite ^/(.*) https://radioiceberg.net/$1 permanent; + } + + server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + include /etc/nginx/mime.types; + server_name radioiceberg.net www.radioiceberg.net localhost 127.0.0.1; + + #SSL + #ssl_certificate /etc/letsencrypt/live/radioiceberg.net/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/radioiceberg.net/privkey.pem; + ssl_trusted_certificate /etc/letsencrypt/live/radioiceberg.net/chain.pem; + + location / { + root /var/www/website; + index index.html; + } + + location /stream { + proxy_read_timeout 3000; + proxy_connect_timeout 3000; + proxy_redirect off; + proxy_pass http://icecast:8000/iceberg.ogg; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Server $host; + proxy_set_header X-Forwarded-For $remote_addr; + } + + location /meta { + proxy_pass http://metadata:7000; + proxy_http_version 1.1; + proxy_connect_timeout 1d; + proxy_send_timeout 1d; + proxy_read_timeout 1d; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + } + } +} \ No newline at end of file diff --git a/src/fonts/lato-lightitalic.woff b/src/fonts/lato-lightitalic.woff new file mode 100644 index 0000000..86b3600 Binary files /dev/null and b/src/fonts/lato-lightitalic.woff differ diff --git a/src/fonts/lato-lightitalic.woff2 b/src/fonts/lato-lightitalic.woff2 new file mode 100644 index 0000000..77aabad Binary files /dev/null and b/src/fonts/lato-lightitalic.woff2 differ diff --git a/src/fonts/lato-regular-webfont.woff2 b/src/fonts/lato-regular-webfont.woff2 new file mode 100644 index 0000000..495cb9e Binary files /dev/null and b/src/fonts/lato-regular-webfont.woff2 differ diff --git a/src/icons/android-chrome-192x192.png b/src/icons/android-chrome-192x192.png new file mode 100644 index 0000000..937d2e0 Binary files /dev/null and b/src/icons/android-chrome-192x192.png differ diff --git a/src/icons/android-chrome-512x512.png b/src/icons/android-chrome-512x512.png new file mode 100644 index 0000000..939b049 Binary files /dev/null and b/src/icons/android-chrome-512x512.png differ diff --git a/src/icons/apple-touch-icon.png b/src/icons/apple-touch-icon.png new file mode 100644 index 0000000..dbbb103 Binary files /dev/null and b/src/icons/apple-touch-icon.png differ diff --git a/src/icons/favicon-16x16.png b/src/icons/favicon-16x16.png new file mode 100644 index 0000000..ebd0022 Binary files /dev/null and b/src/icons/favicon-16x16.png differ diff --git a/src/icons/favicon-32x32.png b/src/icons/favicon-32x32.png new file mode 100644 index 0000000..32673ff Binary files /dev/null and b/src/icons/favicon-32x32.png differ diff --git a/src/icons/favicon.ico b/src/icons/favicon.ico new file mode 100644 index 0000000..c9acbc6 Binary files /dev/null and b/src/icons/favicon.ico differ diff --git a/src/icons/site.webmanifest b/src/icons/site.webmanifest new file mode 100644 index 0000000..cd8a657 --- /dev/null +++ b/src/icons/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "Radio Iceberg", + "short_name": "Iceberg", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ded8d8", + "background_color": "#252525", + "display": "standalone" +} diff --git a/src/images/social-preview.jpg b/src/images/social-preview.jpg new file mode 100644 index 0000000..06be136 Binary files /dev/null and b/src/images/social-preview.jpg differ diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..d0d8a32 --- /dev/null +++ b/src/index.html @@ -0,0 +1,94 @@ + + + + + Radio . Iceberg + + + + + + + + + + + + + + +

Radio . Iceberg

+ +
+
now playing:
+
We don't know'
+
yet...
+
+
+ + + + + +
+ + + diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..e608cd7 --- /dev/null +++ b/src/main.js @@ -0,0 +1,115 @@ +const STOP_STATE_POINTS = "650,0, 1350,0, 1350,500, 650,500"; +const PLAY_STATE_POINTS = "0,0, 2000,250, 2000,250, 0,500"; + +const getAudio = () => document.querySelector("audio"); +const getPlayButton = () => document.querySelector(".play-stream"); +const getPlayButtonAnimation = () => + document.getElementById("play-button-animation"); + +const hideAudio = () => { + const a = getAudio(); + a.hidden = true; +}; + +const playAudio = () => { + getAudio()?.play(); +}; + +const pauseAudio = () => { + getAudio()?.pause(); +}; + +const setButtonPlayState = () => { + const animationNode = getPlayButtonAnimation(); + animationNode.setAttribute("from", PLAY_STATE_POINTS); + animationNode.setAttribute("to", STOP_STATE_POINTS); + animationNode.beginElement(); +}; + +const setButtonStopState = () => { + const animationNode = getPlayButtonAnimation(); + animationNode.setAttribute("from", STOP_STATE_POINTS); + animationNode.setAttribute("to", PLAY_STATE_POINTS); + animationNode.beginElement(); +}; + +const startIcebergsAnimation = () => { + [1, 2, 3].map((i) => { + const polygon = document.getElementById(`iceberg${i}`); + polygon.classList.add("active"); + }); +}; + +const stopIcebergsAnimation = () => { + [1, 2, 3].map((i) => { + const polygon = document.getElementById(`iceberg${i}`); + polygon.classList.remove("active"); + }); +}; + +const handleAudioPlay = () => { + playAudio(); + setButtonPlayState(); + startIcebergsAnimation(); +}; + +const handleAudioStop = () => { + pauseAudio(); + setButtonStopState(); + stopIcebergsAnimation(); +}; + +const addPlayerInteractions = () => { + const playerButton = getPlayButton(); + const audio = getAudio(); + + playerButton.addEventListener("click", () => { + audio.paused ? handleAudioPlay() : handleAudioStop(); + }); + + audio.addEventListener("timeupdate", () => { + window.requestAnimationFrame(() => { + document.documentElement.style.setProperty( + "--time", + `${audio.currentTime * 100}deg` + ); + }); + }); +}; + +async function connectToServer() { + const ws = new WebSocket(`ws://${location.host}/meta`); + ws.addEventListener("message", changeMetadata); +} + +const changeMetadata = (metadataEvent) => { + const metaObject = JSON.parse(metadataEvent.data); + const { artist, title } = metaObject; + const artistBlock = document.querySelector(".now-playing_artist"); + const titleBlock = document.querySelector(".now-playing_song"); + + if (artist) { + artistBlock.innerHTML = artist; + } else { + artistBlock.innerHTML = ""; + } + + if (title) { + titleBlock.innerHTML = title; + document.title = `${title} on Radioiceberg`; + } else { + titleBlock.innerHTML = ""; + } + + if (artist && title) { + document.title = `${artist} - ${title} on Radioiceberg`; + } +}; + +const main = () => { + hideAudio(); + addPlayerInteractions(); + connectToServer(); +}; + +document.addEventListener("DOMContentLoaded", main); diff --git a/src/scripts/dom.js b/src/scripts/dom.js new file mode 100644 index 0000000..fe89522 --- /dev/null +++ b/src/scripts/dom.js @@ -0,0 +1,10 @@ +const getAudio = () => document.querySelector("audio"); +const getPlayButton = () => document.querySelector(".play-stream"); +const getPlayButtonAnimation = () => + document.getElementById("play-button-animation"); + +export { + getAudio, + getPlayButton, + getPlayButtonAnimation +}; diff --git a/src/scripts/interactions.js b/src/scripts/interactions.js new file mode 100644 index 0000000..8ffb66c --- /dev/null +++ b/src/scripts/interactions.js @@ -0,0 +1,14 @@ +import { getAudio } from "./dom.js"; + +const hideAudio = () => { + const a = getAudio(); + a.hidden = true; +}; + +const playAudio = () => { + getAudio()?.play(); +}; + +const pauseAudio = () => { + getAudio()?.pause(); +}; diff --git a/src/scripts/now-playing.js b/src/scripts/now-playing.js new file mode 100644 index 0000000..e69de29 diff --git a/src/styles.css b/src/styles.css new file mode 100644 index 0000000..e588888 --- /dev/null +++ b/src/styles.css @@ -0,0 +1,70 @@ +@import url("styles/global.css"); +@import url("styles/system.css"); + +body { + background-color: var(--color-primary-0); + color: var(--color-primary-1); + height: 100vh; + max-height: 100vh; + overflow: hidden; + position: relative; + line-height: 1.75; + padding: 1rem; +} + +h1, +h2, +h3, +h4, +h5 { + margin: 3rem 0 1.38rem; + font-family: "Lato", sans-serif; + font-weight: 400; + line-height: 1.3; +} + +h1 { + text-transform: uppercase; +} + +h1 { + margin-top: 0; + font-size: 5.063rem; +} + +.play-stream { + background: transparent; + border: none; + padding: 0; + width: 300px; + cursor: pointer; +} + +.now-playing { + font-family: "Lato Light Italic"; + font-size: 1.5rem; +} + +.icebergs { + position: fixed; + bottom: 0; + width: 100%; + rotate: -45deg; + translate: 10% 50%; + max-height: 70vh; +} + +#iceberg1, +#iceberg2, +#iceberg3 { + transition: transform 2s ease-in-out; + transform: translate(0); +} + +#iceberg1.active { + transform: translate(0, calc(100px * sin(var(--time)))); +} + +#iceberg2.active { + transform: translate(0, calc(50px * cos(var(--time)))); +} diff --git a/src/styles/global.css b/src/styles/global.css new file mode 100644 index 0000000..688a5f7 --- /dev/null +++ b/src/styles/global.css @@ -0,0 +1,9 @@ +html { + font-size: 100%; +} + +html, +body { + margin: 0; + padding: 0; +} diff --git a/src/styles/system.css b/src/styles/system.css new file mode 100644 index 0000000..9d83bb9 --- /dev/null +++ b/src/styles/system.css @@ -0,0 +1,36 @@ +/* Typography */ + +@font-face { + font-family: "Lato"; + src: url("fonts/lato-regular-webfont.woff2") format("woff2"), + url("fonts/lato-regular-webfont.woff") format("woff"); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: "Lato Light Italic"; + src: url("fonts/lato-lightitalic.woff2") format("woff2"), + url("fonts/lato-lightitalic.woff") format("woff"); + font-weight: normal; + font-style: normal; +} + +/* Colors */ + +:root { + --color-primary-0: #252525; + --color-primary-1: #ded8d8; + --color-primary-2: #7d7d7d; + --color-primary-3: #010101; + --color-primary-4: #0e0404; + + --color-complement-0: #1e1e1e; + --color-complement-1: #adb2ad; + --color-complement-2: #646464; + --color-complement-3: #010101; + --color-complement-4: #030b03; + + --time: 0; + --lol: calc(100px * sin(var(--time))); +}