alphatheta-connect 0.15.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/LICENSE +21 -0
- package/README.md +166 -0
- package/lib/artwork/index.d.ts +9 -0
- package/lib/artwork/parsers/aiff.d.ts +2 -0
- package/lib/artwork/parsers/flac.d.ts +2 -0
- package/lib/artwork/parsers/id3.d.ts +2 -0
- package/lib/artwork/parsers/index.d.ts +5 -0
- package/lib/artwork/parsers/mp4.d.ts +2 -0
- package/lib/artwork/parsers/utils.d.ts +3 -0
- package/lib/artwork/reader.d.ts +15 -0
- package/lib/artwork/types.d.ts +44 -0
- package/lib/cli/index.d.ts +1 -0
- package/lib/cli.js +11431 -0
- package/lib/cli.js.map +1 -0
- package/lib/constants.d.ts +40 -0
- package/lib/control/index.d.ts +20 -0
- package/lib/db/getArtworkFromFile.d.ts +13 -0
- package/lib/db/getArtworkThumbnail.d.ts +29 -0
- package/lib/db/getFile.d.ts +29 -0
- package/lib/db/getMetadata.d.ts +28 -0
- package/lib/db/getPlaylist.d.ts +26 -0
- package/lib/db/getWaveforms.d.ts +29 -0
- package/lib/db/index.d.ts +70 -0
- package/lib/db/utils.d.ts +7 -0
- package/lib/devices/index.d.ts +66 -0
- package/lib/devices/utils.d.ts +5 -0
- package/lib/entities.d.ts +122 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.js +13381 -0
- package/lib/index.js.map +1 -0
- package/lib/localdb/database-adapter.d.ts +49 -0
- package/lib/localdb/index.d.ts +104 -0
- package/lib/localdb/onelibrary-schema.d.ts +250 -0
- package/lib/localdb/onelibrary.d.ts +185 -0
- package/lib/localdb/orm.d.ts +60 -0
- package/lib/localdb/rekordbox.d.ts +112 -0
- package/lib/localdb/schema.d.ts +1 -0
- package/lib/localdb/utils.d.ts +5 -0
- package/lib/mixstatus/index.d.ts +109 -0
- package/lib/mixstatus/utils.d.ts +9 -0
- package/lib/network.d.ts +201 -0
- package/lib/nfs/index.d.ts +63 -0
- package/lib/nfs/programs.d.ts +61 -0
- package/lib/nfs/rpc.d.ts +60 -0
- package/lib/nfs/utils.d.ts +4 -0
- package/lib/nfs/xdr.d.ts +17 -0
- package/lib/passive/alphatheta.d.ts +94 -0
- package/lib/passive/devices.d.ts +64 -0
- package/lib/passive/index.d.ts +169 -0
- package/lib/passive/localdb.d.ts +122 -0
- package/lib/passive/pcap-adapter.d.ts +77 -0
- package/lib/passive/position.d.ts +32 -0
- package/lib/passive/remotedb.d.ts +49 -0
- package/lib/passive/status.d.ts +41 -0
- package/lib/remotedb/constants.d.ts +8 -0
- package/lib/remotedb/fields.d.ts +169 -0
- package/lib/remotedb/index.d.ts +160 -0
- package/lib/remotedb/message/index.d.ts +39 -0
- package/lib/remotedb/message/item.d.ts +530 -0
- package/lib/remotedb/message/response.d.ts +18 -0
- package/lib/remotedb/message/types.d.ts +227 -0
- package/lib/remotedb/queries.d.ts +99 -0
- package/lib/remotedb/utils.d.ts +23 -0
- package/lib/status/index.d.ts +39 -0
- package/lib/status/media.d.ts +22 -0
- package/lib/status/position.d.ts +29 -0
- package/lib/status/types.d.ts +170 -0
- package/lib/status/utils.d.ts +19 -0
- package/lib/types.d.ts +457 -0
- package/lib/types.js +274 -0
- package/lib/types.js.map +1 -0
- package/lib/utils/converters.d.ts +17 -0
- package/lib/utils/index.d.ts +28 -0
- package/lib/utils/telemetry.d.ts +82 -0
- package/lib/utils/udp.d.ts +20 -0
- package/lib/virtualcdj/index.d.ts +33 -0
- package/package.json +93 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform utilities for detecting AlphaTheta (Pioneer DJ) network interfaces.
|
|
3
|
+
* Supports both USB-connected devices (XDJ-XZ, XDJ-AZ) and Ethernet-connected devices.
|
|
4
|
+
*/
|
|
5
|
+
import { type NetworkInterfaceInfo } from 'os';
|
|
6
|
+
export interface AlphaThetaInterface {
|
|
7
|
+
/** The interface name (e.g., "en15" on macOS, "Ethernet 3" on Windows) */
|
|
8
|
+
name: string;
|
|
9
|
+
/** The MAC address of the interface */
|
|
10
|
+
mac: string;
|
|
11
|
+
/** The IPv4 address of the host on this interface */
|
|
12
|
+
ipv4?: string;
|
|
13
|
+
/** The full NetworkInterfaceInfo from Node.js */
|
|
14
|
+
info: NetworkInterfaceInfo;
|
|
15
|
+
/** How the device is connected */
|
|
16
|
+
connectionType: 'usb' | 'ethernet';
|
|
17
|
+
/** IP addresses of AlphaTheta devices found on this interface */
|
|
18
|
+
deviceIps?: string[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Find the network interface for an AlphaTheta device connected via USB or Ethernet.
|
|
22
|
+
* Works on macOS, Windows, and Linux.
|
|
23
|
+
*
|
|
24
|
+
* Detection methods (tried in order):
|
|
25
|
+
* 1. USB: Looks for USB-connected devices (XDJ-XZ, XDJ-AZ) via system APIs
|
|
26
|
+
* 2. Ethernet: Checks ARP cache for known AlphaTheta/Pioneer MAC address prefixes
|
|
27
|
+
*
|
|
28
|
+
* This is useful for passive mode to automatically detect the correct interface
|
|
29
|
+
* for AlphaTheta devices like XDJ-XZ, XDJ-AZ, CDJ-3000, etc.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* import { findAlphaThetaInterface, bringOnlinePassive } from 'alphatheta-connect';
|
|
34
|
+
*
|
|
35
|
+
* const iface = findAlphaThetaInterface();
|
|
36
|
+
* if (iface) {
|
|
37
|
+
* console.log(`Found AlphaTheta device on ${iface.name} (${iface.connectionType})`);
|
|
38
|
+
* const network = await bringOnlinePassive({ iface: iface.name });
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @returns The AlphaTheta interface info, or null if not found
|
|
43
|
+
*/
|
|
44
|
+
export declare function findAlphaThetaInterface(): AlphaThetaInterface | null;
|
|
45
|
+
/**
|
|
46
|
+
* Find ALL network interfaces with AlphaTheta/Pioneer DJ devices.
|
|
47
|
+
* Works on macOS, Windows, and Linux.
|
|
48
|
+
*
|
|
49
|
+
* Detection methods:
|
|
50
|
+
* 1. USB: Looks for USB-connected devices (XDJ-XZ, XDJ-AZ) via system APIs
|
|
51
|
+
* 2. Ethernet: Checks ARP cache for known AlphaTheta/Pioneer MAC address prefixes
|
|
52
|
+
*
|
|
53
|
+
* Returns all detected interfaces, with USB interfaces listed first.
|
|
54
|
+
* Use this when you want to present users with a choice of interfaces,
|
|
55
|
+
* or when multiple Pro DJ Link networks are available.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* import { findAllAlphaThetaInterfaces, bringOnlinePassive } from 'alphatheta-connect';
|
|
60
|
+
*
|
|
61
|
+
* const interfaces = findAllAlphaThetaInterfaces();
|
|
62
|
+
* if (interfaces.length > 0) {
|
|
63
|
+
* console.log('Available interfaces:');
|
|
64
|
+
* for (const iface of interfaces) {
|
|
65
|
+
* console.log(` ${iface.name} (${iface.connectionType}) - ${iface.ipv4 || 'no IP'}`);
|
|
66
|
+
* }
|
|
67
|
+
*
|
|
68
|
+
* // Let user choose or use first one
|
|
69
|
+
* const network = await bringOnlinePassive({ iface: interfaces[0].name });
|
|
70
|
+
* }
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* @returns Array of AlphaTheta interface info, empty if none found
|
|
74
|
+
*/
|
|
75
|
+
export declare function findAllAlphaThetaInterfaces(): AlphaThetaInterface[];
|
|
76
|
+
/**
|
|
77
|
+
* Get the device IP address from a link-local interface.
|
|
78
|
+
*
|
|
79
|
+
* When connected via USB, the device typically uses a link-local address
|
|
80
|
+
* in the 169.254.x.x range. The device's IP can often be inferred from
|
|
81
|
+
* the host's IP (they're usually on the same /16 subnet).
|
|
82
|
+
*
|
|
83
|
+
* @param hostIp - The host's IP address on the interface
|
|
84
|
+
* @returns The likely device IP, or null if not determinable
|
|
85
|
+
*/
|
|
86
|
+
export declare function inferDeviceIpFromHost(hostIp: string): string | null;
|
|
87
|
+
/**
|
|
88
|
+
* Try to find the device IP by checking the ARP cache.
|
|
89
|
+
* Works on macOS and Linux.
|
|
90
|
+
*
|
|
91
|
+
* @param interfaceName - The interface name to check
|
|
92
|
+
* @returns Array of IP addresses found in ARP cache for this interface
|
|
93
|
+
*/
|
|
94
|
+
export declare function getArpCacheForInterface(interfaceName: string): string[];
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import { Device, DeviceID } from "../types";
|
|
4
|
+
import { PcapAdapter } from './pcap-adapter';
|
|
5
|
+
interface Config {
|
|
6
|
+
/**
|
|
7
|
+
* Time in milliseconds after which a device is considered to have
|
|
8
|
+
* disconnected if it has not broadcast an announcement.
|
|
9
|
+
*
|
|
10
|
+
* @default 10000 ms
|
|
11
|
+
*/
|
|
12
|
+
deviceTimeout?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The interface the passive device manager event emitter should follow
|
|
16
|
+
*/
|
|
17
|
+
interface DeviceEvents {
|
|
18
|
+
/**
|
|
19
|
+
* Fired when a new device becomes available on the network
|
|
20
|
+
*/
|
|
21
|
+
connected: (device: Device) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Fired when a device has not announced itself on the network for the
|
|
24
|
+
* specified timeout.
|
|
25
|
+
*/
|
|
26
|
+
disconnected: (device: Device) => void;
|
|
27
|
+
/**
|
|
28
|
+
* Fired every time the device announces itself on the network
|
|
29
|
+
*/
|
|
30
|
+
announced: (device: Device) => void;
|
|
31
|
+
}
|
|
32
|
+
type Emitter = StrictEventEmitter<EventEmitter, DeviceEvents>;
|
|
33
|
+
/**
|
|
34
|
+
* PassiveDeviceManager tracks devices on the Pro DJ Link network using
|
|
35
|
+
* passive packet capture instead of UDP sockets.
|
|
36
|
+
*
|
|
37
|
+
* It provides the same API as the active DeviceManager, making it easy
|
|
38
|
+
* to swap between active and passive modes.
|
|
39
|
+
*/
|
|
40
|
+
export declare class PassiveDeviceManager {
|
|
41
|
+
#private;
|
|
42
|
+
constructor(adapter: PcapAdapter, config?: Config);
|
|
43
|
+
on: Emitter['on'];
|
|
44
|
+
off: Emitter['off'];
|
|
45
|
+
once: Emitter['once'];
|
|
46
|
+
/**
|
|
47
|
+
* Get active devices on the network.
|
|
48
|
+
*/
|
|
49
|
+
get devices(): Map<number, Device>;
|
|
50
|
+
/**
|
|
51
|
+
* Waits for a specific device ID to appear on the network, with a
|
|
52
|
+
* configurable timeout, in which case it will resolve with null.
|
|
53
|
+
*/
|
|
54
|
+
getDeviceEnsured(id: DeviceID, timeout?: number): Promise<Device | null>;
|
|
55
|
+
/**
|
|
56
|
+
* Reconfigure the device manager.
|
|
57
|
+
*/
|
|
58
|
+
reconfigure(config: Config): void;
|
|
59
|
+
/**
|
|
60
|
+
* Stop listening to the pcap adapter and clean up timeouts.
|
|
61
|
+
*/
|
|
62
|
+
stop(): void;
|
|
63
|
+
}
|
|
64
|
+
export default PassiveDeviceManager;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { DatabasePreference } from "../localdb/database-adapter";
|
|
2
|
+
import { MixstatusProcessor } from "../mixstatus";
|
|
3
|
+
import { PassiveDeviceManager } from './devices';
|
|
4
|
+
import { PassiveLocalDatabase } from './localdb';
|
|
5
|
+
import { PcapAdapter } from './pcap-adapter';
|
|
6
|
+
import { PassivePositionEmitter } from './position';
|
|
7
|
+
import { PassiveRemoteDatabase } from './remotedb';
|
|
8
|
+
import { PassiveStatusEmitter } from './status';
|
|
9
|
+
export { AlphaThetaInterface, findAlphaThetaInterface, findAllAlphaThetaInterfaces, getArpCacheForInterface, } from './alphatheta';
|
|
10
|
+
export { PassiveDeviceManager } from './devices';
|
|
11
|
+
export { PassiveLocalDatabase } from './localdb';
|
|
12
|
+
export { PacketInfo, PcapAdapter, PcapAdapterConfig } from './pcap-adapter';
|
|
13
|
+
export { PassivePositionEmitter } from './position';
|
|
14
|
+
export { PassiveRemoteDatabase } from './remotedb';
|
|
15
|
+
export { PassiveStatusEmitter } from './status';
|
|
16
|
+
export interface PassiveNetworkConfig {
|
|
17
|
+
/**
|
|
18
|
+
* Network interface name (e.g., 'en0', 'eth0', 'en15' for USB-connected devices)
|
|
19
|
+
*/
|
|
20
|
+
iface: string;
|
|
21
|
+
/**
|
|
22
|
+
* Buffer size for packet capture in bytes
|
|
23
|
+
* @default 10485760 (10MB)
|
|
24
|
+
*/
|
|
25
|
+
bufferSize?: number;
|
|
26
|
+
/**
|
|
27
|
+
* Time in milliseconds after which a device is considered disconnected
|
|
28
|
+
* @default 10000
|
|
29
|
+
*/
|
|
30
|
+
deviceTimeout?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Database format preference for loading rekordbox databases.
|
|
33
|
+
*
|
|
34
|
+
* - 'auto': Try OneLibrary first (rekordbox 7.x+), fall back to PDB (rekordbox 6.x)
|
|
35
|
+
* - 'oneLibrary': Only use OneLibrary format (exportLibrary.db)
|
|
36
|
+
* - 'pdb': Only use PDB format (export.pdb)
|
|
37
|
+
*
|
|
38
|
+
* @default 'auto'
|
|
39
|
+
*/
|
|
40
|
+
databasePreference?: DatabasePreference;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* PassiveProlinkNetwork provides a passive monitoring interface to the
|
|
44
|
+
* Pro DJ Link network using pcap packet capture.
|
|
45
|
+
*
|
|
46
|
+
* Unlike the active ProlinkNetwork, passive mode:
|
|
47
|
+
* - Does not bind to UDP ports (no conflicts with Rekordbox)
|
|
48
|
+
* - Does not announce a virtual CDJ (devices don't know we exist)
|
|
49
|
+
* - Cannot send packets (no CDJ control, no queryMediaSlot)
|
|
50
|
+
* - Works with USB-connected devices (XDJ-AZ, XDJ-XZ)
|
|
51
|
+
*
|
|
52
|
+
* Requirements:
|
|
53
|
+
* - Root/sudo privileges for packet capture
|
|
54
|
+
* - libpcap (Linux) or Npcap/WinPcap (Windows)
|
|
55
|
+
* - The 'cap' npm module
|
|
56
|
+
*/
|
|
57
|
+
export declare class PassiveProlinkNetwork {
|
|
58
|
+
#private;
|
|
59
|
+
constructor(config: PassiveNetworkConfig);
|
|
60
|
+
/**
|
|
61
|
+
* Start passive packet capture.
|
|
62
|
+
* Requires root/sudo privileges.
|
|
63
|
+
*/
|
|
64
|
+
start(): void;
|
|
65
|
+
/**
|
|
66
|
+
* Stop packet capture and clean up all resources.
|
|
67
|
+
*/
|
|
68
|
+
stop(): void;
|
|
69
|
+
/**
|
|
70
|
+
* Check if packet capture is active.
|
|
71
|
+
*/
|
|
72
|
+
get isCapturing(): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Get the network interface being monitored.
|
|
75
|
+
*/
|
|
76
|
+
get interfaceName(): string;
|
|
77
|
+
/**
|
|
78
|
+
* Get the pcap adapter for advanced usage.
|
|
79
|
+
*/
|
|
80
|
+
get adapter(): PcapAdapter;
|
|
81
|
+
/**
|
|
82
|
+
* Get the PassiveDeviceManager service. Tracks devices on the network
|
|
83
|
+
* by listening to announcement packets.
|
|
84
|
+
*/
|
|
85
|
+
get deviceManager(): PassiveDeviceManager;
|
|
86
|
+
/**
|
|
87
|
+
* Get the PassiveStatusEmitter service. Reports CDJ status updates
|
|
88
|
+
* received via packet capture.
|
|
89
|
+
*/
|
|
90
|
+
get statusEmitter(): PassiveStatusEmitter;
|
|
91
|
+
/**
|
|
92
|
+
* Get the PassivePositionEmitter service. Reports absolute playhead
|
|
93
|
+
* position updates from CDJ-3000+ devices.
|
|
94
|
+
*/
|
|
95
|
+
get positionEmitter(): PassivePositionEmitter;
|
|
96
|
+
/**
|
|
97
|
+
* Get the PassiveLocalDatabase service. Provides access to rekordbox
|
|
98
|
+
* databases on devices using NFS (works without announcing a VCDJ).
|
|
99
|
+
*/
|
|
100
|
+
get localdb(): PassiveLocalDatabase;
|
|
101
|
+
/**
|
|
102
|
+
* Get (and initialize) the PassiveRemoteDatabase service. Provides access
|
|
103
|
+
* to track metadata via RemoteDB queries.
|
|
104
|
+
*
|
|
105
|
+
* Note: This sends TCP packets to devices, so it's not fully "passive",
|
|
106
|
+
* but it avoids UDP announcements that would conflict with Rekordbox.
|
|
107
|
+
*
|
|
108
|
+
* Useful for getting metadata for Rekordbox Link tracks where NFS
|
|
109
|
+
* access is not available.
|
|
110
|
+
*/
|
|
111
|
+
get remotedb(): PassiveRemoteDatabase;
|
|
112
|
+
/**
|
|
113
|
+
* Get (and initialize) the MixstatusProcessor service. Can be used to
|
|
114
|
+
* monitor the 'status' of devices on the network as a whole.
|
|
115
|
+
*/
|
|
116
|
+
get mixstatus(): MixstatusProcessor;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Create and start a passive Pro DJ Link network monitor.
|
|
120
|
+
*
|
|
121
|
+
* This is the primary entrypoint for passive mode. It captures Pro DJ Link
|
|
122
|
+
* packets via pcap without binding to UDP ports or announcing a virtual CDJ.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```typescript
|
|
126
|
+
* import { bringOnlinePassive } from 'alphatheta-connect';
|
|
127
|
+
*
|
|
128
|
+
* // Start passive monitoring on en15 (XDJ-XZ USB interface)
|
|
129
|
+
* const network = await bringOnlinePassive({ iface: 'en15' });
|
|
130
|
+
*
|
|
131
|
+
* // Listen for devices
|
|
132
|
+
* network.deviceManager.on('connected', device => {
|
|
133
|
+
* console.log('Device connected:', device.name);
|
|
134
|
+
* });
|
|
135
|
+
*
|
|
136
|
+
* // Listen for track changes
|
|
137
|
+
* network.statusEmitter.on('status', status => {
|
|
138
|
+
* console.log('Track:', status.trackId);
|
|
139
|
+
* });
|
|
140
|
+
*
|
|
141
|
+
* // Get track metadata via NFS
|
|
142
|
+
* const track = await network.localdb.get(device.id, MediaSlot.USB);
|
|
143
|
+
*
|
|
144
|
+
* // Cleanup
|
|
145
|
+
* network.stop();
|
|
146
|
+
* ```
|
|
147
|
+
*
|
|
148
|
+
* @param config - Configuration including network interface name
|
|
149
|
+
* @returns PassiveProlinkNetwork instance
|
|
150
|
+
*/
|
|
151
|
+
export declare function bringOnlinePassive(config: PassiveNetworkConfig): PassiveProlinkNetwork;
|
|
152
|
+
/**
|
|
153
|
+
* List available network interfaces that can be used for packet capture.
|
|
154
|
+
* Useful for finding USB-connected DJ hardware interfaces.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```typescript
|
|
158
|
+
* import { listInterfaces } from 'alphatheta-connect/passive';
|
|
159
|
+
*
|
|
160
|
+
* const interfaces = listInterfaces();
|
|
161
|
+
* console.log(interfaces);
|
|
162
|
+
* // [{ name: 'en0', address: '192.168.1.100' }, { name: 'en15', address: '169.254.x.x' }]
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
export declare function listInterfaces(): Array<{
|
|
166
|
+
name: string;
|
|
167
|
+
address: string;
|
|
168
|
+
description?: string;
|
|
169
|
+
}>;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import { DatabaseAdapter, DatabasePreference } from "../localdb/database-adapter";
|
|
4
|
+
import { HydrationProgress } from "../localdb/rekordbox";
|
|
5
|
+
import { FetchProgress } from "../nfs";
|
|
6
|
+
import { Device, DeviceID, MediaSlot, MediaSlotInfo } from "../types";
|
|
7
|
+
import { PassiveDeviceManager } from './devices';
|
|
8
|
+
import { PassiveStatusEmitter } from './status';
|
|
9
|
+
/**
|
|
10
|
+
* Rekordbox databases will only exist within these two slots
|
|
11
|
+
*/
|
|
12
|
+
type DatabaseSlot = MediaSlot.USB | MediaSlot.SD;
|
|
13
|
+
interface CommonProgressOpts {
|
|
14
|
+
device: Device;
|
|
15
|
+
slot: MediaSlot;
|
|
16
|
+
}
|
|
17
|
+
type DownloadProgressOpts = CommonProgressOpts & {
|
|
18
|
+
progress: FetchProgress;
|
|
19
|
+
};
|
|
20
|
+
type HydrationProgressOpts = CommonProgressOpts & {
|
|
21
|
+
progress: HydrationProgress;
|
|
22
|
+
};
|
|
23
|
+
type HydrationDoneOpts = CommonProgressOpts;
|
|
24
|
+
/**
|
|
25
|
+
* Events that may be triggered by the PassiveLocalDatabase emitter
|
|
26
|
+
*/
|
|
27
|
+
interface DatabaseEvents {
|
|
28
|
+
/**
|
|
29
|
+
* Triggered when we are fetching a database from a CDJ
|
|
30
|
+
*/
|
|
31
|
+
fetchProgress: (opts: DownloadProgressOpts) => void;
|
|
32
|
+
/**
|
|
33
|
+
* Triggered when we are hydrating a rekordbox database into the in-memory
|
|
34
|
+
* sqlite database.
|
|
35
|
+
*/
|
|
36
|
+
hydrationProgress: (opts: HydrationProgressOpts) => void;
|
|
37
|
+
/**
|
|
38
|
+
* Triggered when the database has been fully hydrated.
|
|
39
|
+
*/
|
|
40
|
+
hydrationDone: (opts: HydrationDoneOpts) => void;
|
|
41
|
+
}
|
|
42
|
+
type Emitter = StrictEventEmitter<EventEmitter, DatabaseEvents>;
|
|
43
|
+
/**
|
|
44
|
+
* PassiveLocalDatabase provides access to rekordbox databases on devices
|
|
45
|
+
* using passive packet capture.
|
|
46
|
+
*
|
|
47
|
+
* Unlike the active LocalDatabase, this version:
|
|
48
|
+
* - Cannot query for media slot info (no queryMediaSlot)
|
|
49
|
+
* - Listens for mediaSlot broadcasts to cache media info
|
|
50
|
+
* - Provides getWithMedia() method when media info is known
|
|
51
|
+
*
|
|
52
|
+
* NFS access to fetch rekordbox databases works without announcing a VCDJ.
|
|
53
|
+
*/
|
|
54
|
+
export declare class PassiveLocalDatabase {
|
|
55
|
+
#private;
|
|
56
|
+
constructor(deviceManager: PassiveDeviceManager, statusEmitter: PassiveStatusEmitter, preference?: DatabasePreference);
|
|
57
|
+
/**
|
|
58
|
+
* Get the current database preference
|
|
59
|
+
*/
|
|
60
|
+
get preference(): DatabasePreference;
|
|
61
|
+
/**
|
|
62
|
+
* Set the database preference. Only affects newly loaded databases.
|
|
63
|
+
*/
|
|
64
|
+
set preference(value: DatabasePreference);
|
|
65
|
+
on: Emitter['on'];
|
|
66
|
+
off: Emitter['off'];
|
|
67
|
+
once: Emitter['once'];
|
|
68
|
+
/**
|
|
69
|
+
* Get cached media slot info for a device and slot.
|
|
70
|
+
* Returns undefined if no media slot info has been received.
|
|
71
|
+
*/
|
|
72
|
+
getCachedMedia(deviceId: DeviceID, slot: DatabaseSlot): MediaSlotInfo | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* Get all cached media slot info.
|
|
75
|
+
*/
|
|
76
|
+
getAllCachedMedia(): MediaSlotInfo[];
|
|
77
|
+
/**
|
|
78
|
+
* Disconnects the local database connection for the specified device
|
|
79
|
+
*/
|
|
80
|
+
disconnectForDevice(device: Device): void;
|
|
81
|
+
/**
|
|
82
|
+
* Stop listening to events and clean up.
|
|
83
|
+
*/
|
|
84
|
+
stop(): void;
|
|
85
|
+
/**
|
|
86
|
+
* Gets the database adapter for the media metadata in the provided device slot,
|
|
87
|
+
* using cached media info.
|
|
88
|
+
*
|
|
89
|
+
* This method uses cached media slot info that was received from
|
|
90
|
+
* broadcast packets. If no media info is cached for this slot,
|
|
91
|
+
* it will attempt to fetch the database without media info (useful
|
|
92
|
+
* for all-in-one units like XDJ-XZ that don't broadcast mediaSlot).
|
|
93
|
+
*
|
|
94
|
+
* @returns null if no rekordbox media present or fetch fails
|
|
95
|
+
*/
|
|
96
|
+
get(deviceId: DeviceID, slot: DatabaseSlot): Promise<DatabaseAdapter | null>;
|
|
97
|
+
/**
|
|
98
|
+
* Gets the database adapter for the media metadata using provided media slot info.
|
|
99
|
+
*
|
|
100
|
+
* Use this method when you have media slot info from another source
|
|
101
|
+
* (e.g., parsed from status packets or provided manually).
|
|
102
|
+
*
|
|
103
|
+
* @returns null if no rekordbox media present
|
|
104
|
+
*/
|
|
105
|
+
getWithMedia(device: Device, slot: DatabaseSlot, media: MediaSlotInfo): Promise<DatabaseAdapter | null>;
|
|
106
|
+
/**
|
|
107
|
+
* Attempts to get/hydrate a database without cached media slot info.
|
|
108
|
+
* This is used for all-in-one units (XDJ-XZ, XDJ-RX, etc.) that don't
|
|
109
|
+
* broadcast mediaSlot info packets.
|
|
110
|
+
*
|
|
111
|
+
* The method will try to fetch the rekordbox export.pdb file directly
|
|
112
|
+
* via NFS. If successful, the database is hydrated and cached.
|
|
113
|
+
*
|
|
114
|
+
* @returns null if no rekordbox database found or fetch fails
|
|
115
|
+
*/
|
|
116
|
+
getWithoutMedia(device: Device, slot: DatabaseSlot): Promise<DatabaseAdapter | null>;
|
|
117
|
+
/**
|
|
118
|
+
* Preload the databases for all connected devices using cached media info.
|
|
119
|
+
*/
|
|
120
|
+
preload(): Promise<void>;
|
|
121
|
+
}
|
|
122
|
+
export default PassiveLocalDatabase;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
/**
|
|
4
|
+
* Packet info including source IP (useful for short-format packets
|
|
5
|
+
* where the IP may not be fully present in the payload).
|
|
6
|
+
*/
|
|
7
|
+
export interface PacketInfo {
|
|
8
|
+
/** The UDP payload */
|
|
9
|
+
payload: Buffer;
|
|
10
|
+
/** Source IP address (e.g., '169.254.88.83') */
|
|
11
|
+
srcAddr: string;
|
|
12
|
+
}
|
|
13
|
+
interface PcapAdapterEvents {
|
|
14
|
+
/**
|
|
15
|
+
* Fired when a device announcement packet is received (port 50000)
|
|
16
|
+
*/
|
|
17
|
+
announce: (packet: Buffer, info: PacketInfo) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Fired when a status packet is received (port 50002)
|
|
20
|
+
*/
|
|
21
|
+
status: (packet: Buffer, info: PacketInfo) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Fired when a beat/position packet is received (port 50001)
|
|
24
|
+
*/
|
|
25
|
+
beat: (packet: Buffer, info: PacketInfo) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Fired when an error occurs
|
|
28
|
+
*/
|
|
29
|
+
error: (error: Error) => void;
|
|
30
|
+
}
|
|
31
|
+
type Emitter = StrictEventEmitter<EventEmitter, PcapAdapterEvents>;
|
|
32
|
+
export interface PcapAdapterConfig {
|
|
33
|
+
/**
|
|
34
|
+
* Network interface name (e.g., 'en0', 'eth0', 'en15')
|
|
35
|
+
*/
|
|
36
|
+
iface: string;
|
|
37
|
+
/**
|
|
38
|
+
* Buffer size for packet capture in bytes
|
|
39
|
+
* @default 10485760 (10MB)
|
|
40
|
+
*/
|
|
41
|
+
bufferSize?: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* PcapAdapter captures Pro DJ Link packets via pcap (libpcap).
|
|
45
|
+
* It extracts UDP payloads and emits them to event listeners based on
|
|
46
|
+
* destination port.
|
|
47
|
+
*
|
|
48
|
+
* This allows passive monitoring of the Pro DJ Link network without
|
|
49
|
+
* binding to UDP ports or announcing a virtual CDJ.
|
|
50
|
+
*
|
|
51
|
+
* NOTE: Requires root/sudo privileges for packet capture.
|
|
52
|
+
*/
|
|
53
|
+
export declare class PcapAdapter {
|
|
54
|
+
#private;
|
|
55
|
+
constructor(config: PcapAdapterConfig);
|
|
56
|
+
on: Emitter['on'];
|
|
57
|
+
off: Emitter['off'];
|
|
58
|
+
once: Emitter['once'];
|
|
59
|
+
/**
|
|
60
|
+
* Start capturing packets on the configured interface.
|
|
61
|
+
* Requires root/sudo privileges.
|
|
62
|
+
*/
|
|
63
|
+
start(): void;
|
|
64
|
+
/**
|
|
65
|
+
* Stop capturing packets and release resources.
|
|
66
|
+
*/
|
|
67
|
+
stop(): void;
|
|
68
|
+
/**
|
|
69
|
+
* Check if the adapter is currently capturing.
|
|
70
|
+
*/
|
|
71
|
+
get isCapturing(): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Get the interface name being captured.
|
|
74
|
+
*/
|
|
75
|
+
get interfaceName(): string;
|
|
76
|
+
}
|
|
77
|
+
export default PcapAdapter;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import { CDJStatus } from "../types";
|
|
4
|
+
import { PcapAdapter } from './pcap-adapter';
|
|
5
|
+
interface PositionEvents {
|
|
6
|
+
/**
|
|
7
|
+
* Fired when an absolute position packet is received from a CDJ-3000+.
|
|
8
|
+
* These packets are sent approximately every 30ms while a track is loaded.
|
|
9
|
+
*/
|
|
10
|
+
position: (position: CDJStatus.PositionState) => void;
|
|
11
|
+
}
|
|
12
|
+
type Emitter = StrictEventEmitter<EventEmitter, PositionEvents>;
|
|
13
|
+
/**
|
|
14
|
+
* PassivePositionEmitter reports absolute playhead position updates from
|
|
15
|
+
* CDJ-3000+ devices using passive packet capture.
|
|
16
|
+
*
|
|
17
|
+
* Position packets provide precise track position independent of beat grids,
|
|
18
|
+
* enabling accurate timecode, lighting cue, and video synchronization even
|
|
19
|
+
* during scratching, reverse play, loops, and needle jumps.
|
|
20
|
+
*/
|
|
21
|
+
export declare class PassivePositionEmitter {
|
|
22
|
+
#private;
|
|
23
|
+
constructor(adapter: PcapAdapter);
|
|
24
|
+
on: Emitter['on'];
|
|
25
|
+
off: Emitter['off'];
|
|
26
|
+
once: Emitter['once'];
|
|
27
|
+
/**
|
|
28
|
+
* Stop listening to the pcap adapter.
|
|
29
|
+
*/
|
|
30
|
+
stop(): void;
|
|
31
|
+
}
|
|
32
|
+
export default PassivePositionEmitter;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { QueryInterface } from "../remotedb";
|
|
2
|
+
import { Device, DeviceID, MediaSlot, TrackType } from "../types";
|
|
3
|
+
import { PassiveDeviceManager } from './devices';
|
|
4
|
+
/**
|
|
5
|
+
* PassiveRemoteDatabase provides RemoteDB query support for passive mode.
|
|
6
|
+
*
|
|
7
|
+
* This allows querying track metadata from Rekordbox Link without fully
|
|
8
|
+
* announcing a virtual CDJ on the network. It uses a "virtual" device ID
|
|
9
|
+
* (default: 5) for the introduction handshake.
|
|
10
|
+
*
|
|
11
|
+
* Note: This makes the mode not fully "passive" since we're sending TCP
|
|
12
|
+
* packets, but we still avoid UDP announcements that would conflict with
|
|
13
|
+
* Rekordbox.
|
|
14
|
+
*/
|
|
15
|
+
export declare class PassiveRemoteDatabase {
|
|
16
|
+
#private;
|
|
17
|
+
constructor(deviceManager: PassiveDeviceManager, virtualDeviceId?: number);
|
|
18
|
+
/**
|
|
19
|
+
* Open a connection to the specified device for querying
|
|
20
|
+
*/
|
|
21
|
+
connectToDevice(device: Device): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Disconnect from the specified device
|
|
24
|
+
*/
|
|
25
|
+
disconnectFromDevice(device: Device): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Gets the remote database query interface for the given device.
|
|
28
|
+
*
|
|
29
|
+
* If we have not already established a connection with the specified device,
|
|
30
|
+
* we will attempt to first connect.
|
|
31
|
+
*
|
|
32
|
+
* @returns null if the device is not found
|
|
33
|
+
*/
|
|
34
|
+
get(deviceId: DeviceID): Promise<QueryInterface | null>;
|
|
35
|
+
/**
|
|
36
|
+
* Query track metadata from a device.
|
|
37
|
+
*
|
|
38
|
+
* @param deviceId - The device to query (e.g., 17 for Rekordbox)
|
|
39
|
+
* @param trackSlot - The media slot (e.g., MediaSlot.RB for Rekordbox Link)
|
|
40
|
+
* @param trackType - The track type (e.g., TrackType.RB)
|
|
41
|
+
* @param trackId - The track ID to look up
|
|
42
|
+
*/
|
|
43
|
+
getTrackMetadata(deviceId: DeviceID, trackSlot: MediaSlot, trackType: TrackType, trackId: number): Promise<import("../types").Track<import("..").EntityFK.WithRelations> | null>;
|
|
44
|
+
/**
|
|
45
|
+
* Stop all connections
|
|
46
|
+
*/
|
|
47
|
+
stop(): void;
|
|
48
|
+
}
|
|
49
|
+
export default PassiveRemoteDatabase;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import { CDJStatus, MediaSlotInfo } from "../types";
|
|
4
|
+
import { PcapAdapter } from './pcap-adapter';
|
|
5
|
+
interface StatusEvents {
|
|
6
|
+
/**
|
|
7
|
+
* Fired each time the CDJ reports its status
|
|
8
|
+
*/
|
|
9
|
+
status: (status: CDJStatus.State) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Fired when the CDJ reports its media slot status
|
|
12
|
+
*/
|
|
13
|
+
mediaSlot: (info: MediaSlotInfo) => void;
|
|
14
|
+
/**
|
|
15
|
+
* Fired when the mixer broadcasts on-air channel status
|
|
16
|
+
*/
|
|
17
|
+
onAir: (status: CDJStatus.OnAirStatus) => void;
|
|
18
|
+
}
|
|
19
|
+
type Emitter = StrictEventEmitter<EventEmitter, StatusEvents>;
|
|
20
|
+
/**
|
|
21
|
+
* PassiveStatusEmitter reports CDJ status updates received via passive
|
|
22
|
+
* packet capture instead of UDP sockets.
|
|
23
|
+
*
|
|
24
|
+
* It provides the same event API as the active StatusEmitter, but does
|
|
25
|
+
* NOT support the queryMediaSlot method since that requires sending packets.
|
|
26
|
+
*
|
|
27
|
+
* In passive mode, media slot information is received when CDJs broadcast
|
|
28
|
+
* it naturally (e.g., when media is inserted or at startup).
|
|
29
|
+
*/
|
|
30
|
+
export declare class PassiveStatusEmitter {
|
|
31
|
+
#private;
|
|
32
|
+
constructor(adapter: PcapAdapter);
|
|
33
|
+
on: Emitter['on'];
|
|
34
|
+
off: Emitter['off'];
|
|
35
|
+
once: Emitter['once'];
|
|
36
|
+
/**
|
|
37
|
+
* Stop listening to the pcap adapter.
|
|
38
|
+
*/
|
|
39
|
+
stop(): void;
|
|
40
|
+
}
|
|
41
|
+
export default PassiveStatusEmitter;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* All remote database messages include this 4 byte magic value.
|
|
3
|
+
*/
|
|
4
|
+
export declare const REMOTEDB_MAGIC = 2267236782;
|
|
5
|
+
/**
|
|
6
|
+
* The consistent port on which we can query the remote db server for the port
|
|
7
|
+
*/
|
|
8
|
+
export declare const REMOTEDB_SERVER_QUERY_PORT = 12523;
|