@amoshydra/davison 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 amoshydra
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # davison
2
+
3
+ A standalone music server that streams your local music library to Sonos speakers and serves it via WebDAV for tools like Music Assistant.
4
+
5
+ ## Features
6
+
7
+ - **Sonos control** — discover Sonos speakers on your LAN, stream music directly to them
8
+ - **Mobile-first web UI** — browse your music library by folder, control playback, manage queues and playlists
9
+ - **WebDAV server** — expose your music library over WebDAV for Music Assistant, file managers, or any WebDAV client
10
+ - **Direct streaming** — HTTP range-request streaming with album art extraction
11
+ - **Zero configuration** — point it at your music folders and go
12
+ - **No cloud dependency** — everything runs on your local network
13
+
14
+ ## Quick Start
15
+
16
+ ### Prerequisites
17
+
18
+ - Node.js 22+ (or Docker)
19
+ - Music files on a local or mounted drive
20
+
21
+ ### Via npx
22
+
23
+ ```bash
24
+ npx davison --path /path/to/music
25
+ ```
26
+
27
+ Open http://localhost:3000 in your browser.
28
+
29
+ ### Via Docker
30
+
31
+ ```bash
32
+ docker run -d \
33
+ --name davison \
34
+ --network host \
35
+ -v /path/to/music:/music:ro \
36
+ ghcr.io/your-username/davison \
37
+ --path /music
38
+ ```
39
+
40
+ > `--network host` is required for Sonos UPnP discovery to work.
41
+
42
+ ### Via Docker Compose
43
+
44
+ ```yaml
45
+ version: "3.8"
46
+ services:
47
+ davison:
48
+ image: ghcr.io/your-username/davison
49
+ network_mode: host
50
+ volumes:
51
+ - /path/to/music:/music:ro
52
+ command: ["--path", "/music"]
53
+ ```
54
+
55
+ ## Usage
56
+
57
+ ### Command-line options
58
+
59
+ | Option | Description |
60
+ |--------|-------------|
61
+ | `-p, --path <paths...>` | One or more music directories (required) |
62
+ | `--port <number>` | Server port (default: 3000) |
63
+ | `--host <address>` | LAN address (auto-detected if omitted) |
64
+
65
+ ### Web UI
66
+
67
+ | Tab | Description |
68
+ |-----|-------------|
69
+ | **Library** | Browse music by folder structure |
70
+ | **Queue** | View and manage the current playback queue |
71
+ | **Now Playing** | Full-screen player with album art, vinyl animation, swipe gestures |
72
+ | **Playlists** | Create, edit, and load playlists |
73
+ | **Settings** | Discover and select Sonos devices |
74
+
75
+ ### WebDAV
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.
78
+
79
+ To use with Music Assistant:
80
+ 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`
84
+
85
+ ### Sonos
86
+
87
+ davison discovers Sonos speakers on your LAN automatically via SSDP multicast. Select a speaker from the **Settings** tab and start playing.
88
+
89
+ ## Development
90
+
91
+ ```bash
92
+ git clone https://github.com/your-username/davison.git
93
+ cd davison
94
+ pnpm install
95
+ pnpm run dev -- --path /path/to/music
96
+ ```
97
+
98
+ The dev server hot-reloads on file changes.
99
+
100
+ ## Architecture
101
+
102
+ ```
103
+ ┌──────────────────────┐
104
+ │ Web Browser │
105
+ │ (React + Vite) │
106
+ └──────────┬───────────┘
107
+ │ HTTP (port 3000)
108
+ ┌──────────▼───────────┐
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
+ │ └─────────────────┘ │
119
+ └──────────┬───────────┘
120
+
121
+ ┌───────────────────┼───────────────────┐
122
+ │ │ │
123
+ ┌──────▼──────┐ ┌──────▼──────┐ ┌───────▼──────┐
124
+ │ Sonos │ │ Music Files │ │ Music │
125
+ │ Speakers │ │ (on disk) │ │ Assistant │
126
+ │ (UPnP/HTTP) │ │ │ │ (via WebDAV) │
127
+ └─────────────┘ └─────────────┘ └──────────────┘
128
+ ```
129
+
130
+ ## License
131
+
132
+ MIT
package/bin/davison.js ADDED
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+
3
+ // `npx @amoshydra/davison` entry point.
4
+ import { spawn, execSync } from 'node:child_process'
5
+ import { fileURLToPath } from 'node:url'
6
+ import { dirname, resolve } from 'node:path'
7
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs'
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url))
10
+ const root = resolve(__dirname, '..')
11
+
12
+ // Apply the Nephele HEAD Content-Length patch if not already applied
13
+ const getHeadPath = resolve(root, 'node_modules', 'nephele', 'dist', 'Methods', 'GET_HEAD.js')
14
+ if (existsSync(getHeadPath)) {
15
+ const content = readFileSync(getHeadPath, 'utf8')
16
+ if (!content.includes("'Content-Length':")) {
17
+ const patchPath = resolve(root, 'patches', 'nephele.patch')
18
+ if (existsSync(patchPath)) {
19
+ try {
20
+ execSync(`patch -p0 < "${patchPath}"`, { cwd: resolve(root, 'node_modules', 'nephele'), stdio: 'pipe' })
21
+ } catch {
22
+ // patch wasn't applied, server still works
23
+ }
24
+ }
25
+ }
26
+ }
27
+ }
28
+ }
29
+ }
30
+
31
+ // Auto-build if needed (first run from git clone)
32
+ if (!existsSync(resolve(root, 'dist-server', 'index.js'))) {
33
+ const viteBin = resolve(root, 'node_modules', '.bin', 'vite')
34
+ const tscBin = resolve(root, 'node_modules', '.bin', 'tsc')
35
+ if (existsSync(viteBin) && existsSync(tscBin)) {
36
+ console.log('Building server and frontend...')
37
+ try {
38
+ execSync(`"${tscBin}" -p "${root}/tsconfig.server.json"`, { cwd: root, stdio: 'inherit' })
39
+ execSync(`"${viteBin}" build`, { cwd: root, stdio: 'inherit' })
40
+ } catch {
41
+ console.warn('Build failed — falling back to tsx source')
42
+ }
43
+ }
44
+ }
45
+
46
+ // Try compiled server first, fall back to tsx source
47
+ let entryPoint = resolve(root, 'dist-server', 'index.js')
48
+ if (!existsSync(entryPoint)) {
49
+ const tsxLoader = resolve(root, 'node_modules', 'tsx', 'dist', 'loader.mjs')
50
+ if (existsSync(tsxLoader)) {
51
+ entryPoint = resolve(root, 'server', 'index.ts')
52
+ spawn(process.execPath, ['--import', tsxLoader, entryPoint, ...process.argv.slice(2)], {
53
+ stdio: 'inherit',
54
+ env: { ...process.env, NODE_ENV: process.env.NODE_ENV || 'production' },
55
+ }).on('exit', (code) => process.exit(code ?? 1))
56
+ } else {
57
+ console.error('Server not built. Run: npm run build')
58
+ process.exit(1)
59
+ }
60
+ } else {
61
+ spawn(process.execPath, [entryPoint, ...process.argv.slice(2)], {
62
+ stdio: 'inherit',
63
+ env: { ...process.env, NODE_ENV: process.env.NODE_ENV || 'production' },
64
+ }).on('exit', (code) => process.exit(code ?? 1))
65
+ }