Updates for default folder

This commit is contained in:
Matt Walsh
2025-03-24 18:24:49 -05:00
parent 65444978b7
commit e9164d8c36
15 changed files with 169 additions and 30 deletions

View File

@@ -1,3 +1,4 @@
.git/ .git/
Dockerfile Dockerfile
.vscode/ .vscode/
dist/

9
.gitignore vendored
View File

@@ -7,7 +7,8 @@ server/music/*
#except for the readme #except for the readme
!server/music/readme.txt !server/music/readme.txt
#and the sample songs #and the sample songs
!server/music/Catch the Sun.mp3 !server/music/default
!server/music/Crisp day.mp3
!server/music/Rolling Clouds.mp3 #dist folder
!server/music/Strong Breeze.mp3 dist/*
!dist/readme.txt

View File

@@ -98,6 +98,13 @@ As time allows I will be working on the following enhancements.
* Better error reporting when api.weather.gov is down (happens more often than you would think) * Better error reporting when api.weather.gov is down (happens more often than you would think)
## Serving static files
The app can be served as a static set of files on any web server. Run the provided gulp task to create a set of static distribution files:
```
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.
## Community Notes ## Community Notes
Thanks to the WeatherStar community for providing these discussions to further extend your retro forecasts! Thanks to the WeatherStar community for providing these discussions to further extend your retro forecasts!

1
dist/readme.txt vendored Normal file
View File

@@ -0,0 +1 @@
This folder is a placeholder for static files generated by the gulp task buildDist

View File

@@ -12,11 +12,13 @@ import s3Upload from 'gulp-s3-upload';
import webpack from 'webpack-stream'; import webpack from 'webpack-stream';
import TerserPlugin from 'terser-webpack-plugin'; import TerserPlugin from 'terser-webpack-plugin';
import { readFile } from 'fs/promises'; import { readFile } from 'fs/promises';
import reader from '../src/playlist-reader.mjs';
import file from "gulp-file";
// get cloudfront // get cloudfront
import { CloudFrontClient, CreateInvalidationCommand } from '@aws-sdk/client-cloudfront'; import { CloudFrontClient, CreateInvalidationCommand } from '@aws-sdk/client-cloudfront';
const clean = () => deleteAsync(['./dist**']); const clean = () => deleteAsync(['./dist/**/*', '!./dist/readme.txt']);
const cloudfront = new CloudFrontClient({ region: 'us-east-1' }); const cloudfront = new CloudFrontClient({ region: 'us-east-1' });
@@ -122,8 +124,9 @@ const compressHtml = async () => {
const otherFiles = [ const otherFiles = [
'server/robots.txt', 'server/robots.txt',
'server/manifest.json', 'server/manifest.json',
'server/music/**/*.mp3'
]; ];
const copyOtherFiles = () => src(otherFiles, { base: 'server/' }) const copyOtherFiles = () => src(otherFiles, { base: 'server/', encoding: false })
.pipe(dest('./dist')); .pipe(dest('./dist'));
const s3 = s3Upload({ const s3 = s3Upload({
@@ -170,10 +173,20 @@ const invalidate = () => cloudfront.send(new CreateInvalidationCommand({
}, },
})); }));
const buildDist = series(clean, parallel(buildJs, compressJsData, compressJsVendor, copyCss, compressHtml, copyOtherFiles)); const buildPlaylist = async () => {
const availableFiles = await reader();
const playlist = { availableFiles };
return file('playlist.json', JSON.stringify(playlist)).pipe(dest('./dist'))
}
const buildDist = series(clean, parallel(buildJs, compressJsData, compressJsVendor, copyCss, compressHtml, copyOtherFiles, buildPlaylist));
// upload_images could be in parallel with upload, but _images logs a lot and has little changes // upload_images could be in parallel with upload, but _images logs a lot and has little changes
// by running upload last the majority of the changes will be at the bottom of the log for easy viewing // by running upload last the majority of the changes will be at the bottom of the log for easy viewing
const publishFrontend = series(buildDist, uploadImages, upload, invalidate); const publishFrontend = series(buildDist, uploadImages, upload, invalidate);
export default publishFrontend; export default publishFrontend;
export {
buildDist,
}

View File

@@ -1,7 +1,8 @@
import updateVendor from './gulp/update-vendor.mjs'; import updateVendor from './gulp/update-vendor.mjs';
import publishFrontend from './gulp/publish-frontend.mjs'; import publishFrontend, { buildDist } from './gulp/publish-frontend.mjs'
export { export {
updateVendor, updateVendor,
publishFrontend, publishFrontend,
buildDist,
}; };

97
package-lock.json generated
View File

@@ -22,6 +22,7 @@
"gulp-awspublish": "^8.0.0", "gulp-awspublish": "^8.0.0",
"gulp-concat": "^2.6.1", "gulp-concat": "^2.6.1",
"gulp-ejs": "^5.1.0", "gulp-ejs": "^5.1.0",
"gulp-file": "^0.4.0",
"gulp-htmlmin": "^5.0.1", "gulp-htmlmin": "^5.0.1",
"gulp-rename": "^2.0.0", "gulp-rename": "^2.0.0",
"gulp-s3-upload": "^1.7.3", "gulp-s3-upload": "^1.7.3",
@@ -5473,6 +5474,102 @@
"readable-stream": "2 || 3" "readable-stream": "2 || 3"
} }
}, },
"node_modules/gulp-file": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/gulp-file/-/gulp-file-0.4.0.tgz",
"integrity": "sha512-3NPCJpAPpbNoV2aml8T96OK3Aof4pm4PMOIa1jSQbMNSNUUXdZ5QjVgLXLStjv0gg9URcETc7kvYnzXdYXUWug==",
"dev": true,
"license": "BSD",
"dependencies": {
"through2": "^0.4.1",
"vinyl": "^2.1.0"
}
},
"node_modules/gulp-file/node_modules/isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
"dev": true,
"license": "MIT"
},
"node_modules/gulp-file/node_modules/object-keys": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz",
"integrity": "sha512-ncrLw+X55z7bkl5PnUvHwFK9FcGuFYo9gtjws2XtSzL+aZ8tm830P60WJ0dSmFVaSalWieW5MD7kEdnXda9yJw==",
"dev": true,
"license": "MIT"
},
"node_modules/gulp-file/node_modules/readable-stream": {
"version": "1.0.34",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
"integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==",
"dev": true,
"license": "MIT",
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.1",
"isarray": "0.0.1",
"string_decoder": "~0.10.x"
}
},
"node_modules/gulp-file/node_modules/replace-ext": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz",
"integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/gulp-file/node_modules/string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==",
"dev": true,
"license": "MIT"
},
"node_modules/gulp-file/node_modules/through2": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz",
"integrity": "sha512-45Llu+EwHKtAZYTPPVn3XZHBgakWMN3rokhEv5hu596XP+cNgplMg+Gj+1nmAvj+L0K7+N49zBKx5rah5u0QIQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"readable-stream": "~1.0.17",
"xtend": "~2.1.1"
}
},
"node_modules/gulp-file/node_modules/vinyl": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz",
"integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==",
"dev": true,
"license": "MIT",
"dependencies": {
"clone": "^2.1.1",
"clone-buffer": "^1.0.0",
"clone-stats": "^1.0.0",
"cloneable-readable": "^1.0.0",
"remove-trailing-separator": "^1.0.1",
"replace-ext": "^1.0.0"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/gulp-file/node_modules/xtend": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz",
"integrity": "sha512-vMNKzr2rHP9Dp/e1NQFnLQlwlhp9L/LfvnsVdHxN1f+uggyVI3i08uD14GPvCToPkdsRfyPqIyYGmIk58V98ZQ==",
"dev": true,
"dependencies": {
"object-keys": "~0.4.0"
},
"engines": {
"node": ">=0.4"
}
},
"node_modules/gulp-htmlmin": { "node_modules/gulp-htmlmin": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/gulp-htmlmin/-/gulp-htmlmin-5.0.1.tgz", "resolved": "https://registry.npmjs.org/gulp-htmlmin/-/gulp-htmlmin-5.0.1.tgz",

View File

@@ -7,6 +7,7 @@
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"build:css": "sass --style=compressed ./server/styles/scss/main.scss ./server/styles/main.css", "build:css": "sass --style=compressed ./server/styles/scss/main.scss ./server/styles/main.css",
"build": "gulp buildDist",
"lint": "eslint ./server/scripts/**/*.mjs", "lint": "eslint ./server/scripts/**/*.mjs",
"lint:fix": "eslint --fix ./server/scripts/**/*.mjs" "lint:fix": "eslint --fix ./server/scripts/**/*.mjs"
}, },
@@ -21,32 +22,33 @@
}, },
"homepage": "https://github.com/netbymatt/ws4kp#readme", "homepage": "https://github.com/netbymatt/ws4kp#readme",
"devDependencies": { "devDependencies": {
"del": "^8.0.0",
"jquery": "^3.6.0",
"jquery-touchswipe": "^1.6.19",
"luxon": "^3.0.0",
"nosleep.js": "^0.12.0",
"suncalc": "^1.8.0",
"swiped-events": "^1.1.4",
"@aws-sdk/client-cloudfront": "^3.609.0", "@aws-sdk/client-cloudfront": "^3.609.0",
"gulp-awspublish": "^8.0.0", "del": "^8.0.0",
"gulp-s3-upload": "^1.7.3",
"eslint": "^8.2.0", "eslint": "^8.2.0",
"eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-import": "^2.10.0", "eslint-plugin-import": "^2.10.0",
"gulp": "^5.0.0", "gulp": "^5.0.0",
"gulp-awspublish": "^8.0.0",
"gulp-concat": "^2.6.1", "gulp-concat": "^2.6.1",
"gulp-ejs": "^5.1.0", "gulp-ejs": "^5.1.0",
"gulp-file": "^0.4.0",
"gulp-htmlmin": "^5.0.1", "gulp-htmlmin": "^5.0.1",
"gulp-rename": "^2.0.0", "gulp-rename": "^2.0.0",
"gulp-s3-upload": "^1.7.3",
"gulp-sass": "^6.0.0", "gulp-sass": "^6.0.0",
"gulp-terser": "^2.0.0", "gulp-terser": "^2.0.0",
"jquery": "^3.6.0",
"jquery-touchswipe": "^1.6.19",
"luxon": "^3.0.0",
"nosleep.js": "^0.12.0",
"sass": "^1.54.0",
"suncalc": "^1.8.0",
"swiped-events": "^1.1.4",
"terser-webpack-plugin": "^5.3.6", "terser-webpack-plugin": "^5.3.6",
"webpack-stream": "^7.0.0", "webpack-stream": "^7.0.0"
"sass": "^1.54.0"
}, },
"dependencies": { "dependencies": {
"express": "^4.17.1", "ejs": "^3.1.5",
"ejs": "^3.1.5" "express": "^4.17.1"
} }
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,2 +1,3 @@
.mp3 files placed in this folder will be available via the un-mute button in the application. .mp3 files placed in this folder will be available via the un-mute button in the application.
No subdirectories will be scanned, and music will be played in a random order. No subdirectories will be scanned, and music will be played in a random order.
The default folder will be used only if no .mp3 files are found in this /server/music folder

View File

@@ -63,12 +63,17 @@ const toggleMedia = (forcedState) => {
stateChanged(); stateChanged();
}; };
const startMedia = () => { const startMedia = async () => {
// if there's not media player yet, enable it // if there's not media player yet, enable it
if (!player) { if (!player) {
initializePlayer(); initializePlayer();
} else { } else {
player.play(); try {
await player.play();
} catch (e) {
console.error('Couldn\'t play music');
console.error(e);
}
} }
}; };
@@ -128,11 +133,12 @@ const initializePlayer = () => {
console.log('player initialized'); console.log('player initialized');
}; };
const playerCanPlay = () => { const playerCanPlay = async () => {
// check to make sure they user still wants music (protect against slow loading music) // check to make sure they user still wants music (protect against slow loading music)
if (!mediaPlaying.value) return; if (!mediaPlaying.value) return;
// start playing // start playing
player.play(); startMedia();
}; };
const playerEnded = () => { const playerEnded = () => {

View File

@@ -1,12 +1,21 @@
import fs from 'fs/promises'; import fs from 'fs/promises';
const mp3Filter = (file) => file.match(/\.mp3$/);
const reader = async () => { const reader = async () => {
// get the listing of files in the folder // get the listing of files in the folder
const rawFiles = await fs.readdir('./server/music'); const rawFiles = await fs.readdir('./server/music');
// filter for mp3 files // filter for mp3 files
const files = rawFiles.filter((file) => file.match(/\.mp3$/)); const files = rawFiles.filter(mp3Filter);
console.log(files); // if files were found return them
return files; if (files.length > 0) {
return files;
}
// fall back to the default folder
const defaultFiles = await fs.readdir('./server/music/default');
return defaultFiles.map(file => `default/${file}`).filter(mp3Filter);
}; };
export default reader; export default reader;