add client-side generation for playlist.json for static builds

This commit is contained in:
Mitchell Scott
2025-06-13 08:37:01 -06:00
parent 1ac514293b
commit 2827913d42
4 changed files with 67 additions and 18 deletions

View File

@@ -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;"]

View File

@@ -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

20
nginx.conf Normal file
View File

@@ -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;
}
}

View File

@@ -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 = () => {