diff --git a/Dockerfile b/Dockerfile index 5583756..3ff3d52 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,15 @@ FROM node:24-alpine AS node-builder WORKDIR /app -# RUN npm install gulp - COPY package.json . COPY package-lock.json . - COPY . . RUN npm install RUN npm run build +RUN rm dist/playlist.json FROM nginx:alpine -# COPY --from=node-builder /app/server /usr/share/nginx/html COPY --from=node-builder /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf +CMD ["nginx", "-g", "daemon off;"] diff --git a/README.md b/README.md index 64f3218..9eee6e3 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,13 @@ npm run buildDist ``` The resulting files will be in the /dist folder in the root of the project. These can then be uploaded to a web server for hosting, no server-side scripting is required. +When using the provided Docker image, the browser will generate `playlist.json` +on the fly by scanning the `/music` directory served by nginx. The image +intentionally omits this file so the page falls back to scanning the directory. +Simply bind mount your music folder and the playlist will be created +automatically. If no files are found in `/music`, the built in tracks located in +`/music/default/` will be used instead. + ## What's different I've made several changes to this Weather Star 4000 simulation compared to the original hardware unit and the code that this was forked from. @@ -129,9 +136,12 @@ If you're looking for the original music that played during forecasts [TWCClassi ### Customizing the music Placing .mp3 files in the `/server/music` folder will override the default music included in the repo. Subdirectories will not be scanned. When weatherstar loads in the browser it will load a list if available files and randomize the order when it starts playing. On each loop through the available tracks the order will again be shuffled. If you're using the static files method to host your WeatherStar music is located in `/music`. -If using docker, you must pass a local accessible folder to the container in the `/app/server/music` directory. +If using docker, you can bind mount a local folder containing your music files. +Mount the folder at `/usr/share/nginx/html/music` so the browser can read the +directory listing and build the playlist automatically. If there are no `.mp3` +files in `/music`, the built in tracks from `/music/default/` are used. ``` -docker run -p 8080:8080 -v /path/to/local/music:/app/server/music ghcr.io/netbymatt/ws4kp +docker run -p 8080:8080 -v /path/to/local/music:/usr/share/nginx/html/music ghcr.io/netbymatt/ws4kp ``` ### Music doesn't auto play diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..207aac9 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,20 @@ +server { + listen 80; + server_name localhost; + + root /usr/share/nginx/html; + + location / { + index index.html index.htm; + try_files $uri $uri/ =404; + } + + location /music/ { + autoindex on; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} diff --git a/server/scripts/modules/media.mjs b/server/scripts/modules/media.mjs index 235b6af..b44939a 100644 --- a/server/scripts/modules/media.mjs +++ b/server/scripts/modules/media.mjs @@ -1,4 +1,4 @@ -import { json } from './utils/fetch.mjs'; +import { json, text } from './utils/fetch.mjs'; import Setting from './utils/setting.mjs'; let playlist; @@ -19,18 +19,38 @@ document.addEventListener('DOMContentLoaded', () => { getMedia(); }); +const scanMusicDirectory = async () => { + const parseDirectory = async (path, prefix = "") => { + const listing = await text(path); + const matches = [...listing.matchAll(/href="([^\"]+\.mp3)"/gi)]; + return matches.map((m) => `${prefix}${m[1]}`); + }; + + try { + let files = await parseDirectory("music/"); + if (files.length === 0) { + files = await parseDirectory("music/default/", "default/"); + } + return { availableFiles: files }; + } catch (e) { + console.error("Unable to scan music directory"); + console.error(e); + return { availableFiles: [] }; + } +}; + + const getMedia = async () => { - try { - // fetch the playlist - const rawPlaylist = await json('playlist.json'); - // store the playlist - playlist = rawPlaylist; - // enable the media player - enableMediaPlayer(); - } catch (e) { - console.error("Couldn't get playlist"); - console.error(e); - } + try { + // fetch the playlist + const rawPlaylist = await json('playlist.json'); + playlist = rawPlaylist; + } catch (e) { + console.warn("Couldn't get playlist.json, falling back to directory scan"); + playlist = await scanMusicDirectory(); + } + + enableMediaPlayer(); }; const enableMediaPlayer = () => {