stagelinq 1.0.7 → 3.0.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.
Files changed (194) hide show
  1. package/README.md +194 -50
  2. package/dist/Databases/Databases.d.ts +19 -0
  3. package/dist/Databases/Databases.js +106 -0
  4. package/dist/Databases/Databases.js.map +1 -0
  5. package/dist/Databases/DbConnection.d.ts +22 -0
  6. package/dist/Databases/DbConnection.js +48 -0
  7. package/dist/Databases/DbConnection.js.map +1 -0
  8. package/dist/Databases/index.d.ts +2 -0
  9. package/dist/Databases/index.js +19 -0
  10. package/dist/Databases/index.js.map +1 -0
  11. package/dist/LogEmitter/index.d.ts +20 -0
  12. package/dist/LogEmitter/index.js +35 -0
  13. package/dist/LogEmitter/index.js.map +1 -0
  14. package/dist/StageLinq/index.d.ts +131 -0
  15. package/dist/StageLinq/index.js +237 -0
  16. package/dist/StageLinq/index.js.map +1 -0
  17. package/dist/cli/beatinfo.d.ts +11 -0
  18. package/dist/cli/beatinfo.js +84 -0
  19. package/dist/cli/beatinfo.js.map +1 -0
  20. package/dist/cli/discover.d.ts +9 -0
  21. package/dist/cli/discover.js +97 -0
  22. package/dist/cli/discover.js.map +1 -0
  23. package/dist/cli/index.d.ts +1 -0
  24. package/dist/cli/index.js +197 -0
  25. package/dist/cli/index.js.map +1 -0
  26. package/dist/config.d.ts +31 -0
  27. package/dist/config.js +32 -0
  28. package/dist/config.js.map +1 -0
  29. package/dist/devices/Player.d.ts +63 -0
  30. package/dist/devices/Player.js +189 -0
  31. package/dist/devices/Player.js.map +1 -0
  32. package/dist/devices/PlayerMessageQueue.d.ts +36 -0
  33. package/dist/devices/PlayerMessageQueue.js +56 -0
  34. package/dist/devices/PlayerMessageQueue.js.map +1 -0
  35. package/dist/eaas/beacon.d.ts +72 -0
  36. package/dist/eaas/beacon.js +161 -0
  37. package/dist/eaas/beacon.js.map +1 -0
  38. package/dist/eaas/discoverer.d.ts +89 -0
  39. package/dist/eaas/discoverer.js +225 -0
  40. package/dist/eaas/discoverer.js.map +1 -0
  41. package/dist/eaas/engine-library-client.d.ts +120 -0
  42. package/dist/eaas/engine-library-client.js +204 -0
  43. package/dist/eaas/engine-library-client.js.map +1 -0
  44. package/dist/eaas/grpc-types.d.ts +319 -0
  45. package/dist/eaas/grpc-types.js +90 -0
  46. package/dist/eaas/grpc-types.js.map +1 -0
  47. package/dist/eaas/http-client.d.ts +75 -0
  48. package/dist/eaas/http-client.js +139 -0
  49. package/dist/eaas/http-client.js.map +1 -0
  50. package/dist/eaas/index.d.ts +33 -0
  51. package/dist/eaas/index.js +50 -0
  52. package/dist/eaas/index.js.map +1 -0
  53. package/dist/eaas/messages.d.ts +43 -0
  54. package/dist/eaas/messages.js +186 -0
  55. package/dist/eaas/messages.js.map +1 -0
  56. package/dist/eaas/types.d.ts +64 -0
  57. package/dist/eaas/types.js +25 -0
  58. package/dist/eaas/types.js.map +1 -0
  59. package/{index.ts → dist/index.d.ts} +2 -0
  60. package/dist/index.js +48 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/network/ConnectionHealth.d.ts +121 -0
  63. package/dist/network/ConnectionHealth.js +216 -0
  64. package/dist/network/ConnectionHealth.js.map +1 -0
  65. package/dist/network/NetworkDevice.d.ts +40 -0
  66. package/dist/network/NetworkDevice.js +274 -0
  67. package/dist/network/NetworkDevice.js.map +1 -0
  68. package/dist/network/StageLinqDevices.d.ts +91 -0
  69. package/dist/network/StageLinqDevices.js +276 -0
  70. package/dist/network/StageLinqDevices.js.map +1 -0
  71. package/dist/network/StageLinqListener.d.ts +15 -0
  72. package/dist/network/StageLinqListener.js +58 -0
  73. package/dist/network/StageLinqListener.js.map +1 -0
  74. package/dist/network/announce.d.ts +25 -0
  75. package/dist/network/announce.js +260 -0
  76. package/dist/network/announce.js.map +1 -0
  77. package/dist/network/index.d.ts +5 -0
  78. package/dist/network/index.js +22 -0
  79. package/dist/network/index.js.map +1 -0
  80. package/dist/services/BeatInfo.d.ts +71 -0
  81. package/dist/services/BeatInfo.js +128 -0
  82. package/dist/services/BeatInfo.js.map +1 -0
  83. package/dist/services/Broadcast.d.ts +47 -0
  84. package/dist/services/Broadcast.js +88 -0
  85. package/dist/services/Broadcast.js.map +1 -0
  86. package/dist/services/FileTransfer.d.ts +30 -0
  87. package/dist/services/FileTransfer.js +283 -0
  88. package/dist/services/FileTransfer.js.map +1 -0
  89. package/dist/services/Service.d.ts +22 -0
  90. package/dist/services/Service.js +178 -0
  91. package/dist/services/Service.js.map +1 -0
  92. package/dist/services/StateMap.d.ts +20 -0
  93. package/dist/services/StateMap.js +149 -0
  94. package/dist/services/StateMap.js.map +1 -0
  95. package/dist/services/TimeSync.d.ts +57 -0
  96. package/dist/services/TimeSync.js +136 -0
  97. package/dist/services/TimeSync.js.map +1 -0
  98. package/{services/index.ts → dist/services/index.d.ts} +3 -0
  99. package/dist/services/index.js +23 -0
  100. package/dist/services/index.js.map +1 -0
  101. package/dist/tsconfig.tsbuildinfo +1 -0
  102. package/dist/types/common.d.ts +236 -0
  103. package/dist/types/common.js +243 -0
  104. package/dist/types/common.js.map +1 -0
  105. package/dist/types/index.d.ts +46 -0
  106. package/dist/types/index.js +21 -0
  107. package/dist/types/index.js.map +1 -0
  108. package/dist/types/models/Track.d.ts +50 -0
  109. package/dist/types/models/Track.js +3 -0
  110. package/dist/types/models/Track.js.map +1 -0
  111. package/dist/types/models/index.d.ts +1 -0
  112. package/dist/types/models/index.js +18 -0
  113. package/dist/types/models/index.js.map +1 -0
  114. package/dist/types/player.d.ts +40 -0
  115. package/dist/types/player.js +3 -0
  116. package/dist/types/player.js.map +1 -0
  117. package/dist/types/tokens.d.ts +10 -0
  118. package/dist/types/tokens.js +36 -0
  119. package/dist/types/tokens.js.map +1 -0
  120. package/dist/utils/Context.d.ts +13 -0
  121. package/dist/utils/Context.js +37 -0
  122. package/dist/utils/Context.js.map +1 -0
  123. package/dist/utils/ReadContext.d.ts +15 -0
  124. package/dist/utils/ReadContext.js +110 -0
  125. package/dist/utils/ReadContext.js.map +1 -0
  126. package/dist/utils/WriteContext.d.ts +22 -0
  127. package/dist/utils/WriteContext.js +87 -0
  128. package/dist/utils/WriteContext.js.map +1 -0
  129. package/dist/utils/getTempFilePath.d.ts +7 -0
  130. package/dist/utils/getTempFilePath.js +59 -0
  131. package/dist/utils/getTempFilePath.js.map +1 -0
  132. package/dist/utils/hex.d.ts +1 -0
  133. package/dist/utils/hex.js +55 -0
  134. package/dist/utils/hex.js.map +1 -0
  135. package/{utils/index.ts → dist/utils/index.d.ts} +2 -0
  136. package/dist/utils/index.js +26 -0
  137. package/dist/utils/index.js.map +1 -0
  138. package/dist/utils/log.js +2 -0
  139. package/dist/utils/log.js.map +1 -0
  140. package/dist/utils/sleep.d.ts +1 -0
  141. package/dist/utils/sleep.js +7 -0
  142. package/dist/utils/sleep.js.map +1 -0
  143. package/dist/utils/tcp.d.ts +4 -0
  144. package/dist/utils/tcp.js +18 -0
  145. package/dist/utils/tcp.js.map +1 -0
  146. package/dist/utils/token.d.ts +40 -0
  147. package/dist/utils/token.js +74 -0
  148. package/dist/utils/token.js.map +1 -0
  149. package/dist/utils/trackPath.d.ts +77 -0
  150. package/dist/utils/trackPath.js +131 -0
  151. package/dist/utils/trackPath.js.map +1 -0
  152. package/dist/vitest.config.d.ts +2 -0
  153. package/dist/vitest.config.js +24 -0
  154. package/dist/vitest.config.js.map +1 -0
  155. package/package.json +41 -11
  156. package/.gitattributes +0 -2
  157. package/.prettierignore +0 -7
  158. package/.prettierrc.js +0 -10
  159. package/.vscode/launch.json +0 -40
  160. package/.vscode/tasks.json +0 -24
  161. package/Databases/Databases.ts +0 -89
  162. package/Databases/DbConnection.ts +0 -51
  163. package/Databases/index.ts +0 -2
  164. package/LogEmitter/index.ts +0 -51
  165. package/StageLinq/index.ts +0 -59
  166. package/TODO.md +0 -5
  167. package/cli/index.ts +0 -187
  168. package/devices/Player.ts +0 -236
  169. package/devices/PlayerMessageQueue.ts +0 -63
  170. package/network/NetworkDevice.ts +0 -302
  171. package/network/StageLinqDevices.ts +0 -309
  172. package/network/StageLinqListener.ts +0 -50
  173. package/network/announce.ts +0 -133
  174. package/network/index.ts +0 -4
  175. package/network-path-prototype.patch +0 -181
  176. package/services/FileTransfer.ts +0 -303
  177. package/services/Service.ts +0 -144
  178. package/services/StateMap.ts +0 -170
  179. package/stagelinq.code-workspace +0 -16
  180. package/tsconfig.json +0 -13
  181. package/types/common.ts +0 -240
  182. package/types/index.ts +0 -53
  183. package/types/models/Track.ts +0 -50
  184. package/types/models/index.ts +0 -1
  185. package/types/player.ts +0 -43
  186. package/types/tokens.ts +0 -39
  187. package/utils/Context.ts +0 -45
  188. package/utils/ReadContext.ts +0 -114
  189. package/utils/WriteContext.ts +0 -105
  190. package/utils/getTempFilePath.ts +0 -23
  191. package/utils/hex.ts +0 -62
  192. package/utils/sleep.ts +0 -3
  193. package/utils/tcp.ts +0 -17
  194. /package/{utils/log.ts → dist/utils/log.d.ts} +0 -0
package/README.md CHANGED
@@ -1,83 +1,227 @@
1
1
  # StageLinq
2
2
 
3
- NodeJS library implementation to access information through the Denon StageLinq protocol.
3
+ TypeScript library to connect to Denon DJ hardware via the StageLinq protocol.
4
4
 
5
- # Features
5
+ ## Installation
6
6
 
7
- * Tested with Denon two SC6000s, X1850, Prime 4, Prime 2, and Prime Go.
8
- * Event emitters for state changes, tracks getting loaded, and current playing track.
9
- * Event emitter for debug logging.
10
- * Downloads source databases for you.
11
- * You can implement handling the database yourself or use this library's BetterSqlite3 dependency.
7
+ ```bash
8
+ npm install stagelinq
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```ts
14
+ import { StageLinq } from 'stagelinq';
15
+
16
+ const stagelinq = new StageLinq({ downloadDbSources: true });
17
+
18
+ stagelinq.devices.on('trackLoaded', (status) => {
19
+ console.log(`${status.title} - ${status.artist} loaded on deck ${status.deck}`);
20
+ });
21
+
22
+ stagelinq.devices.on('nowPlaying', (status) => {
23
+ console.log(`Now Playing: ${status.title} - ${status.artist}`);
24
+ });
12
25
 
13
- ---
26
+ await stagelinq.connect();
27
+ ```
14
28
 
15
- ## Usage
29
+ ## Features
30
+
31
+ | Feature | Go | Python | TS v2 |
32
+ |---------|:--:|:------:|:-----:|
33
+ | Device Discovery | ✅ | ✅ | ✅ |
34
+ | StateMap Service | ✅ | ✅ | ✅ |
35
+ | BeatInfo Service | ✅ | ❌ | ✅ |
36
+ | FileTransfer Service | ✅ | ❌ | ✅ |
37
+ | TimeSync Service | ✅ | ❌ | ✅ |
38
+ | Broadcast Service | ✅ | ❌ | ✅ |
39
+ | EAAS (gRPC library) | ✅ | ❌ | ✅ |
40
+ | EAAS (HTTP files) | ✅ | ❌ | ✅ |
41
+ | Windows Support | ✅ | ✅ | ✅ |
42
+ | macOS Support | ✅ | ❌ | ✅ |
43
+ | Linux Support | ✅ | ✅ | ✅ |
44
+ | Multi-interface discovery | ✅ | ✅ | ✅ |
45
+ | Database download | ✅ | ❌ | ✅ |
46
+ | Track path resolution | ❌ | ❌ | ✅ |
47
+ | Instance-based API | ✅ | ✅ | ✅ |
48
+ | Static class API | ❌ | ❌ | ✅ |
49
+ | CLI demo tools | ✅ | ✅ | ✅ |
50
+ | Wireshark dissector | ❌ | ✅ | ✅ |
51
+ | Unit tests | ✅ | ✅ | ✅ |
52
+
53
+ *Go = [go-stagelinq](https://github.com/icedream/go-stagelinq), Python = [PyStageLinQ](https://github.com/Jaxc/PyStageLinQ)*
54
+
55
+ ## Supported Devices
56
+
57
+ - Denon SC6000 / SC6000M
58
+ - Denon SC5000 / SC5000M
59
+ - Denon Prime 4 / Prime 2 / Prime Go
60
+ - Denon X1850 / X1800 mixers
61
+ - Denon LC6000
62
+
63
+ ## API
64
+
65
+ ### Instance-based (recommended)
16
66
 
17
67
  ```ts
18
- import { StageLinq } from '../StageLinq';
68
+ import { StageLinq } from 'stagelinq';
69
+
70
+ const stagelinq = new StageLinq({
71
+ downloadDbSources: true,
72
+ maxRetries: 3,
73
+ });
19
74
 
20
- const options = { downloadDbSources: true };
21
- const stageLinq = new StageLinq(options);
75
+ // Device events
76
+ stagelinq.devices.on('ready', (info) => {
77
+ console.log(`Connected to ${info.software.name}`);
78
+ });
79
+
80
+ stagelinq.devices.on('trackLoaded', (status) => {
81
+ console.log(`Loaded: ${status.title} on deck ${status.deck}`);
82
+ });
22
83
 
23
- stageLinq.devices.on('ready', (connectionInfo) => {
24
- console.log(`Device ${connectionInfo.software.name} on ` +
25
- `${connectionInfo.address}:${connectionInfo.port} is ready.`);
84
+ stagelinq.devices.on('nowPlaying', (status) => {
85
+ console.log(`Playing: ${status.title}`);
26
86
  });
27
87
 
28
- stageLinq.devices.on('trackLoaded', (status) => {
29
- console.log(`"${status.title}" - ${status.artist} loaded on player ` +
30
- `${status.deck})`);
88
+ // Database events
89
+ stagelinq.devices.on('dbDownloaded', (sourceId, dbPath) => {
90
+ console.log(`Database saved to ${dbPath}`);
31
91
  });
32
92
 
33
- stageLinq.devices.on('nowPlaying', (status) => {
34
- console.log(`Now Playing: "${status.title}" - ${status.artist})`);
93
+ await stagelinq.connect();
94
+
95
+ // Later...
96
+ await stagelinq.disconnect();
97
+ ```
98
+
99
+ ### Static class
100
+
101
+ ```ts
102
+ import { StageLinq } from 'stagelinq';
103
+
104
+ StageLinq.options = { downloadDbSources: true };
105
+
106
+ StageLinq.devices.on('nowPlaying', (status) => {
107
+ console.log(`Playing: ${status.title}`);
35
108
  });
109
+
110
+ await StageLinq.connect();
36
111
  ```
37
112
 
38
- A [complete example](https://github.com/chrisle/StageLinq/blob/main/cli/index.ts) with all events and options can be found in the CLI.
113
+ ## Events
39
114
 
40
- ---
115
+ ```ts
116
+ // Track events
117
+ stagelinq.devices.on('trackLoaded', (status: PlayerStatus) => {});
118
+ stagelinq.devices.on('nowPlaying', (status: PlayerStatus) => {});
41
119
 
42
- ## Overview
120
+ // Device events
121
+ stagelinq.devices.on('ready', (info: ConnectionInfo) => {});
122
+ stagelinq.devices.on('deviceConnected', (info: ConnectionInfo) => {});
43
123
 
44
- The idea behind this library is to have a structure something like this:
124
+ // State events (200+ states per deck)
125
+ stagelinq.devices.on('stateChanged', (state: StateData) => {});
45
126
 
46
- **StageLinq > Devices > Player > Deck**
127
+ // Beat events (real-time BPM, beat position)
128
+ stagelinq.devices.on('beatMessage', (data: BeatData) => {});
129
+
130
+ // Database events
131
+ stagelinq.devices.on('dbDownloaded', (sourceId, dbPath) => {});
132
+ ```
133
+
134
+ **[View all events →](docs/events.md)**
135
+
136
+ ## State Data
137
+
138
+ The library tracks 200+ state values per deck:
139
+
140
+ | Category | Example States |
141
+ |----------|----------------|
142
+ | **Track** | `ArtistName`, `SongName`, `TrackNetworkPath`, `TrackLength` |
143
+ | **Playback** | `Play`, `PlayState`, `CurrentBPM`, `Speed` |
144
+ | **Sync** | `SyncMode`, `DeckIsMaster`, `MasterTempo` |
145
+ | **Loop** | `LoopEnableState`, `CurrentLoopSizeInBeats` |
146
+ | **Mixer** | `ExternalMixerVolume`, `CrossfaderPosition` |
147
+
148
+ **[View all states →](docs/events.md#full-state-list)**
149
+
150
+ ## Examples
151
+
152
+ See the [cli/](cli/) folder for complete examples:
153
+
154
+ - [`cli/index.ts`](cli/index.ts) - Full example with all events
155
+ - [`cli/discover.ts`](cli/discover.ts) - Network discovery demo
156
+ - [`cli/beatinfo.ts`](cli/beatinfo.ts) - Real-time beat information
157
+
158
+ Run examples:
159
+
160
+ ```bash
161
+ # Main CLI
162
+ npx ts-node cli/index.ts
163
+
164
+ # Discovery
165
+ npx ts-node cli/discover.ts
166
+
167
+ # Beat info
168
+ npx ts-node cli/beatinfo.ts
169
+ ```
170
+
171
+ ## Tools
172
+
173
+ ### Wireshark Dissector
174
+
175
+ A Lua dissector for Wireshark is included for protocol debugging:
176
+
177
+ ```bash
178
+ # Copy to Wireshark plugins folder
179
+ cp tools/wireshark/stagelinq.lua ~/.local/lib/wireshark/plugins/
180
+ ```
181
+
182
+ ## Testing
183
+
184
+ ```bash
185
+ npm test # Run tests
186
+ npm run test:watch # Watch mode
187
+ npm run test:coverage # Coverage report
188
+ ```
47
189
 
48
- A StageLinq sets up a device listener and a class that handles all the
49
- devices (`StageLinqDevices`).
190
+ **205 tests** across 14 test files.
50
191
 
51
- `StageLinqDevices` figures out if it wants to connect or not and handles
52
- connections. There may be one or more device on the network. For each device it
53
- will try to connect to it and subscribe to it's `StateMap`.
192
+ ## Documentation
54
193
 
55
- Currently there is only one type of device: `Player`. A `Player` may have up to
56
- 4 decks A, B, C, D (aka "layers"). The `Player` handles incoming messages,
57
- parses them, groups them, and emits events. These events bubble up to the
58
- `Device`.
194
+ ### Guides
195
+ - [Events Reference](docs/events.md) - All events and state data
196
+ - [Protocol Specification](docs/protocol.md) - StageLinq protocol details
59
197
 
60
- ## Database
198
+ ### Services
199
+ - [StateMap](docs/statemap.md) - Track state, playback, faders
200
+ - [BeatInfo](docs/beatinfo.md) - Real-time beat and tempo
201
+ - [FileTransfer](docs/filetransfer.md) - Database and file downloads
202
+ - [EAAS](docs/eaas.md) - Engine Library gRPC/HTTP access
203
+ - [Connection Health](docs/connection-health.md) - Auto-reconnection
61
204
 
62
- You can use BetterSqlite3 bundled into this library or let this library
63
- download the files for you, then choose your own Sqlite library to
64
- query the database. See CLI example.
205
+ ### Reference
206
+ - [CHANGELOG](CHANGELOG.md) - Version history
65
207
 
66
- ## Logging
208
+ ## Contributors
67
209
 
68
- I needed the logging to be used outside of the library so I made them events
69
- that you can listen to.
210
+ - **Chris Le** ([@chrisle](https://github.com/chrisle)) - Maintainer
211
+ - **Martijn Reuvers** - Core development
212
+ - **MarByteBeep** ([@MarByteBeep](https://github.com/MarByteBeep)) - Original TypeScript implementation
213
+ - **honusz** - BeatInfo, TimeSync, Broadcast services
214
+ - **Kalle Kirjalainen** - Contributions
215
+ - **docBliny** - Contributions
70
216
 
71
- * `error`: When something bad happens.
72
- * `warn`: When something happens but doesn't affect anything.
73
- * `info`/`log`: When we have something to say
74
- * `debug`: Spits out the parsed version of the packets.
75
- * `silly`: Dumps all kinds of internal stuff
217
+ ## Attribution
76
218
 
77
- ## About
219
+ This library incorporates code and ideas from other StageLinq implementations:
78
220
 
79
- Forked from @MarByteBeep's code.
221
+ - **[go-stagelinq](https://github.com/icedream/go-stagelinq)** by Carl Kittelberger (icedream) - EAAS, Windows fix, token utilities
222
+ - **[PyStageLinQ](https://github.com/Jaxc/PyStageLinQ)** by Jaxc - Wireshark dissector, protocol documentation
223
+ - **[kyleawayan/StageLinq](https://github.com/kyleawayan/StageLinq)** by Kyle Awayan - Track path resolution fixes
80
224
 
81
- Additional reverse engineering work: https://github.com/chrisle/stagelinq-pcap
225
+ ## License
82
226
 
83
- Used in my app Now Playing https://www.nowplaying2.com
227
+ GPL-3.0
@@ -0,0 +1,19 @@
1
+ import { ConnectionInfo, Source } from '../types';
2
+ import { EventEmitter } from 'stream';
3
+ import { FileTransfer } from '../services';
4
+ import { NetworkDevice } from '../network';
5
+ export declare interface Databases {
6
+ on(event: 'dbDownloaded', listener: (sourceName: string, dbPath: string) => void): this;
7
+ on(event: 'dbDownloading', listener: (sourceName: string, dbPath: string) => void): this;
8
+ on(event: 'dbProgress', listener: (sourceName: string, total: number, bytesDownloaded: number, percentComplete: number) => void): this;
9
+ }
10
+ export declare class Databases extends EventEmitter {
11
+ sources: Map<string, string>;
12
+ constructor();
13
+ downloadSourcesFromDevice(connectionInfo: ConnectionInfo, networkDevice: NetworkDevice): Promise<string[]>;
14
+ /**
15
+ * Download databases from this network source.
16
+ */
17
+ downloadDb(sourceId: string, service: FileTransfer, source: Source): Promise<void>;
18
+ getDbPath(dbSourceName?: string): string;
19
+ }
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.Databases = void 0;
37
+ const stream_1 = require("stream");
38
+ const services_1 = require("../services");
39
+ const utils_1 = require("../utils");
40
+ const LogEmitter_1 = require("../LogEmitter");
41
+ const fs = __importStar(require("fs"));
42
+ class Databases extends stream_1.EventEmitter {
43
+ constructor() {
44
+ super();
45
+ this.sources = new Map();
46
+ }
47
+ async downloadSourcesFromDevice(connectionInfo, networkDevice) {
48
+ const service = await networkDevice.connectToService(services_1.FileTransfer);
49
+ const sources = await service.getSources();
50
+ const output = [];
51
+ for (const source of sources) {
52
+ const deviceId = /(\w{8})(\w{4})(\w{4})(\w{4})(\w{12})/i
53
+ .exec(Buffer.from(connectionInfo.token).toString('hex')).splice(1).join('-');
54
+ const dbConnectionName = `net://${deviceId}/${source.name}`;
55
+ LogEmitter_1.Logger.debug(`DB network path: ${dbConnectionName}`);
56
+ if (this.sources.has(dbConnectionName)) {
57
+ LogEmitter_1.Logger.debug(`Already seen ${source} on ${connectionInfo.address}:${connectionInfo.port}`);
58
+ }
59
+ else {
60
+ await this.downloadDb(dbConnectionName, service, source);
61
+ output.push(dbConnectionName);
62
+ }
63
+ }
64
+ return output;
65
+ }
66
+ /**
67
+ * Download databases from this network source.
68
+ */
69
+ async downloadDb(sourceId, service, source) {
70
+ const dbPath = (0, utils_1.getTempFilePath)(`${sourceId}/m.db`);
71
+ // Read database from source
72
+ LogEmitter_1.Logger.debug(`Reading database ${sourceId}`);
73
+ this.emit('dbDownloading', sourceId, dbPath);
74
+ service.on('fileTransferProgress', (progress) => {
75
+ this.emit('dbProgress', sourceId, progress.total, progress.bytesDownloaded, progress.percentComplete);
76
+ });
77
+ // Save database to a file
78
+ await service.waitTillAvailable();
79
+ const file = await service.getFile(source.database.location);
80
+ LogEmitter_1.Logger.debug(`Saving ${sourceId} to ${dbPath}`);
81
+ fs.writeFileSync(dbPath, Buffer.from(file));
82
+ this.sources.set(sourceId, dbPath);
83
+ LogEmitter_1.Logger.debug(`Downloaded ${sourceId} to ${dbPath}`);
84
+ this.emit('dbDownloaded', sourceId, dbPath);
85
+ }
86
+ getDbPath(dbSourceName) {
87
+ if (!this.sources.size)
88
+ throw new Error(`No data sources have been downloaded`);
89
+ if (!dbSourceName || !this.sources.has(dbSourceName)) {
90
+ // Hack: Denon will save metadata on streaming files but only on an
91
+ // internal database. So if the source is "(Unknown)streaming://"
92
+ // return the first internal database we find.
93
+ for (const entry of Array.from(this.sources.entries())) {
94
+ if (/\(Internal\)/.test(entry[0])) {
95
+ LogEmitter_1.Logger.debug(`Returning copy of internal database`);
96
+ return this.sources.get(entry[0]);
97
+ }
98
+ }
99
+ // Else, throw an exception.
100
+ throw new Error(`Data source "${dbSourceName}" doesn't exist.`);
101
+ }
102
+ return this.sources.get(dbSourceName);
103
+ }
104
+ }
105
+ exports.Databases = Databases;
106
+ //# sourceMappingURL=Databases.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Databases.js","sourceRoot":"","sources":["../../Databases/Databases.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { Track } from '../types';
2
+ export declare class DbConnection {
3
+ private db;
4
+ private dbPath;
5
+ constructor(dbPath: string);
6
+ /**
7
+ * Execute a SQL query.
8
+ *
9
+ * @param query SQL query to execute
10
+ * @param params Parameters for BetterSqlite3 result.all.
11
+ * @returns
12
+ */
13
+ querySource<T>(query: string, ...params: any[]): T[];
14
+ /**
15
+ * Return track's DB entry.
16
+ *
17
+ * @param trackPath Path of track on the source's filesystem.
18
+ * @returns
19
+ */
20
+ getTrackInfo(trackPath: string): Track;
21
+ close(): void;
22
+ }
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DbConnection = void 0;
4
+ const Database = require("better-sqlite3");
5
+ class DbConnection {
6
+ constructor(dbPath) {
7
+ this.dbPath = dbPath;
8
+ console.debug(`Opening ${this.dbPath}`);
9
+ this.db = new Database(this.dbPath);
10
+ }
11
+ /**
12
+ * Execute a SQL query.
13
+ *
14
+ * @param query SQL query to execute
15
+ * @param params Parameters for BetterSqlite3 result.all.
16
+ * @returns
17
+ */
18
+ querySource(query, ...params) {
19
+ console.debug(`Querying ${this.dbPath}: ${query} (${params.join(', ')})`);
20
+ const result = this.db.prepare(query);
21
+ // @ts-ignore
22
+ return result.all(params);
23
+ }
24
+ /**
25
+ * Return track's DB entry.
26
+ *
27
+ * @param trackPath Path of track on the source's filesystem.
28
+ * @returns
29
+ */
30
+ getTrackInfo(trackPath) {
31
+ let result;
32
+ if (/streaming:\/\//.test(trackPath)) {
33
+ result = this.querySource('SELECT * FROM Track WHERE uri = (?) LIMIT 1', trackPath);
34
+ }
35
+ else {
36
+ result = this.querySource('SELECT * FROM Track WHERE path = (?) LIMIT 1', trackPath);
37
+ }
38
+ if (!result)
39
+ throw new Error(`Could not find track: ${trackPath} in database.`);
40
+ return result[0];
41
+ }
42
+ close() {
43
+ console.debug(`Closing ${this.dbPath}`);
44
+ this.db.close();
45
+ }
46
+ }
47
+ exports.DbConnection = DbConnection;
48
+ //# sourceMappingURL=DbConnection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DbConnection.js","sourceRoot":"","sources":["../../Databases/DbConnection.ts"],"names":[],"mappings":";;;AAAA,2CAA4C;AAI5C,MAAa,YAAY;IAKvB,YAAY,MAAc;QACxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,OAAO,CAAC,KAAK,CAAC,WAAW,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,EAAE,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IAED;;;;;;OAMG;IACH,WAAW,CAAI,KAAa,EAAE,GAAG,MAAa;QAC5C,OAAO,CAAC,KAAK,CAAC,YAAY,IAAI,CAAC,MAAM,KAAK,KAAK,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1E,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACtC,aAAa;QACb,OAAO,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC;IAED;;;;;OAKG;IACH,YAAY,CAAC,SAAiB;QAC5B,IAAI,MAAe,CAAC;QACpB,IAAI,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YACrC,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,6CAA6C,EAAE,SAAS,CAAC,CAAC;QACtF,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,8CAA8C,EAAE,SAAS,CAAC,CAAC;QACvF,CAAC;QACD,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,SAAS,eAAe,CAAC,CAAC;QAChF,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC;IAED,KAAK;QACH,OAAO,CAAC,KAAK,CAAC,WAAW,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;CAEF;AA/CD,oCA+CC"}
@@ -0,0 +1,2 @@
1
+ export * from './Databases';
2
+ export * from './DbConnection';
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./Databases"), exports);
18
+ __exportStar(require("./DbConnection"), exports);
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../Databases/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA4B;AAC5B,iDAA+B"}
@@ -0,0 +1,20 @@
1
+ import { EventEmitter } from 'stream';
2
+ export declare interface Logger {
3
+ on(event: 'log', listener: (...args: any) => void): this;
4
+ on(event: 'error', listener: (...args: any) => void): this;
5
+ on(event: 'warn', listener: (...args: any) => void): this;
6
+ on(event: 'info', listener: (...args: any) => void): this;
7
+ on(event: 'debug', listener: (...args: any) => void): this;
8
+ on(event: 'silly', listener: (...args: any) => void): this;
9
+ on(event: 'any', listener: (...args: any) => void): this;
10
+ }
11
+ export declare class Logger extends EventEmitter {
12
+ private static _instance;
13
+ static get instance(): Logger;
14
+ static log(...args: any): void;
15
+ static error(...args: any): void;
16
+ static warn(...args: any): void;
17
+ static info(...args: any): void;
18
+ static debug(...args: any): void;
19
+ static silly(...args: any): void;
20
+ }
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Logger = void 0;
4
+ const stream_1 = require("stream");
5
+ class Logger extends stream_1.EventEmitter {
6
+ static get instance() {
7
+ return this._instance || (this._instance = new this());
8
+ }
9
+ static log(...args) {
10
+ Logger.instance.emit('log', ...args);
11
+ Logger.instance.emit('any', ...args);
12
+ }
13
+ static error(...args) {
14
+ Logger.instance.emit('error', ...args);
15
+ Logger.instance.emit('any', ...args);
16
+ }
17
+ static warn(...args) {
18
+ Logger.instance.emit('warn', ...args);
19
+ Logger.instance.emit('any', ...args);
20
+ }
21
+ static info(...args) {
22
+ Logger.instance.emit('info', ...args);
23
+ Logger.instance.emit('any', ...args);
24
+ }
25
+ static debug(...args) {
26
+ Logger.instance.emit('debug', ...args);
27
+ Logger.instance.emit('any', ...args);
28
+ }
29
+ static silly(...args) {
30
+ Logger.instance.emit('silly', ...args);
31
+ Logger.instance.emit('any', ...args);
32
+ }
33
+ }
34
+ exports.Logger = Logger;
35
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../LogEmitter/index.ts"],"names":[],"mappings":";;;AAAA,mCAAsC;AAYtC,MAAa,MAAO,SAAQ,qBAAY;IAItC,MAAM,KAAK,QAAQ;QACjB,OAAO,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,CAAC,GAAG,CAAC,GAAG,IAAS;QACrB,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;QACrC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,GAAG,IAAS;QACvB,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;QACvC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,GAAG,IAAS;QACtB,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;QACtC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,GAAG,IAAS;QACtB,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;QACtC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,GAAG,IAAS;QACvB,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;QACvC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,GAAG,IAAS;QACvB,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;QACvC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;IACvC,CAAC;CAEF;AAtCD,wBAsCC"}