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,112 @@
|
|
|
1
|
+
import { Track } from "../entities";
|
|
2
|
+
import { MetadataORM } from "./orm";
|
|
3
|
+
import { BeatGrid, CueAndLoop, ExtendedCue, SongStructure, WaveformHD, WaveformPreviewData } from "../types";
|
|
4
|
+
import { TelemetrySpan as Span } from "../utils/telemetry";
|
|
5
|
+
/**
|
|
6
|
+
* The provided function should resolve ANLZ files into buffers. Typically
|
|
7
|
+
* you would just read the file, but in the case of the prolink network, this
|
|
8
|
+
* would handle loading the file over NFS.
|
|
9
|
+
*/
|
|
10
|
+
type AnlzResolver = (path: string) => Promise<Buffer>;
|
|
11
|
+
/**
|
|
12
|
+
* Data returned from loading DAT anlz files
|
|
13
|
+
*/
|
|
14
|
+
interface AnlzResponseDAT {
|
|
15
|
+
/**
|
|
16
|
+
* Embedded beat grid information
|
|
17
|
+
*/
|
|
18
|
+
beatGrid: BeatGrid | null;
|
|
19
|
+
/**
|
|
20
|
+
* Embedded cue and loop information
|
|
21
|
+
*/
|
|
22
|
+
cueAndLoops: CueAndLoop[] | null;
|
|
23
|
+
/**
|
|
24
|
+
* Standard waveform preview (400 bytes, PWAV tag)
|
|
25
|
+
*/
|
|
26
|
+
waveformPreview: WaveformPreviewData | null;
|
|
27
|
+
/**
|
|
28
|
+
* Tiny waveform preview (100 bytes, PWV2 tag)
|
|
29
|
+
*/
|
|
30
|
+
waveformTiny: WaveformPreviewData | null;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Data returned from loading EXT anlz files
|
|
34
|
+
*/
|
|
35
|
+
interface AnlzResponseEXT {
|
|
36
|
+
/**
|
|
37
|
+
* HD Waveform information (PWV5 tag)
|
|
38
|
+
*/
|
|
39
|
+
waveformHd: WaveformHD | null;
|
|
40
|
+
/**
|
|
41
|
+
* Extended cues with colors and comments (PCO2 tag)
|
|
42
|
+
*/
|
|
43
|
+
extendedCues: ExtendedCue[] | null;
|
|
44
|
+
/**
|
|
45
|
+
* Song structure / phrase analysis (PSSI tag)
|
|
46
|
+
*/
|
|
47
|
+
songStructure: SongStructure | null;
|
|
48
|
+
/**
|
|
49
|
+
* Monochrome detailed waveform (PWV3 tag)
|
|
50
|
+
*/
|
|
51
|
+
waveformDetail: Uint8Array | null;
|
|
52
|
+
/**
|
|
53
|
+
* Color waveform preview (PWV4 tag, 7200 bytes = 1200 columns × 6 bytes)
|
|
54
|
+
*/
|
|
55
|
+
waveformColorPreview: Uint8Array | null;
|
|
56
|
+
}
|
|
57
|
+
interface AnlzResponse {
|
|
58
|
+
DAT: AnlzResponseDAT;
|
|
59
|
+
EXT: AnlzResponseEXT;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Details about the current state of the hydtration task
|
|
63
|
+
*/
|
|
64
|
+
export interface HydrationProgress {
|
|
65
|
+
/**
|
|
66
|
+
* The specific table that progress is being reported for
|
|
67
|
+
*/
|
|
68
|
+
table: string;
|
|
69
|
+
/**
|
|
70
|
+
* The total progress steps for this table
|
|
71
|
+
*/
|
|
72
|
+
total: number;
|
|
73
|
+
/**
|
|
74
|
+
* The completed number of progress steps
|
|
75
|
+
*/
|
|
76
|
+
complete: number;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Options to hydrate the database
|
|
80
|
+
*/
|
|
81
|
+
interface Options {
|
|
82
|
+
/**
|
|
83
|
+
* The metadata ORM of which the tables will be hydrated
|
|
84
|
+
*/
|
|
85
|
+
orm: MetadataORM;
|
|
86
|
+
/**
|
|
87
|
+
* This buffer should contain the Rekordbox pdb file contents. It will be
|
|
88
|
+
* used to do the hydration
|
|
89
|
+
*/
|
|
90
|
+
pdbData: Buffer;
|
|
91
|
+
/**
|
|
92
|
+
* Sentry tracing span for the parent transaction
|
|
93
|
+
*/
|
|
94
|
+
span?: Span;
|
|
95
|
+
/**
|
|
96
|
+
* For larger music collections, it may take some time to load everything,
|
|
97
|
+
* especially when limited by IO. When hydration progresses this function
|
|
98
|
+
* will be called.
|
|
99
|
+
*/
|
|
100
|
+
onProgress?: (progress: HydrationProgress) => void;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Given a rekordbox pdb file contents. This function will hydrate the provided
|
|
104
|
+
* database with all entities from the Rekordbox database. This includes all
|
|
105
|
+
* track metadata, including analyzed metadata (such as beatgrids and waveforms).
|
|
106
|
+
*/
|
|
107
|
+
export declare function hydrateDatabase({ pdbData, span, ...options }: Options): Promise<void>;
|
|
108
|
+
/**
|
|
109
|
+
* Loads the ANLZ data of a Track entity from the analyzePath.
|
|
110
|
+
*/
|
|
111
|
+
export declare function loadAnlz<T extends keyof AnlzResponse>(track: Track, type: T, anlzResolver: AnlzResolver): Promise<AnlzResponse[T]>;
|
|
112
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const generateSchema: () => string;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { CueAndLoop, HotcueButton } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Create a CueAndLoop entry given common parameters
|
|
4
|
+
*/
|
|
5
|
+
export declare const makeCueLoopEntry: (isCue: boolean, isLoop: boolean, offset: number, length: number, button: false | HotcueButton) => null | CueAndLoop;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import { CDJStatus, DeviceID, MixstatusMode } from "../types";
|
|
4
|
+
export interface MixstatusConfig {
|
|
5
|
+
/**
|
|
6
|
+
* Selects the mixstatus reporting mode
|
|
7
|
+
*/
|
|
8
|
+
mode: MixstatusMode;
|
|
9
|
+
/**
|
|
10
|
+
* Specifies the duration in seconds that no tracks must be on air. This can
|
|
11
|
+
* be thought of as how long 'air silence' is reasonable in a set before a
|
|
12
|
+
* separate one is considered have begun.
|
|
13
|
+
*
|
|
14
|
+
* @default 30 (half a minute)
|
|
15
|
+
*/
|
|
16
|
+
timeBetweenSets: number;
|
|
17
|
+
/**
|
|
18
|
+
* Indicates if the status objects reported should have their on-air flag
|
|
19
|
+
* read. Setting this to false will degrade the functionality of the processor
|
|
20
|
+
* such that it will not consider the value of isOnAir and always assume CDJs
|
|
21
|
+
* are live.
|
|
22
|
+
*
|
|
23
|
+
* @default true
|
|
24
|
+
*/
|
|
25
|
+
useOnAirStatus: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Configures how many beats a track may not be live or playing for it to
|
|
28
|
+
* still be considered active.
|
|
29
|
+
*
|
|
30
|
+
* @default 8 (two bars)
|
|
31
|
+
*/
|
|
32
|
+
allowedInterruptBeats: number;
|
|
33
|
+
/**
|
|
34
|
+
* Configures how many beats the track must consecutively be playing for
|
|
35
|
+
* (since the beat it was cued at) until the track is considered to be
|
|
36
|
+
* active.
|
|
37
|
+
*
|
|
38
|
+
* Used for MixstatusMode.SmartTiming
|
|
39
|
+
*
|
|
40
|
+
* @default 128 (2 phrases)
|
|
41
|
+
*/
|
|
42
|
+
beatsUntilReported: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The interface the mix status event emitter should follow
|
|
46
|
+
*/
|
|
47
|
+
interface MixstatusEvents {
|
|
48
|
+
/**
|
|
49
|
+
* Fired when a track is considered to be on-air and is being heard by the
|
|
50
|
+
* audience
|
|
51
|
+
*/
|
|
52
|
+
nowPlaying: (state: CDJStatus.State) => void;
|
|
53
|
+
/**
|
|
54
|
+
* Fired when a track has stopped and is completely offair
|
|
55
|
+
*/
|
|
56
|
+
stopped: (opt: {
|
|
57
|
+
deviceId: DeviceID;
|
|
58
|
+
}) => void;
|
|
59
|
+
/**
|
|
60
|
+
* Fired when a DJ set first starts
|
|
61
|
+
*/
|
|
62
|
+
setStarted: () => void;
|
|
63
|
+
/**
|
|
64
|
+
* Fired when tracks have been stopped
|
|
65
|
+
*/
|
|
66
|
+
setEnded: () => void;
|
|
67
|
+
}
|
|
68
|
+
type Emitter = StrictEventEmitter<EventEmitter, MixstatusEvents>;
|
|
69
|
+
/**
|
|
70
|
+
* MixstatusProcessor is a configurable processor which when fed device state
|
|
71
|
+
* will attempt to accurately determine events that happen within the DJ set.
|
|
72
|
+
*
|
|
73
|
+
* The following events are fired:
|
|
74
|
+
*
|
|
75
|
+
* - nowPlaying: The track is considered playing and on air to the audience.
|
|
76
|
+
* - stopped: The track was stopped / paused / went off-air.
|
|
77
|
+
*
|
|
78
|
+
* Additionally the following non-track status are reported:
|
|
79
|
+
*
|
|
80
|
+
* - setStarted: The first track has begun playing.
|
|
81
|
+
* - setEnded: The TimeBetweenSets has passed since any tracks were live.
|
|
82
|
+
*
|
|
83
|
+
* See Config for configuration options.
|
|
84
|
+
*
|
|
85
|
+
* Config options may be changed after the processor has been created and is
|
|
86
|
+
* actively receiving state updates.
|
|
87
|
+
*/
|
|
88
|
+
export declare class MixstatusProcessor {
|
|
89
|
+
#private;
|
|
90
|
+
constructor(config?: Partial<MixstatusConfig>);
|
|
91
|
+
/**
|
|
92
|
+
* Update the configuration
|
|
93
|
+
*/
|
|
94
|
+
configure(config?: Partial<MixstatusConfig>): void;
|
|
95
|
+
on: Emitter['on'];
|
|
96
|
+
off: Emitter['off'];
|
|
97
|
+
once: Emitter['once'];
|
|
98
|
+
removeAllListeners: (event?: keyof MixstatusEvents) => EventEmitter<[never]>;
|
|
99
|
+
/**
|
|
100
|
+
* Feed a CDJStatus state object to the mix state processor
|
|
101
|
+
*/
|
|
102
|
+
handleState(state: CDJStatus.State): void;
|
|
103
|
+
/**
|
|
104
|
+
* Manually reports the track that has been playing the longest which has not
|
|
105
|
+
* yet been reported as live.
|
|
106
|
+
*/
|
|
107
|
+
triggerNextTrack(): void;
|
|
108
|
+
}
|
|
109
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CDJStatus } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Returns true if the the status reports a playing state.
|
|
4
|
+
*/
|
|
5
|
+
export declare const isPlaying: (s: CDJStatus.State) => boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Returns true if the status reports a stopping state.
|
|
8
|
+
*/
|
|
9
|
+
export declare const isStopping: (s: CDJStatus.State) => boolean;
|
package/lib/network.d.ts
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { Socket } from 'dgram';
|
|
2
|
+
import { NetworkInterfaceInfoIPv4 } from 'os';
|
|
3
|
+
import Control from "./control";
|
|
4
|
+
import Database from "./db";
|
|
5
|
+
import DeviceManager from "./devices";
|
|
6
|
+
import LocalDatabase from "./localdb";
|
|
7
|
+
import { DatabasePreference } from "./localdb/database-adapter";
|
|
8
|
+
import { MixstatusProcessor } from "./mixstatus";
|
|
9
|
+
import RemoteDatabase from "./remotedb";
|
|
10
|
+
import StatusEmitter from "./status";
|
|
11
|
+
import PositionEmitter from "./status/position";
|
|
12
|
+
import { NetworkState } from "./types";
|
|
13
|
+
export interface NetworkConfig {
|
|
14
|
+
/**
|
|
15
|
+
* The network interface to listen for devices on the network over
|
|
16
|
+
*/
|
|
17
|
+
iface: NetworkInterfaceInfoIPv4;
|
|
18
|
+
/**
|
|
19
|
+
* The ID of the virtual CDJ to pose as.
|
|
20
|
+
*
|
|
21
|
+
* IMPORTANT:
|
|
22
|
+
*
|
|
23
|
+
* You will likely want to configure this to be > 6, however it is important to
|
|
24
|
+
* note, if you choose an ID within the 1-6 range, no other CDJ may exist on the
|
|
25
|
+
* network using that ID. you CAN NOT have 6 CDJs if you're using one of their slots.
|
|
26
|
+
*
|
|
27
|
+
* However, should you want to make metadata queries to a unanalized media
|
|
28
|
+
* device connected to the CDJ, or metadata queries for CD disc data, you MUST
|
|
29
|
+
* use a ID within the 1-6 range, as the CDJs will not respond to metadata
|
|
30
|
+
* requests outside of the range of 1-6
|
|
31
|
+
*
|
|
32
|
+
* Note that rekordbox analyzed media connected to the CDJ is accessed out of
|
|
33
|
+
* band of the networks remote database protocol, and is not limited by this
|
|
34
|
+
* restriction.
|
|
35
|
+
*/
|
|
36
|
+
vcdjId: number;
|
|
37
|
+
/**
|
|
38
|
+
* The name to announce the virtual CDJ as on the network.
|
|
39
|
+
*
|
|
40
|
+
* This name will appear in device lists on other Pro DJ Link equipment.
|
|
41
|
+
*
|
|
42
|
+
* @default 'ProLink-Connect'
|
|
43
|
+
*/
|
|
44
|
+
vcdjName?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Enable full startup protocol for robust device negotiation.
|
|
47
|
+
* When enabled, the virtual CDJ will go through the complete startup
|
|
48
|
+
* sequence (stages 0x0a → 0x00 → 0x02 → 0x04 → 0x06) before regular
|
|
49
|
+
* keep-alive announcements.
|
|
50
|
+
*
|
|
51
|
+
* This is recommended for production setups with CDJ-3000 and DJM-V10
|
|
52
|
+
* hardware to ensure proper device discovery and network stability.
|
|
53
|
+
*/
|
|
54
|
+
fullStartup?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Database format preference for loading rekordbox databases.
|
|
57
|
+
*
|
|
58
|
+
* - 'auto': Try OneLibrary first (rekordbox 7.x+), fall back to PDB (rekordbox 6.x)
|
|
59
|
+
* - 'oneLibrary': Only use OneLibrary format (exportLibrary.db)
|
|
60
|
+
* - 'pdb': Only use PDB format (export.pdb)
|
|
61
|
+
*
|
|
62
|
+
* @default 'auto'
|
|
63
|
+
*/
|
|
64
|
+
databasePreference?: DatabasePreference;
|
|
65
|
+
}
|
|
66
|
+
interface ConstructOpts {
|
|
67
|
+
config?: NetworkConfig;
|
|
68
|
+
announceSocket: Socket;
|
|
69
|
+
beatSocket: Socket;
|
|
70
|
+
statusSocket: Socket;
|
|
71
|
+
deviceManager: DeviceManager;
|
|
72
|
+
statusEmitter: StatusEmitter;
|
|
73
|
+
positionEmitter: PositionEmitter;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Services that are not accessible until connected
|
|
77
|
+
*/
|
|
78
|
+
type ConnectedServices = 'statusEmitter' | 'positionEmitter' | 'control' | 'db' | 'localdb' | 'remotedb' | 'mixstatus';
|
|
79
|
+
export type ConnectedProlinkNetwork = ProlinkNetwork & {
|
|
80
|
+
[P in ConnectedServices]: NonNullable<ProlinkNetwork[P]>;
|
|
81
|
+
} & {
|
|
82
|
+
state: NetworkState.Connected;
|
|
83
|
+
isConfigured: true;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Brings the Prolink network online.
|
|
87
|
+
*
|
|
88
|
+
* This is the primary entrypoint for connecting to the prolink network.
|
|
89
|
+
*/
|
|
90
|
+
export declare function bringOnline(config?: NetworkConfig): Promise<ProlinkNetwork>;
|
|
91
|
+
export declare class ProlinkNetwork {
|
|
92
|
+
#private;
|
|
93
|
+
/**
|
|
94
|
+
* @internal
|
|
95
|
+
*/
|
|
96
|
+
constructor({ config, announceSocket, beatSocket, statusSocket, deviceManager, statusEmitter, positionEmitter, }: ConstructOpts);
|
|
97
|
+
/**
|
|
98
|
+
* Configure / reconfigure the network with an explicit configuration.
|
|
99
|
+
*
|
|
100
|
+
* You may need to disconnect and re-connect the network after making a
|
|
101
|
+
* networking configuration change.
|
|
102
|
+
*/
|
|
103
|
+
configure(config: Partial<NetworkConfig>): void;
|
|
104
|
+
/**
|
|
105
|
+
* Wait for another device to show up on the network to determine which network
|
|
106
|
+
* interface to listen on.
|
|
107
|
+
*
|
|
108
|
+
* Defaults the Virtual CDJ ID to 7.
|
|
109
|
+
*/
|
|
110
|
+
autoconfigFromPeers(): Promise<void>;
|
|
111
|
+
/**
|
|
112
|
+
* Connect to the network.
|
|
113
|
+
*
|
|
114
|
+
* The network must first have been configured (either with autoconfigFromPeers
|
|
115
|
+
* or manual configuration). This will then initialize all the network services.
|
|
116
|
+
*/
|
|
117
|
+
connect(): void;
|
|
118
|
+
/**
|
|
119
|
+
* Disconnect from the network
|
|
120
|
+
*/
|
|
121
|
+
disconnect(): () => Promise<[unknown, unknown, unknown]>;
|
|
122
|
+
/**
|
|
123
|
+
* Close UDP sockets.
|
|
124
|
+
*/
|
|
125
|
+
close(): Promise<[unknown, unknown, unknown]>;
|
|
126
|
+
/**
|
|
127
|
+
* Get the current NetworkState of the network.
|
|
128
|
+
*
|
|
129
|
+
* When the network is Online you may use the deviceManager to list and react to
|
|
130
|
+
* devices on the nettwork
|
|
131
|
+
*
|
|
132
|
+
* Once the network is Connected you may use the statusEmitter to listen for
|
|
133
|
+
* player status events, query the media databases of devices using the db
|
|
134
|
+
* service (or specifically query the localdb or remotedb).
|
|
135
|
+
*/
|
|
136
|
+
get state(): NetworkState;
|
|
137
|
+
/**
|
|
138
|
+
* Check if the network has been configured. You cannot connect to the network
|
|
139
|
+
* until it has been configured.
|
|
140
|
+
*/
|
|
141
|
+
get isConfigured(): boolean;
|
|
142
|
+
/**
|
|
143
|
+
* Typescript discriminate helper. Refines the type of the network to one
|
|
144
|
+
* that reflects the connected status. Useful to avoid having to gaurd the
|
|
145
|
+
* service getters from nulls.
|
|
146
|
+
*/
|
|
147
|
+
isConnected(): this is ConnectedProlinkNetwork;
|
|
148
|
+
/**
|
|
149
|
+
* Get the {@link DeviceManager} service. This service is used to monitor and
|
|
150
|
+
* react to devices connecting and disconnecting from the prolink network.
|
|
151
|
+
*/
|
|
152
|
+
get deviceManager(): DeviceManager;
|
|
153
|
+
/**
|
|
154
|
+
* Get the {@link StatusEmitter} service. This service is used to monitor
|
|
155
|
+
* status updates on each CDJ.
|
|
156
|
+
*/
|
|
157
|
+
get statusEmitter(): StatusEmitter | null;
|
|
158
|
+
/**
|
|
159
|
+
* Get the {@link PositionEmitter} service. This service provides events with
|
|
160
|
+
* absolute playhead position updates from CDJ-3000+ devices.
|
|
161
|
+
*
|
|
162
|
+
* Position packets are sent approximately every 30ms while a track is loaded,
|
|
163
|
+
* providing precise position tracking independent of beat grids. This enables
|
|
164
|
+
* accurate timecode/video sync even during scratching, reverse play, and loops.
|
|
165
|
+
*/
|
|
166
|
+
get positionEmitter(): PositionEmitter | null;
|
|
167
|
+
/**
|
|
168
|
+
* Get the {@link Control} service. This service can be used to control the
|
|
169
|
+
* Playstate of CDJs on the network.
|
|
170
|
+
*/
|
|
171
|
+
get control(): Control | null;
|
|
172
|
+
/**
|
|
173
|
+
* Get the {@link Database} service. This service is used to retrieve
|
|
174
|
+
* metadata and listings from devices on the network, automatically choosing the
|
|
175
|
+
* best strategy to access the data.
|
|
176
|
+
*/
|
|
177
|
+
get db(): Database | null;
|
|
178
|
+
/**
|
|
179
|
+
* Get the {@link LocalDatabase} service. This service is used to query and sync
|
|
180
|
+
* metadata that is downloaded directly from the rekordbox database present
|
|
181
|
+
* on media connected to the CDJs.
|
|
182
|
+
*/
|
|
183
|
+
get localdb(): LocalDatabase | null;
|
|
184
|
+
/**
|
|
185
|
+
* Get the {@link RemoteDatabase} service. This service is used to query
|
|
186
|
+
* metadata directly from the database service running on Rekordbox and the CDJs
|
|
187
|
+
* themselves.
|
|
188
|
+
*
|
|
189
|
+
* NOTE: To use this service to access the CDJ remote database service, the
|
|
190
|
+
* Virtual CDJ must report itself as an ID between 1 and 6. This means
|
|
191
|
+
* there cannot be four physical CDJs on the network to access any CDJs
|
|
192
|
+
* remote database.
|
|
193
|
+
*/
|
|
194
|
+
get remotedb(): RemoteDatabase | null;
|
|
195
|
+
/**
|
|
196
|
+
* Get (and initialize) the {@link MixstatusProcessor} service. This service can
|
|
197
|
+
* be used to monitor the 'status' of devices on the network as a whole.
|
|
198
|
+
*/
|
|
199
|
+
get mixstatus(): MixstatusProcessor | null;
|
|
200
|
+
}
|
|
201
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Device } from "../types";
|
|
2
|
+
import { TelemetrySpan as Span } from "../utils/telemetry";
|
|
3
|
+
import { fetchFile as fetchFileCall, FileInfo } from './programs';
|
|
4
|
+
export type { FileInfo } from './programs';
|
|
5
|
+
import { RetryConfig } from './rpc';
|
|
6
|
+
export interface FetchProgress {
|
|
7
|
+
read: number;
|
|
8
|
+
total: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The slot <-> mount name mapping is well known.
|
|
12
|
+
*/
|
|
13
|
+
declare const slotMountMapping: {
|
|
14
|
+
readonly 3: "/C/";
|
|
15
|
+
readonly 2: "/B/";
|
|
16
|
+
readonly 4: "/";
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Media slots that support NFS access.
|
|
20
|
+
*/
|
|
21
|
+
export type NfsMediaSlot = keyof typeof slotMountMapping;
|
|
22
|
+
interface FetchFileOptions {
|
|
23
|
+
device: Device;
|
|
24
|
+
slot: keyof typeof slotMountMapping;
|
|
25
|
+
path: string;
|
|
26
|
+
onProgress?: Parameters<typeof fetchFileCall>[2];
|
|
27
|
+
span?: Span;
|
|
28
|
+
chunkSize?: number;
|
|
29
|
+
}
|
|
30
|
+
interface FetchFileRangeOptions {
|
|
31
|
+
device: Device;
|
|
32
|
+
slot: keyof typeof slotMountMapping;
|
|
33
|
+
path: string;
|
|
34
|
+
offset: number;
|
|
35
|
+
length: number;
|
|
36
|
+
span?: Span;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Fetch a range of bytes from a file on a device's NFS server.
|
|
40
|
+
* Optimized for partial reads (e.g., reading file headers for metadata extraction).
|
|
41
|
+
*/
|
|
42
|
+
export declare function fetchFileRange({ device, slot, path, offset, length, span, }: FetchFileRangeOptions): Promise<Buffer>;
|
|
43
|
+
/**
|
|
44
|
+
* Get file info (size, handle) without fetching the file content.
|
|
45
|
+
*/
|
|
46
|
+
export declare function getFileInfo({ device, slot, path, span, }: Omit<FetchFileOptions, 'onProgress' | 'chunkSize'>): Promise<FileInfo>;
|
|
47
|
+
/**
|
|
48
|
+
* Fetch a file from a devices NFS server.
|
|
49
|
+
*
|
|
50
|
+
* NOTE: The connection and root filehandle (The 'mounted' NFS export on the
|
|
51
|
+
* device) is cached to improve subsequent fetching performance. It's
|
|
52
|
+
* important that when the device disconnects you call the {@link
|
|
53
|
+
* resetDeviceCache} function.
|
|
54
|
+
*/
|
|
55
|
+
export declare function fetchFile({ device, slot, path, onProgress, span, chunkSize, }: FetchFileOptions): Promise<Buffer<ArrayBuffer>>;
|
|
56
|
+
/**
|
|
57
|
+
* Clear the cached NFS connection and root filehandle for the given device
|
|
58
|
+
*/
|
|
59
|
+
export declare function resetDeviceCache(device: Device): void;
|
|
60
|
+
/**
|
|
61
|
+
* Configure the retry strategy for making NFS calls using this module
|
|
62
|
+
*/
|
|
63
|
+
export declare function configureRetryStrategy(config: RetryConfig): void;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { TelemetrySpan as Span } from "../utils/telemetry";
|
|
2
|
+
import { RpcConnection, RpcProgram } from './rpc';
|
|
3
|
+
import { FetchProgress } from '.';
|
|
4
|
+
interface Program {
|
|
5
|
+
id: number;
|
|
6
|
+
version: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Queries for the listening port of a RPC program
|
|
10
|
+
*/
|
|
11
|
+
export declare function makeProgramClient(conn: RpcConnection, program: Program): Promise<RpcProgram>;
|
|
12
|
+
/**
|
|
13
|
+
* Export represents a NFS export on a remote system
|
|
14
|
+
*/
|
|
15
|
+
interface Export {
|
|
16
|
+
/**
|
|
17
|
+
* The name of the exported filesystem
|
|
18
|
+
*/
|
|
19
|
+
filesystem: string;
|
|
20
|
+
/**
|
|
21
|
+
* The groups allowed to mount this filesystem
|
|
22
|
+
*/
|
|
23
|
+
groups: string[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Attributes a remote file
|
|
27
|
+
*/
|
|
28
|
+
export interface FileInfo {
|
|
29
|
+
handle: Buffer;
|
|
30
|
+
name: string;
|
|
31
|
+
size: number;
|
|
32
|
+
type: 'null' | 'regular' | 'directory' | 'block' | 'char' | 'link';
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Request a list of export entries.
|
|
36
|
+
*/
|
|
37
|
+
export declare function getExports(conn: RpcProgram, span?: Span): Promise<Export[]>;
|
|
38
|
+
/**
|
|
39
|
+
* Mount the specified export, returning the file handle.
|
|
40
|
+
*/
|
|
41
|
+
export declare function mountFilesystem(conn: RpcProgram, { filesystem }: Export, span?: Span): Promise<Buffer<ArrayBufferLike>>;
|
|
42
|
+
/**
|
|
43
|
+
* Lookup a file within the directory of the provided file handle, returning
|
|
44
|
+
* the FileInfo object if the file can be located.
|
|
45
|
+
*/
|
|
46
|
+
export declare function lookupFile(conn: RpcProgram, handle: Buffer, filename: string, span?: Span): Promise<FileInfo>;
|
|
47
|
+
/**
|
|
48
|
+
* Lookup the absolute path to a file, given the root file handle and path,
|
|
49
|
+
*/
|
|
50
|
+
export declare function lookupPath(conn: RpcProgram, rootHandle: Buffer, filepath: string, span?: Span): Promise<FileInfo>;
|
|
51
|
+
/**
|
|
52
|
+
* Fetch the specified file the remote NFS server. This will read the entire
|
|
53
|
+
* file into memory.
|
|
54
|
+
*/
|
|
55
|
+
export declare function fetchFile(conn: RpcProgram, file: FileInfo, onProgress?: (progress: FetchProgress) => void, span?: Span, readSize?: number): Promise<Buffer<ArrayBuffer>>;
|
|
56
|
+
/**
|
|
57
|
+
* Fetch a range of bytes from a file on the remote NFS server.
|
|
58
|
+
* Unlike fetchFile, this only reads the specified range.
|
|
59
|
+
*/
|
|
60
|
+
export declare function fetchFileRange(conn: RpcProgram, file: FileInfo, offset: number, length: number, span?: Span): Promise<Buffer>;
|
|
61
|
+
export {};
|
package/lib/nfs/rpc.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Mutex } from 'async-mutex';
|
|
2
|
+
import { OperationOptions } from 'retry';
|
|
3
|
+
import { Socket } from 'dgram';
|
|
4
|
+
interface RpcCall {
|
|
5
|
+
port: number;
|
|
6
|
+
program: number;
|
|
7
|
+
version: number;
|
|
8
|
+
procedure: number;
|
|
9
|
+
data: Buffer;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Configuration for the retry strategy to use when making RPC calls
|
|
13
|
+
*
|
|
14
|
+
* @see https://www.npmjs.com/package/promise-retry#promiseretryfn-options
|
|
15
|
+
*/
|
|
16
|
+
export type RetryConfig = OperationOptions & {
|
|
17
|
+
/**
|
|
18
|
+
* Time in milliseconds to wait before a RPC transaction should timeout.
|
|
19
|
+
* @default 1000
|
|
20
|
+
*/
|
|
21
|
+
transactionTimeout?: number;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Generic RPC connection. Can be used to make RPC 2 calls to any program
|
|
25
|
+
* specified in the RpcCall.
|
|
26
|
+
*/
|
|
27
|
+
export declare class RpcConnection {
|
|
28
|
+
address: string;
|
|
29
|
+
retryConfig: RetryConfig;
|
|
30
|
+
socket: Socket;
|
|
31
|
+
mutex: Mutex;
|
|
32
|
+
xid: number;
|
|
33
|
+
constructor(address: string, retryConfig?: RetryConfig);
|
|
34
|
+
connected: boolean;
|
|
35
|
+
setupRequest({ program, version, procedure, data }: Omit<RpcCall, 'port'>): any;
|
|
36
|
+
/**
|
|
37
|
+
* Execute a RPC transaction (call and response).
|
|
38
|
+
*
|
|
39
|
+
* If a transaction does not complete after the configured timeout it will be
|
|
40
|
+
* retried with the retry configuration.
|
|
41
|
+
*/
|
|
42
|
+
call({ port, ...call }: RpcCall): Promise<Buffer<ArrayBufferLike>>;
|
|
43
|
+
disconnect(): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
type RpcProgramCall = Pick<RpcCall, 'procedure' | 'data'>;
|
|
46
|
+
/**
|
|
47
|
+
* RpcProgram is constructed with specialization details for a specific RPC
|
|
48
|
+
* program. This should be used to avoid having to repeat yourself for calls
|
|
49
|
+
* made using the RpcConnection.
|
|
50
|
+
*/
|
|
51
|
+
export declare class RpcProgram {
|
|
52
|
+
program: number;
|
|
53
|
+
version: number;
|
|
54
|
+
port: number;
|
|
55
|
+
conn: RpcConnection;
|
|
56
|
+
constructor(conn: RpcConnection, program: number, version: number, port: number);
|
|
57
|
+
call(data: RpcProgramCall): Promise<Buffer<ArrayBufferLike>>;
|
|
58
|
+
disconnect(): void;
|
|
59
|
+
}
|
|
60
|
+
export {};
|
package/lib/nfs/xdr.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RPC XDR data types. This implements nearly the entire XDR spec for the
|
|
3
|
+
* ONC-RPC protocol.
|
|
4
|
+
*/
|
|
5
|
+
export declare const rpc: any;
|
|
6
|
+
/**
|
|
7
|
+
* Portmap RPC XDR types
|
|
8
|
+
*/
|
|
9
|
+
export declare const portmap: any;
|
|
10
|
+
/**
|
|
11
|
+
* Mount RPC XDR types
|
|
12
|
+
*/
|
|
13
|
+
export declare const mount: any;
|
|
14
|
+
/**
|
|
15
|
+
* NFS RPC XDR types
|
|
16
|
+
*/
|
|
17
|
+
export declare const nfs: any;
|