alphatheta-connect 0.26.1 → 0.27.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/lib/cli.js +160 -32
- package/lib/cli.js.map +1 -1
- package/lib/control/index.d.ts +18 -2
- package/lib/control/load.d.ts +29 -0
- package/lib/control/stagehand.d.ts +14 -4
- package/lib/index.d.ts +5 -0
- package/lib/index.js +945 -33
- package/lib/index.js.map +1 -1
- package/lib/network.d.ts +7 -1
- package/lib/remote/hue.d.ts +66 -0
- package/lib/remote/index.d.ts +100 -0
- package/lib/remote/server.d.ts +33 -0
- package/package.json +1 -1
package/lib/cli.js
CHANGED
|
@@ -811,14 +811,17 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
811
811
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
812
812
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
813
813
|
};
|
|
814
|
-
var _Control_hostDevice, _Control_beatSocket, _Control_correlationByte;
|
|
814
|
+
var _Control_hostDevice, _Control_beatSocket, _Control_statusSocket, _Control_correlationByte;
|
|
815
815
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
816
|
-
exports.makePlaystatePacket = void 0;
|
|
816
|
+
exports.makePlaystatePacket = exports.makeLoadTrackPacket = void 0;
|
|
817
817
|
const constants_1 = __webpack_require__(/*! src/constants */ "./src/constants.ts");
|
|
818
818
|
const types_1 = __webpack_require__(/*! src/types */ "./src/types.ts");
|
|
819
819
|
const utils_1 = __webpack_require__(/*! src/utils */ "./src/utils/index.ts");
|
|
820
820
|
const udp_1 = __webpack_require__(/*! src/utils/udp */ "./src/utils/udp.ts");
|
|
821
|
+
const load_1 = __webpack_require__(/*! ./load */ "./src/control/load.ts");
|
|
821
822
|
const stagehand_1 = __webpack_require__(/*! ./stagehand */ "./src/control/stagehand.ts");
|
|
823
|
+
var load_2 = __webpack_require__(/*! ./load */ "./src/control/load.ts");
|
|
824
|
+
Object.defineProperty(exports, "makeLoadTrackPacket", ({ enumerable: true, get: function () { return load_2.makeLoadTrackPacket; } }));
|
|
822
825
|
const STATE_MAP = {
|
|
823
826
|
[types_1.CDJStatus.PlayState.Cued]: 0x01,
|
|
824
827
|
[types_1.CDJStatus.PlayState.Playing]: 0x00,
|
|
@@ -839,20 +842,51 @@ const makePlaystatePacket = ({ hostDevice, device, playState }) => Uint8Array.fr
|
|
|
839
842
|
]);
|
|
840
843
|
exports.makePlaystatePacket = makePlaystatePacket;
|
|
841
844
|
class Control {
|
|
842
|
-
constructor(beatSocket, hostDevice) {
|
|
845
|
+
constructor(beatSocket, hostDevice, statusSocket = null) {
|
|
843
846
|
_Control_hostDevice.set(this, void 0);
|
|
844
847
|
/**
|
|
845
848
|
* The socket used to send control packets
|
|
846
849
|
*/
|
|
847
850
|
_Control_beatSocket.set(this, void 0);
|
|
851
|
+
/**
|
|
852
|
+
* The socket bound to the status port, which the Load Track command must
|
|
853
|
+
* leave from: a CDJ-3000 ignores it from any other port. Absent when the
|
|
854
|
+
* control service was built without one.
|
|
855
|
+
*/
|
|
856
|
+
_Control_statusSocket.set(this, void 0);
|
|
848
857
|
/**
|
|
849
858
|
* Randomized correlation byte per session for Stagehand commands
|
|
850
859
|
*/
|
|
851
860
|
_Control_correlationByte.set(this, void 0);
|
|
852
861
|
__classPrivateFieldSet(this, _Control_beatSocket, beatSocket, "f");
|
|
862
|
+
__classPrivateFieldSet(this, _Control_statusSocket, statusSocket, "f");
|
|
853
863
|
__classPrivateFieldSet(this, _Control_hostDevice, hostDevice, "f");
|
|
854
864
|
__classPrivateFieldSet(this, _Control_correlationByte, Math.floor(Math.random() * 256), "f");
|
|
855
865
|
}
|
|
866
|
+
/**
|
|
867
|
+
* Send one Stagehand transport packet (0x07) carrying `op` with the press /
|
|
868
|
+
* release flag, for opcodes the named methods do not cover.
|
|
869
|
+
*/
|
|
870
|
+
async transport(device, op, press) {
|
|
871
|
+
const p = (0, stagehand_1.makeStagehandTransportPacket)(__classPrivateFieldGet(this, _Control_hostDevice, "f"), op, press, __classPrivateFieldGet(this, _Control_correlationByte, "f"));
|
|
872
|
+
await (0, udp_1.udpSend)(__classPrivateFieldGet(this, _Control_beatSocket, "f"), p, constants_1.BEAT_PORT, device.ip.address);
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Tell a player to load a track, as rekordbox does when a row is dragged
|
|
876
|
+
* onto a deck: the Load Track command (0x19) from our status port to the
|
|
877
|
+
* player's. The player answers 0x1a and the load shows in its next status.
|
|
878
|
+
*
|
|
879
|
+
* `source` names where the track lives: a device number and its slot (a
|
|
880
|
+
* player's USB or SD, or the rekordbox slot of a library served over the
|
|
881
|
+
* link).
|
|
882
|
+
*/
|
|
883
|
+
async loadTrack(device, trackId, source) {
|
|
884
|
+
if (__classPrivateFieldGet(this, _Control_statusSocket, "f") === null) {
|
|
885
|
+
throw new Error('loadTrack needs the status socket; connect through ProlinkNetwork');
|
|
886
|
+
}
|
|
887
|
+
const p = (0, load_1.makeLoadTrackPacket)(__classPrivateFieldGet(this, _Control_hostDevice, "f"), device, trackId, source);
|
|
888
|
+
await (0, udp_1.udpSend)(__classPrivateFieldGet(this, _Control_statusSocket, "f"), p, constants_1.STATUS_PORT, device.ip.address);
|
|
889
|
+
}
|
|
856
890
|
/**
|
|
857
891
|
* Start or stop a CDJ on the network.
|
|
858
892
|
* Delegates automatically to Stagehand-specific transport control if connected in Stagehand mode.
|
|
@@ -918,10 +952,79 @@ class Control {
|
|
|
918
952
|
await (0, udp_1.udpSend)(__classPrivateFieldGet(this, _Control_beatSocket, "f"), p, constants_1.STATUS_PORT, device.ip.address);
|
|
919
953
|
}
|
|
920
954
|
}
|
|
921
|
-
_Control_hostDevice = new WeakMap(), _Control_beatSocket = new WeakMap(), _Control_correlationByte = new WeakMap();
|
|
955
|
+
_Control_hostDevice = new WeakMap(), _Control_beatSocket = new WeakMap(), _Control_statusSocket = new WeakMap(), _Control_correlationByte = new WeakMap();
|
|
922
956
|
exports["default"] = Control;
|
|
923
957
|
|
|
924
958
|
|
|
959
|
+
/***/ },
|
|
960
|
+
|
|
961
|
+
/***/ "./src/control/load.ts"
|
|
962
|
+
/*!*****************************!*\
|
|
963
|
+
!*** ./src/control/load.ts ***!
|
|
964
|
+
\*****************************/
|
|
965
|
+
(__unused_webpack_module, exports, __webpack_require__) {
|
|
966
|
+
|
|
967
|
+
"use strict";
|
|
968
|
+
|
|
969
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
970
|
+
exports.LOAD_TRACK_LENGTH = void 0;
|
|
971
|
+
exports.makeLoadTrackPacket = makeLoadTrackPacket;
|
|
972
|
+
const constants_1 = __webpack_require__(/*! src/constants */ "./src/constants.ts");
|
|
973
|
+
const types_1 = __webpack_require__(/*! src/types */ "./src/types.ts");
|
|
974
|
+
const utils_1 = __webpack_require__(/*! src/utils */ "./src/utils/index.ts");
|
|
975
|
+
/**
|
|
976
|
+
* Byte length of a Load Track command.
|
|
977
|
+
*/
|
|
978
|
+
exports.LOAD_TRACK_LENGTH = 0x58;
|
|
979
|
+
/**
|
|
980
|
+
* Generates the Load Track command (0x19, 88 bytes), unicast to a player's
|
|
981
|
+
* status port.
|
|
982
|
+
*
|
|
983
|
+
* Byte for byte what rekordbox 7.2 sends a CDJ-3000 (captured 2026-09-13 and
|
|
984
|
+
* verified live on two CDJ-3000s): the status-style header with a 20-byte
|
|
985
|
+
* name, `01 01`, our device number, the length `0034` of what follows, our
|
|
986
|
+
* number again, then the source device, slot and track type, the track ID
|
|
987
|
+
* big-endian, `32` at 0x33, the destination player counted from zero at
|
|
988
|
+
* 0x40, and `32` at 0x4b.
|
|
989
|
+
*
|
|
990
|
+
* A player only honours the command from the address its keep-alives
|
|
991
|
+
* announced and from port 50002.
|
|
992
|
+
*/
|
|
993
|
+
function makeLoadTrackPacket(hostDevice, device, trackId, source) {
|
|
994
|
+
var _a;
|
|
995
|
+
const packet = new Uint8Array(exports.LOAD_TRACK_LENGTH);
|
|
996
|
+
// 0-9: magic header
|
|
997
|
+
packet.set(constants_1.PROLINK_HEADER, 0);
|
|
998
|
+
// 10: kind 0x19
|
|
999
|
+
packet[10] = 0x19;
|
|
1000
|
+
// 11-30: device name (20 bytes)
|
|
1001
|
+
packet.set((0, utils_1.buildName)(hostDevice), 11);
|
|
1002
|
+
// 31-35: 01, subtype 01, our number, then the length 0x0034 of what follows
|
|
1003
|
+
packet[31] = 0x01;
|
|
1004
|
+
packet[32] = 0x01;
|
|
1005
|
+
packet[33] = hostDevice.id;
|
|
1006
|
+
packet[34] = 0x00;
|
|
1007
|
+
packet[35] = 0x34;
|
|
1008
|
+
// 36: our number again
|
|
1009
|
+
packet[36] = hostDevice.id;
|
|
1010
|
+
// 40-42: the source device, its slot and the track type
|
|
1011
|
+
packet[40] = source.deviceId;
|
|
1012
|
+
packet[41] = source.slot;
|
|
1013
|
+
packet[42] = (_a = source.trackType) !== null && _a !== void 0 ? _a : types_1.TrackType.RB;
|
|
1014
|
+
// 44-47: the track ID, big-endian
|
|
1015
|
+
packet[44] = (trackId >>> 24) & 0xff;
|
|
1016
|
+
packet[45] = (trackId >>> 16) & 0xff;
|
|
1017
|
+
packet[46] = (trackId >>> 8) & 0xff;
|
|
1018
|
+
packet[47] = trackId & 0xff;
|
|
1019
|
+
packet[0x33] = 0x32;
|
|
1020
|
+
// 0x40: the player to load onto, counted from zero; a player accepts the
|
|
1021
|
+
// command by address regardless
|
|
1022
|
+
packet[0x40] = Math.max(device.id - 1, 0);
|
|
1023
|
+
packet[0x4b] = 0x32;
|
|
1024
|
+
return packet;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
|
|
925
1028
|
/***/ },
|
|
926
1029
|
|
|
927
1030
|
/***/ "./src/control/stagehand.ts"
|
|
@@ -938,59 +1041,75 @@ exports.makeStagehandPrefWritePacket = makeStagehandPrefWritePacket;
|
|
|
938
1041
|
const constants_1 = __webpack_require__(/*! src/constants */ "./src/constants.ts");
|
|
939
1042
|
const utils_1 = __webpack_require__(/*! src/utils */ "./src/utils/index.ts");
|
|
940
1043
|
/**
|
|
941
|
-
*
|
|
1044
|
+
* The name field of a Stagehand unicast frame: 19 bytes, one shorter than the
|
|
1045
|
+
* broadcast frames' 20, so the bytes that follow it sit at offset 30.
|
|
1046
|
+
*/
|
|
1047
|
+
function buildUnicastName(device) {
|
|
1048
|
+
return (0, utils_1.buildName)(device).subarray(0, 19);
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Generates a Stagehand transport control packet (0x07, 48 bytes).
|
|
1052
|
+
*
|
|
1053
|
+
* Byte for byte what the iPad app sends a CDJ-3000 (captured 2026-05-23,
|
|
1054
|
+
* `stagehand-allproto-20260523T142615.pcap`, pinned in the tests): the
|
|
1055
|
+
* unicast header `03 01 00`, a byte that varies per command, the length
|
|
1056
|
+
* `0030`, then `3a`, `01`, the opcode and the press flag each in their own
|
|
1057
|
+
* 16-bit slot.
|
|
942
1058
|
*
|
|
943
1059
|
* @param hostDevice - The Stagehand device posing as sender
|
|
944
|
-
* @param op - The command opcode (e.g. 0x0f, 0x14, 0x18, 0x1a, 0x1b)
|
|
1060
|
+
* @param op - The command opcode (e.g. 0x0f, 0x14, 0x18, 0x19, 0x1a, 0x1b)
|
|
945
1061
|
* @param press - Whether the action is press (true) or release (false)
|
|
946
|
-
* @param correlationByte - The
|
|
1062
|
+
* @param correlationByte - The per-session correlation byte
|
|
947
1063
|
*/
|
|
948
1064
|
function makeStagehandTransportPacket(hostDevice, op, press, correlationByte) {
|
|
949
|
-
const packet = new Uint8Array(
|
|
1065
|
+
const packet = new Uint8Array(48);
|
|
950
1066
|
// 0-9: magic header
|
|
951
1067
|
packet.set(constants_1.PROLINK_HEADER, 0);
|
|
952
1068
|
// 10: opcode 0x07
|
|
953
1069
|
packet[10] = 0x07;
|
|
954
|
-
// 11-
|
|
955
|
-
packet.set((
|
|
956
|
-
//
|
|
1070
|
+
// 11-29: device name
|
|
1071
|
+
packet.set(buildUnicastName(hostDevice), 11);
|
|
1072
|
+
// 30-32: unicast header 03 01 00
|
|
1073
|
+
packet[30] = 0x03;
|
|
957
1074
|
packet[31] = 0x01;
|
|
958
|
-
|
|
959
|
-
packet[32] = 0x03;
|
|
1075
|
+
packet[32] = 0x00;
|
|
960
1076
|
// 33: per-session correlation byte
|
|
961
1077
|
packet[33] = correlationByte;
|
|
962
|
-
// 34-35:
|
|
1078
|
+
// 34-35: packet length 0x0030 (48 bytes)
|
|
963
1079
|
packet[34] = 0x00;
|
|
964
1080
|
packet[35] = 0x30;
|
|
965
|
-
//
|
|
966
|
-
packet[
|
|
967
|
-
//
|
|
968
|
-
packet[
|
|
969
|
-
//
|
|
970
|
-
packet[
|
|
1081
|
+
// 39: Stagehand sub-id 0x3a
|
|
1082
|
+
packet[39] = 0x3a;
|
|
1083
|
+
// 41: 0x01
|
|
1084
|
+
packet[41] = 0x01;
|
|
1085
|
+
// 43: command opcode
|
|
1086
|
+
packet[43] = op;
|
|
1087
|
+
// 45: press/release flag
|
|
1088
|
+
packet[45] = press ? 0x01 : 0x00;
|
|
971
1089
|
return packet;
|
|
972
1090
|
}
|
|
973
1091
|
/**
|
|
974
|
-
* Generates a Stagehand preference write packet (0x6b,
|
|
1092
|
+
* Generates a Stagehand preference write packet (0x6b, 116 bytes).
|
|
1093
|
+
*
|
|
1094
|
+
* As captured from the iPad app (same capture as the transport packet): the
|
|
1095
|
+
* unicast header `03 01 00 3a`, the length `0050`, the write flag, then the
|
|
1096
|
+
* preference slots.
|
|
975
1097
|
*
|
|
976
1098
|
* @param hostDevice - The Stagehand device posing as sender
|
|
977
1099
|
* @param options - The preferences to write (onAir, quantize)
|
|
978
1100
|
*/
|
|
979
1101
|
function makeStagehandPrefWritePacket(hostDevice, options) {
|
|
980
|
-
const packet = new Uint8Array(
|
|
1102
|
+
const packet = new Uint8Array(116);
|
|
981
1103
|
// 0-9: magic header
|
|
982
1104
|
packet.set(constants_1.PROLINK_HEADER, 0);
|
|
983
1105
|
// 10: opcode 0x6b
|
|
984
1106
|
packet[10] = 0x6b;
|
|
985
|
-
// 11-
|
|
986
|
-
|
|
987
|
-
//
|
|
988
|
-
|
|
989
|
-
packet.set(name, 11);
|
|
990
|
-
// 31: 0x01 (subscription-id-a constant)
|
|
1107
|
+
// 11-29: device name
|
|
1108
|
+
packet.set(buildUnicastName(hostDevice), 11);
|
|
1109
|
+
// 30-32: unicast header 03 01 00
|
|
1110
|
+
packet[30] = 0x03;
|
|
991
1111
|
packet[31] = 0x01;
|
|
992
|
-
|
|
993
|
-
packet[32] = 0x03;
|
|
1112
|
+
packet[32] = 0x00;
|
|
994
1113
|
// 33: Stagehand sub-id constant 0x3a
|
|
995
1114
|
packet[33] = 0x3a;
|
|
996
1115
|
// 34-35: body length 0x0050 (80 bytes)
|
|
@@ -7176,9 +7295,9 @@ class ProlinkNetwork {
|
|
|
7176
7295
|
// Create unified database
|
|
7177
7296
|
const database = new db_1.default(localdb, remotedb, __classPrivateFieldGet(this, _ProlinkNetwork_deviceManager, "f"), __classPrivateFieldGet(this, _ProlinkNetwork_logger, "f"));
|
|
7178
7297
|
// Create controller service
|
|
7179
|
-
const control = new control_1.default(__classPrivateFieldGet(this, _ProlinkNetwork_beatSocket, "f"), vcdj);
|
|
7298
|
+
const control = new control_1.default(__classPrivateFieldGet(this, _ProlinkNetwork_beatSocket, "f"), vcdj, __classPrivateFieldGet(this, _ProlinkNetwork_statusSocket, "f"));
|
|
7180
7299
|
__classPrivateFieldSet(this, _ProlinkNetwork_state, types_1.NetworkState.Connected, "f");
|
|
7181
|
-
__classPrivateFieldSet(this, _ProlinkNetwork_connection, { announcer, heartbeat, control, remotedb, localdb, database }, "f");
|
|
7300
|
+
__classPrivateFieldSet(this, _ProlinkNetwork_connection, { vcdj, announcer, heartbeat, control, remotedb, localdb, database }, "f");
|
|
7182
7301
|
tx.finish();
|
|
7183
7302
|
}
|
|
7184
7303
|
/**
|
|
@@ -7265,6 +7384,15 @@ class ProlinkNetwork {
|
|
|
7265
7384
|
get positionEmitter() {
|
|
7266
7385
|
return __classPrivateFieldGet(this, _ProlinkNetwork_state, "f") === types_1.NetworkState.Connected ? __classPrivateFieldGet(this, _ProlinkNetwork_positionEmitter, "f") : null;
|
|
7267
7386
|
}
|
|
7387
|
+
/**
|
|
7388
|
+
* The device this network announces itself as, once connected: the
|
|
7389
|
+
* `hostDevice` the status emitter's media query and the control service
|
|
7390
|
+
* send from.
|
|
7391
|
+
*/
|
|
7392
|
+
get virtualDevice() {
|
|
7393
|
+
var _a, _b;
|
|
7394
|
+
return (_b = (_a = __classPrivateFieldGet(this, _ProlinkNetwork_connection, "f")) === null || _a === void 0 ? void 0 : _a.vcdj) !== null && _b !== void 0 ? _b : null;
|
|
7395
|
+
}
|
|
7268
7396
|
/**
|
|
7269
7397
|
* Get the {@link Control} service. This service can be used to control the
|
|
7270
7398
|
* Playstate of CDJs on the network.
|