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.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +166 -0
  3. package/lib/artwork/index.d.ts +9 -0
  4. package/lib/artwork/parsers/aiff.d.ts +2 -0
  5. package/lib/artwork/parsers/flac.d.ts +2 -0
  6. package/lib/artwork/parsers/id3.d.ts +2 -0
  7. package/lib/artwork/parsers/index.d.ts +5 -0
  8. package/lib/artwork/parsers/mp4.d.ts +2 -0
  9. package/lib/artwork/parsers/utils.d.ts +3 -0
  10. package/lib/artwork/reader.d.ts +15 -0
  11. package/lib/artwork/types.d.ts +44 -0
  12. package/lib/cli/index.d.ts +1 -0
  13. package/lib/cli.js +11431 -0
  14. package/lib/cli.js.map +1 -0
  15. package/lib/constants.d.ts +40 -0
  16. package/lib/control/index.d.ts +20 -0
  17. package/lib/db/getArtworkFromFile.d.ts +13 -0
  18. package/lib/db/getArtworkThumbnail.d.ts +29 -0
  19. package/lib/db/getFile.d.ts +29 -0
  20. package/lib/db/getMetadata.d.ts +28 -0
  21. package/lib/db/getPlaylist.d.ts +26 -0
  22. package/lib/db/getWaveforms.d.ts +29 -0
  23. package/lib/db/index.d.ts +70 -0
  24. package/lib/db/utils.d.ts +7 -0
  25. package/lib/devices/index.d.ts +66 -0
  26. package/lib/devices/utils.d.ts +5 -0
  27. package/lib/entities.d.ts +122 -0
  28. package/lib/index.d.ts +10 -0
  29. package/lib/index.js +13381 -0
  30. package/lib/index.js.map +1 -0
  31. package/lib/localdb/database-adapter.d.ts +49 -0
  32. package/lib/localdb/index.d.ts +104 -0
  33. package/lib/localdb/onelibrary-schema.d.ts +250 -0
  34. package/lib/localdb/onelibrary.d.ts +185 -0
  35. package/lib/localdb/orm.d.ts +60 -0
  36. package/lib/localdb/rekordbox.d.ts +112 -0
  37. package/lib/localdb/schema.d.ts +1 -0
  38. package/lib/localdb/utils.d.ts +5 -0
  39. package/lib/mixstatus/index.d.ts +109 -0
  40. package/lib/mixstatus/utils.d.ts +9 -0
  41. package/lib/network.d.ts +201 -0
  42. package/lib/nfs/index.d.ts +63 -0
  43. package/lib/nfs/programs.d.ts +61 -0
  44. package/lib/nfs/rpc.d.ts +60 -0
  45. package/lib/nfs/utils.d.ts +4 -0
  46. package/lib/nfs/xdr.d.ts +17 -0
  47. package/lib/passive/alphatheta.d.ts +94 -0
  48. package/lib/passive/devices.d.ts +64 -0
  49. package/lib/passive/index.d.ts +169 -0
  50. package/lib/passive/localdb.d.ts +122 -0
  51. package/lib/passive/pcap-adapter.d.ts +77 -0
  52. package/lib/passive/position.d.ts +32 -0
  53. package/lib/passive/remotedb.d.ts +49 -0
  54. package/lib/passive/status.d.ts +41 -0
  55. package/lib/remotedb/constants.d.ts +8 -0
  56. package/lib/remotedb/fields.d.ts +169 -0
  57. package/lib/remotedb/index.d.ts +160 -0
  58. package/lib/remotedb/message/index.d.ts +39 -0
  59. package/lib/remotedb/message/item.d.ts +530 -0
  60. package/lib/remotedb/message/response.d.ts +18 -0
  61. package/lib/remotedb/message/types.d.ts +227 -0
  62. package/lib/remotedb/queries.d.ts +99 -0
  63. package/lib/remotedb/utils.d.ts +23 -0
  64. package/lib/status/index.d.ts +39 -0
  65. package/lib/status/media.d.ts +22 -0
  66. package/lib/status/position.d.ts +29 -0
  67. package/lib/status/types.d.ts +170 -0
  68. package/lib/status/utils.d.ts +19 -0
  69. package/lib/types.d.ts +457 -0
  70. package/lib/types.js +274 -0
  71. package/lib/types.js.map +1 -0
  72. package/lib/utils/converters.d.ts +17 -0
  73. package/lib/utils/index.d.ts +28 -0
  74. package/lib/utils/telemetry.d.ts +82 -0
  75. package/lib/utils/udp.d.ts +20 -0
  76. package/lib/virtualcdj/index.d.ts +33 -0
  77. 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;