Initial commit
All checks were successful
NPM Audit Check / Check NPM audit (push) Successful in -2m13s
All checks were successful
NPM Audit Check / Check NPM audit (push) Successful in -2m13s
This commit is contained in:
commit
4a260f112f
27
.github/workflows/npm-audit.yml
vendored
Normal file
27
.github/workflows/npm-audit.yml
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
name: NPM Audit Check
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
schedule:
|
||||||
|
- cron: '15 16 * * 5'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
npm_audit:
|
||||||
|
name: Check NPM audit
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 20
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: https://github.com/actions/checkout@v4
|
||||||
|
|
||||||
|
- name: NPM Audit
|
||||||
|
run: npm audit
|
104
.gitignore
vendored
Normal file
104
.gitignore
vendored
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# TypeScript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
.env.test
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# next.js build output
|
||||||
|
.next
|
||||||
|
|
||||||
|
# nuxt.js build output
|
||||||
|
.nuxt
|
||||||
|
|
||||||
|
# gatsby files
|
||||||
|
.cache/
|
||||||
|
public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
|
||||||
|
# custom .gitignore
|
||||||
|
config/config.json5
|
||||||
|
distribution/
|
||||||
|
.env
|
12
README.md
Normal file
12
README.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# morning-report
|
||||||
|
|
||||||
|
randomly generate a radio show-style weather report according to a flexible config file.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
118
config/config.example.json5
Normal file
118
config/config.example.json5
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
{
|
||||||
|
"programs": [
|
||||||
|
[
|
||||||
|
"intro music",
|
||||||
|
"intro",
|
||||||
|
"hi's and low's",
|
||||||
|
"notable weather",
|
||||||
|
"signoff"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"segments": {
|
||||||
|
"intro": [
|
||||||
|
"intro 1",
|
||||||
|
"intro 2"
|
||||||
|
],
|
||||||
|
"hi's and low's": [
|
||||||
|
"hilo 1",
|
||||||
|
"hilo 2"
|
||||||
|
],
|
||||||
|
"notable weather": [
|
||||||
|
"rain 1",
|
||||||
|
"rain 2",
|
||||||
|
"storm 1",
|
||||||
|
"storm 2",
|
||||||
|
"snow 1",
|
||||||
|
"hail 1"
|
||||||
|
],
|
||||||
|
"signoff": [
|
||||||
|
"signoff 1",
|
||||||
|
"signoff 2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sequences": {
|
||||||
|
"intro music": {
|
||||||
|
"tracks": [
|
||||||
|
"audio/intro_music.flac"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"intro 1": {
|
||||||
|
"tracks": [
|
||||||
|
"audio/intro_01.flac"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"intro 2": {
|
||||||
|
"tracks": [
|
||||||
|
"audio/intro_02.flac"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hilo 1": {
|
||||||
|
"tracks": [
|
||||||
|
"audio/hi_01.flac",
|
||||||
|
"%cory hi",
|
||||||
|
"audio/lo_01.flac",
|
||||||
|
"%cory lo"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hilo 2": {
|
||||||
|
"tracks": [
|
||||||
|
"audio/hi_02.flac",
|
||||||
|
"%cory hi",
|
||||||
|
"audio/lo_02.flac",
|
||||||
|
"%cory lo"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"rain 1": {
|
||||||
|
"condition": "weather == rain",
|
||||||
|
"tracks": [
|
||||||
|
"audio/rain1.flac"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"rain 2": {
|
||||||
|
"condition": "weather == rain",
|
||||||
|
"tracks": [
|
||||||
|
"audio/rain2.flac"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"storm 1": {
|
||||||
|
"condition": "weather == storm",
|
||||||
|
"tracks": [
|
||||||
|
"audio/storm1.flac"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"storm 2": {
|
||||||
|
"condition": "weather == storm",
|
||||||
|
"tracks": [
|
||||||
|
"audio/storm2.flac"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"snow 1": {
|
||||||
|
"condition": "weather == snow",
|
||||||
|
"tracks": [
|
||||||
|
"audio/snow1.flac"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hail 1": {
|
||||||
|
"condition": "weather == hail",
|
||||||
|
"tracks": [
|
||||||
|
"audio/hail1.flac"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"signoff 1": {
|
||||||
|
"tracks": [
|
||||||
|
"audio/signoff_01.flac"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"signoff 2": {
|
||||||
|
"tracks": [
|
||||||
|
"audio/signoff_02.flac"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"voices": {
|
||||||
|
"cory": {
|
||||||
|
"directory": "audio/voice/cory/",
|
||||||
|
"extension": "flac"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
115
package-lock.json
generated
Normal file
115
package-lock.json
generated
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
{
|
||||||
|
"name": "morning-report",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "morning-report",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"json5": "2.2.3",
|
||||||
|
"openweathermap-ts": "1.2.10"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "24.3.0",
|
||||||
|
"typescript": "5.9.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "24.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz",
|
||||||
|
"integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~7.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/json5": {
|
||||||
|
"version": "2.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||||
|
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"json5": "lib/cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/node-fetch": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "4.x || >=6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"encoding": "^0.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"encoding": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/openweathermap-ts": {
|
||||||
|
"version": "1.2.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/openweathermap-ts/-/openweathermap-ts-1.2.10.tgz",
|
||||||
|
"integrity": "sha512-Zckv2aXN8ENSeAeroces2jJciLWb6aLNXEmvG6pmF+BcIMw2kwRo6++/AKUNoU5suOp47UWA6lllDV0TNm//OA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"node-fetch": "^2.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "5.9.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
|
||||||
|
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "7.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
|
||||||
|
"integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||||
|
"license": "BSD-2-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
package.json
Normal file
34
package.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "morning-report",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Procedurally generates a radio weather report",
|
||||||
|
"keywords": [
|
||||||
|
"weather",
|
||||||
|
"radio",
|
||||||
|
"audio"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "ssh://git@git.sanin.dev:3689/corysanin/morning-report.git"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"author": {
|
||||||
|
"name": "Cory Sanin",
|
||||||
|
"email": "corysanin@outlook.com",
|
||||||
|
"url": "https://sanin.dev/"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"main": "distribution/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "npx tsc",
|
||||||
|
"start": "node distribution/index.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"json5": "2.2.3",
|
||||||
|
"openweathermap-ts": "1.2.10"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "5.9.2",
|
||||||
|
"@types/node": "24.3.0"
|
||||||
|
}
|
||||||
|
}
|
23
src/index.ts
Normal file
23
src/index.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import path from 'path';
|
||||||
|
import fsp from 'fs/promises';
|
||||||
|
import json5 from 'json5';
|
||||||
|
import Sequencer from './sequencer.js';
|
||||||
|
import type {Programs, Segments, Sequences} from './sequencer.js';
|
||||||
|
import type { Voices } from './voice.js';
|
||||||
|
|
||||||
|
|
||||||
|
interface Config {
|
||||||
|
programs: Programs,
|
||||||
|
segments: Segments,
|
||||||
|
sequences: Sequences,
|
||||||
|
voices: Voices
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('morning-report\nCory Sanin 2025\n');
|
||||||
|
|
||||||
|
const config: Config = json5.parse(await fsp.readFile(process.env['CONFIG'] || path.join('config', 'config.json5'), { encoding: 'utf-8' }));
|
||||||
|
const sequence = Sequencer(config);
|
||||||
|
console.log(sequence.join('\n'));
|
||||||
|
|
||||||
|
|
||||||
|
export type { Config };
|
54
src/sequencer.ts
Normal file
54
src/sequencer.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import type { Config } from './index.js';
|
||||||
|
|
||||||
|
type SegmentName = string;
|
||||||
|
type SequenceName = string;
|
||||||
|
type Programs = SegmentName[][];
|
||||||
|
type Segments = { [segment: SegmentName]: SequenceName[] };
|
||||||
|
type Sequence = {
|
||||||
|
condition?: string;
|
||||||
|
tracks: string[];
|
||||||
|
}
|
||||||
|
type Sequences = { [sequence: SequenceName]: Sequence };
|
||||||
|
|
||||||
|
let config: Config = null;
|
||||||
|
|
||||||
|
function selectOne<T>(arr: T[]): T {
|
||||||
|
return arr[Math.floor(Math.random() * arr.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
function conditionIsMet(condition: string | undefined = undefined): boolean {
|
||||||
|
if (typeof condition !== 'string') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// TODO: parse condition, return bool
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function processSequence(sequence: Sequence): string[] {
|
||||||
|
const tracks = sequence.tracks;
|
||||||
|
// TODO: process voice macros
|
||||||
|
return tracks;
|
||||||
|
}
|
||||||
|
|
||||||
|
function processSegment(segment: SegmentName): string[] {
|
||||||
|
if (!(segment in config.segments)) {
|
||||||
|
return processSequence(config.sequences[segment]);
|
||||||
|
}
|
||||||
|
const potentialSequences: SequenceName[] = config.segments[segment].filter(s => conditionIsMet(config.sequences[s].condition));
|
||||||
|
if (potentialSequences.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return processSequence(config.sequences[selectOne(potentialSequences)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Sequencer(conf: Config): string[] {
|
||||||
|
config = conf;
|
||||||
|
const sequence: string[] = [];
|
||||||
|
const program: SegmentName[] = selectOne(conf.programs);
|
||||||
|
program.forEach(segment => sequence.push(...processSegment(segment)));
|
||||||
|
return sequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Sequencer;
|
||||||
|
export { Sequencer };
|
||||||
|
export type { SegmentName, SequenceName, Programs, Segments, Sequence, Sequences };
|
0
src/stitcher.ts
Normal file
0
src/stitcher.ts
Normal file
10
src/voice.ts
Normal file
10
src/voice.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
|
||||||
|
interface Voice {
|
||||||
|
directory: string;
|
||||||
|
extension: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Voices = { [name: string]: Voice };
|
||||||
|
|
||||||
|
export type { Voice, Voices };
|
0
src/weather.ts
Normal file
0
src/weather.ts
Normal file
17
tsconfig.json
Normal file
17
tsconfig.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": ["ESNext"],
|
||||||
|
"module": "NodeNext",
|
||||||
|
"target": "ESNext",
|
||||||
|
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"moduleResolution": "node16",
|
||||||
|
"sourceMap": true,
|
||||||
|
"inlineSources": true,
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./distribution"
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["**/*.spec.ts"]
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user