@amoshydra/davison 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,7 +24,7 @@ A standalone music server that streams your local music library to Sonos speaker
24
24
  npx davison --path /path/to/music
25
25
  ```
26
26
 
27
- Open http://localhost:3000 in your browser.
27
+ Open http://localhost:4534 in your browser.
28
28
 
29
29
  ### Via Docker
30
30
 
@@ -59,7 +59,7 @@ services:
59
59
  | Option | Description |
60
60
  |--------|-------------|
61
61
  | `-p, --path <paths...>` | One or more music directories (required) |
62
- | `--port <number>` | Server port (default: 3000) |
62
+ | `--port <number>` | Server port (default: 4534) |
63
63
  | `--host <address>` | LAN address (auto-detected if omitted) |
64
64
 
65
65
  ### Web UI
@@ -74,13 +74,22 @@ services:
74
74
 
75
75
  ### WebDAV
76
76
 
77
- The WebDAV server is available at `/webdav` (e.g., `http://192.168.0.x:3000/webdav/`). It mirrors your music folder structure. No authentication required.
77
+ The WebDAV server is available at `/webdav` (e.g., `http://<your-server-ip>:4534/webdav/`). It mirrors your music folder structure.
78
+
79
+ #### Authentication
80
+
81
+ Disabled by default. To require Basic authentication, set the `DAVISON_WEBDAV_USER` and `DAVISON_WEBDAV_PASS` environment variables:
82
+
83
+ ```bash
84
+ DAVISON_WEBDAV_USER=davison DAVISON_WEBDAV_PASS="your-password" npx davison --path /music
85
+ ```
86
+
87
+ Environment variables are recommended over CLI flags because they do not appear in process listings (`ps aux`) or shell history. When authentication is enabled, the server logs `WebDAV auth enabled for user "<username>"` on startup.
78
88
 
79
89
  To use with Music Assistant:
80
90
  1. Add a **WebDAV** provider
81
- 2. URL: `http://<your-server-ip>:3000/webdav/`
82
- 3. Leave username/password blank
83
- 4. Content type: `music`
91
+ 2. URL: `http://<your-server-ip>:4534/webdav/`
92
+ 3. Username/Password: set them if you configured auth, leave blank otherwise
84
93
 
85
94
  ### Sonos
86
95
 
@@ -101,30 +110,30 @@ The dev server hot-reloads on file changes.
101
110
 
102
111
  ```
103
112
  ┌──────────────────────┐
104
- │ Web Browser
105
- │ (React + Vite)
113
+ │ Web Browser
114
+ │ (React + Vite)
106
115
  └──────────┬───────────┘
107
- │ HTTP (port 3000)
116
+ │ HTTP (port 4534)
108
117
  ┌──────────▼───────────┐
109
- │ Express Server
110
- │ ┌─────────────────┐
111
- │ │ REST API (/api) │
112
- │ │ Queue Manager │
113
- │ │ Sonos Controller│
114
- │ │ Playlist Store │
115
- │ ├─────────────────┤
116
- │ │ WebDAV (/webdav)│
117
- │ │ (Nephele VFS) │
118
- │ └─────────────────┘
118
+ │ Express Server
119
+ │ ┌─────────────────┐
120
+ │ │ REST API (/api) │
121
+ │ │ Queue Manager │
122
+ │ │ Sonos Controller│
123
+ │ │ Playlist Store │
124
+ │ ├─────────────────┤
125
+ │ │ WebDAV (/webdav)│
126
+ │ │ (Nephele VFS) │
127
+ │ └─────────────────┘
119
128
  └──────────┬───────────┘
120
129
 
121
130
  ┌───────────────────┼───────────────────┐
122
131
  │ │ │
123
- ┌──────▼──────┐ ┌──────▼──────┐ ┌───────▼──────┐
124
- Sonos │ │ Music Files │ │ Music │
125
- Speakers │ │ (on disk) │ │ Assistant │
126
- (UPnP/HTTP) │ │ │ │ (via WebDAV) │
127
- └─────────────┘ └─────────────┘ └──────────────┘
132
+ ┌──────▼──────┐ ┌──────▼──────┐ ┌───────▼──────┐
133
+ Sonos │ │ Music Files │ │ Music │
134
+ Speakers │ │ (on disk) │ │ Assistant │
135
+ (UPnP/HTTP) │ │ │ │ (via WebDAV) │
136
+ └─────────────┘ └─────────────┘ └──────────────┘
128
137
  ```
129
138
 
130
139
  ## License
@@ -16,9 +16,11 @@ function detectLanIp() {
16
16
  return '127.0.0.1';
17
17
  }
18
18
  export const config = {
19
- port: 3000,
19
+ port: 4534,
20
20
  host: detectLanIp(),
21
21
  musicPaths: [],
22
+ webdavUser: '',
23
+ webdavPass: '',
22
24
  dataDir: path.resolve(import.meta.dirname, '..', 'data'),
23
25
  playlistsFile: path.resolve(import.meta.dirname, '..', 'data', 'playlists.json'),
24
26
  };
@@ -1,6 +1,6 @@
1
1
  import express from 'express';
2
2
  import ViteExpress from 'vite-express';
3
- import { resolve, dirname } from 'node:path';
3
+ import { resolve, dirname, isAbsolute } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { Command } from 'commander';
6
6
  import { config } from './config.js';
@@ -14,14 +14,20 @@ program
14
14
  .name('davison')
15
15
  .description('Sonos music player with web UI')
16
16
  .option('-p, --path <paths...>', 'Path(s) to music directories')
17
- .option('--port <number>', 'Server port', '3000')
17
+ .option('--port <number>', 'Server port', '4534')
18
18
  .option('--host <address>', 'Server LAN address (auto-detected if omitted)')
19
19
  .parse(process.argv);
20
20
  const options = program.opts();
21
- config.port = parseInt(options.port, 10) || 3000;
21
+ config.port = parseInt(options.port, 10) || 4534;
22
22
  if (options.host)
23
23
  config.host = options.host;
24
24
  config.musicPaths = options.path || [];
25
+ // WebDAV credentials from environment (not CLI — avoids ps leak)
26
+ config.webdavUser = process.env.DAVISON_WEBDAV_USER || '';
27
+ config.webdavPass = process.env.DAVISON_WEBDAV_PASS || '';
28
+ // Resolve relative paths against original cwd before main() may chdir
29
+ const originalCwd = process.cwd();
30
+ config.musicPaths = config.musicPaths.map((p) => isAbsolute(p) ? p : resolve(originalCwd, p));
25
31
  config.musicPaths.forEach(p => {
26
32
  if (p.includes(' ')) {
27
33
  console.warn(`Warning: path "${p}" contains spaces — ensure it was quoted in the shell`);
@@ -1,9 +1,10 @@
1
1
  import createServer from 'nephele';
2
- import { ResourceNotFoundError, BadGatewayError, ForbiddenError } from 'nephele/dist/Errors/index.js';
2
+ import { ResourceNotFoundError, BadGatewayError, ForbiddenError, UnauthorizedError } from 'nephele/dist/Errors/index.js';
3
3
  import { createReadStream } from 'node:fs';
4
4
  import { stat } from 'node:fs/promises';
5
5
  import path from 'node:path';
6
6
  import { getMusicLibrary } from './music-discovery.js';
7
+ import { config } from '../config.js';
7
8
  import os from 'node:os';
8
9
  import mime from 'mime';
9
10
  function buildDirs(tracks) {
@@ -369,13 +370,25 @@ export function initWebdav() {
369
370
  const tracks = getMusicLibrary();
370
371
  const tree = buildDirs(tracks);
371
372
  const adapter = new VirtualAdapter(tree);
372
- class NoAuth {
373
- async authenticate() { return { username: 'sonos' }; }
373
+ if (config.webdavUser) {
374
+ console.log(`WebDAV auth enabled for user "${config.webdavUser}"`);
375
+ }
376
+ class WebdavAuth {
377
+ async authenticate(request) {
378
+ if (config.webdavUser) {
379
+ const auth = request.headers.authorization || '';
380
+ const expected = 'Basic ' + Buffer.from(`${config.webdavUser}:${config.webdavPass}`).toString('base64');
381
+ if (auth !== expected) {
382
+ throw new UnauthorizedError();
383
+ }
384
+ }
385
+ return { username: config.webdavUser || 'davison' };
386
+ }
374
387
  async cleanAuthentication() { }
375
388
  }
376
389
  _webdavApp = createServer({
377
390
  adapter: adapter,
378
- authenticator: new NoAuth(),
391
+ authenticator: new WebdavAuth(),
379
392
  });
380
393
  return _webdavApp;
381
394
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amoshydra/davison",
3
- "version": "0.0.3",
3
+ "version": "0.1.0",
4
4
  "description": "WebDAV music server + Sonos controller. npx @amoshydra/davison --path /music",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,7 +23,8 @@
23
23
  "patches/",
24
24
  "package.json",
25
25
  "README.md",
26
- "LICENSE"
26
+ "LICENSE",
27
+ "vite.config.ts"
27
28
  ],
28
29
  "dependencies": {
29
30
  "commander": "^12.1.0",
@@ -32,7 +33,6 @@
32
33
  "music-metadata": "^10.6.0",
33
34
  "nephele": "1.0.0-alpha.67",
34
35
  "sonos": "^1.14.3",
35
- "uuid": "^10.0.0",
36
36
  "vite-express": "^0.22.1"
37
37
  },
38
38
  "devDependencies": {
package/vite.config.ts ADDED
@@ -0,0 +1,9 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ build: {
7
+ outDir: 'dist',
8
+ },
9
+ })