alphatheta-connect 0.15.0 → 0.19.1
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/README.md +29 -3
- package/lib/artwork/index.d.ts +2 -1
- package/lib/cli.js +1122 -1332
- package/lib/cli.js.map +1 -1
- package/lib/constants.d.ts +7 -1
- package/lib/db/getArtworkFromFile.d.ts +2 -0
- package/lib/db/getFile.d.ts +7 -2
- package/lib/db/getMetadata.d.ts +3 -2
- package/lib/db/getTrackAnalysis.d.ts +28 -0
- package/lib/db/index.d.ts +8 -8
- package/lib/entities.d.ts +13 -111
- package/lib/index.d.ts +10 -2
- package/lib/index.js +1357 -1364
- package/lib/index.js.map +1 -1
- package/lib/localdb/database-adapter.d.ts +2 -47
- package/lib/localdb/index.d.ts +1 -1
- package/lib/localdb/onelibrary/index.d.ts +5 -0
- package/lib/localdb/onelibrary.d.ts +3 -181
- package/lib/localdb/rekordbox/anlz-parsers.d.ts +37 -0
- package/lib/localdb/rekordbox/entity-creators.d.ts +27 -0
- package/lib/localdb/rekordbox/hydrator.d.ts +20 -0
- package/lib/localdb/rekordbox/index.d.ts +13 -0
- package/lib/localdb/rekordbox/table-mappings.d.ts +10 -0
- package/lib/localdb/rekordbox/types.d.ts +118 -0
- package/lib/localdb/rekordbox.d.ts +6 -110
- package/lib/logger.d.ts +13 -0
- package/lib/metadata.d.ts +45 -0
- package/lib/network.d.ts +25 -0
- package/lib/nfs/index.d.ts +35 -10
- package/lib/nfs/index.test.d.ts +1 -0
- package/lib/nfs/programs.d.ts +10 -1
- package/lib/passive/index.d.ts +1 -1
- package/lib/passive/remotedb.d.ts +12 -1
- package/lib/remotedb/queries.d.ts +2 -2
- package/lib/types.d.ts +55 -78
- package/lib/types.js +21 -38
- package/lib/types.js.map +1 -1
- package/lib/utils/index.d.ts +9 -0
- package/lib/virtualcdj/index.d.ts +7 -1
- package/package.json +23 -2
- package/lib/localdb/onelibrary-schema.d.ts +0 -250
package/lib/cli.js
CHANGED
|
@@ -14,6 +14,7 @@ exports.PictureType = exports.createNfsFileReaderWithInfo = exports.createNfsFil
|
|
|
14
14
|
exports.isArtworkExtractionSupported = isArtworkExtractionSupported;
|
|
15
15
|
exports.extractArtwork = extractArtwork;
|
|
16
16
|
exports.extractArtworkFromDevice = extractArtworkFromDevice;
|
|
17
|
+
const logger_1 = __webpack_require__(/*! src/logger */ "./src/logger.ts");
|
|
17
18
|
const nfs_1 = __webpack_require__(/*! src/nfs */ "./src/nfs/index.ts");
|
|
18
19
|
const parsers_1 = __webpack_require__(/*! ./parsers */ "./src/artwork/parsers/index.ts");
|
|
19
20
|
const reader_1 = __webpack_require__(/*! ./reader */ "./src/artwork/reader.ts");
|
|
@@ -50,10 +51,14 @@ async function extractArtwork(reader) {
|
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
|
-
async function extractArtworkFromDevice(device, slot, filePath) {
|
|
54
|
+
async function extractArtworkFromDevice(device, slot, filePath, logger = logger_1.noopLogger) {
|
|
55
|
+
logger.debug(`[artwork-nfs] getFileInfo: device=${device.ip.address}, slot=${slot}, path=${filePath}`);
|
|
54
56
|
const fileInfo = await (0, nfs_1.getFileInfo)({ device, slot, path: filePath });
|
|
57
|
+
logger.debug(`[artwork-nfs] File found: ${fileInfo.size} bytes`);
|
|
55
58
|
const reader = (0, reader_1.createNfsFileReader)(device, slot, filePath, fileInfo.size);
|
|
56
|
-
|
|
59
|
+
const result = await extractArtwork(reader);
|
|
60
|
+
logger.debug(`[artwork-nfs] extractArtwork result: ${result ? `${result.mimeType} (${result.data.length}b)` : 'null'}`);
|
|
61
|
+
return result;
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
|
|
@@ -674,7 +679,7 @@ cli();
|
|
|
674
679
|
"use strict";
|
|
675
680
|
|
|
676
681
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
677
|
-
exports.VIRTUAL_CDJ_FIRMWARE = exports.VIRTUAL_CDJ_NAME = exports.PROLINK_HEADER = exports.STARTUP_STAGE_INTERVAL = exports.ANNOUNCE_INTERVAL = exports.STATUS_PORT = exports.BEAT_PORT = exports.ANNOUNCE_PORT = exports.DEFAULT_VCDJ_ID = void 0;
|
|
682
|
+
exports.MAX_CDJ_DEVICE_ID = exports.MIN_CDJ_DEVICE_ID = exports.VIRTUAL_CDJ_FIRMWARE = exports.VIRTUAL_CDJ_NAME = exports.PROLINK_HEADER = exports.STARTUP_STAGE_INTERVAL = exports.ANNOUNCE_INTERVAL = exports.STATUS_PORT = exports.BEAT_PORT = exports.ANNOUNCE_PORT = exports.DEFAULT_VCDJ_ID = void 0;
|
|
678
683
|
/**
|
|
679
684
|
* The default virtual CDJ ID to use.
|
|
680
685
|
*
|
|
@@ -715,7 +720,13 @@ exports.VIRTUAL_CDJ_NAME = 'ProLink-Connect';
|
|
|
715
720
|
* VirtualCDJFirmware is a string indicating the firmware version reported with
|
|
716
721
|
* status packets.
|
|
717
722
|
*/
|
|
718
|
-
exports.VIRTUAL_CDJ_FIRMWARE = '
|
|
723
|
+
exports.VIRTUAL_CDJ_FIRMWARE = '3.20';
|
|
724
|
+
/**
|
|
725
|
+
* CDJs use device IDs 1-6. Devices outside this range (e.g. Stagehand at 154)
|
|
726
|
+
* should not be queried for media slots or databases as they may crash.
|
|
727
|
+
*/
|
|
728
|
+
exports.MIN_CDJ_DEVICE_ID = 1;
|
|
729
|
+
exports.MAX_CDJ_DEVICE_ID = 6;
|
|
719
730
|
|
|
720
731
|
|
|
721
732
|
/***/ },
|
|
@@ -833,37 +844,46 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
833
844
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
834
845
|
exports.viaFileExtraction = viaFileExtraction;
|
|
835
846
|
const artwork_1 = __webpack_require__(/*! src/artwork */ "./src/artwork/index.ts");
|
|
847
|
+
const logger_1 = __webpack_require__(/*! src/logger */ "./src/logger.ts");
|
|
836
848
|
const types_1 = __webpack_require__(/*! src/types */ "./src/types.ts");
|
|
849
|
+
const utils_1 = __webpack_require__(/*! src/utils */ "./src/utils/index.ts");
|
|
837
850
|
const Telemetry = __importStar(__webpack_require__(/*! src/utils/telemetry */ "./src/utils/telemetry.ts"));
|
|
838
851
|
/**
|
|
839
852
|
* Extract artwork directly from an audio file via NFS.
|
|
840
853
|
*/
|
|
841
854
|
async function viaFileExtraction(device, opts) {
|
|
842
|
-
var _a, _b;
|
|
855
|
+
var _a, _b, _c;
|
|
843
856
|
const { trackSlot, track, span } = opts;
|
|
857
|
+
const logger = (_a = opts.logger) !== null && _a !== void 0 ? _a : logger_1.noopLogger;
|
|
844
858
|
if (trackSlot !== types_1.MediaSlot.USB &&
|
|
845
859
|
trackSlot !== types_1.MediaSlot.SD &&
|
|
846
860
|
trackSlot !== types_1.MediaSlot.RB) {
|
|
861
|
+
logger.debug(`[artwork-nfs] Skipping: unsupported slot ${(0, utils_1.getSlotName)(trackSlot)} (device ${device.name})`);
|
|
847
862
|
return null;
|
|
848
863
|
}
|
|
849
864
|
const slot = trackSlot;
|
|
850
865
|
if (!track.filePath) {
|
|
866
|
+
logger.debug('[artwork-nfs] Skipping: no filePath on track');
|
|
851
867
|
return null;
|
|
852
868
|
}
|
|
853
|
-
const extension = (
|
|
869
|
+
const extension = (_c = (_b = track.filePath.split('.').pop()) === null || _b === void 0 ? void 0 : _b.toLowerCase()) !== null && _c !== void 0 ? _c : '';
|
|
854
870
|
if (!(0, artwork_1.isArtworkExtractionSupported)(extension)) {
|
|
871
|
+
logger.debug(`[artwork-nfs] Skipping: unsupported extension ".${extension}" (${track.filePath})`);
|
|
855
872
|
return null;
|
|
856
873
|
}
|
|
874
|
+
logger.debug(`[artwork-nfs] Extracting from ${track.filePath} (slot=${(0, utils_1.getSlotName)(trackSlot)}, device=${device.name} @ ${device.ip.address})`);
|
|
857
875
|
const tx = span
|
|
858
876
|
? span.startChild({ op: 'getArtworkFromFile' })
|
|
859
877
|
: Telemetry.startTransaction({ name: 'getArtworkFromFile' });
|
|
860
878
|
try {
|
|
861
|
-
const artwork = await (0, artwork_1.extractArtworkFromDevice)(device, slot, track.filePath);
|
|
879
|
+
const artwork = await (0, artwork_1.extractArtworkFromDevice)(device, slot, track.filePath, logger);
|
|
862
880
|
if (!artwork) {
|
|
881
|
+
logger.debug('[artwork-nfs] No embedded artwork found in file');
|
|
863
882
|
tx.setData('result', 'no_artwork');
|
|
864
883
|
tx.finish();
|
|
865
884
|
return null;
|
|
866
885
|
}
|
|
886
|
+
logger.debug(`[artwork-nfs] Success: ${artwork.mimeType} (${artwork.data.length} bytes)`);
|
|
867
887
|
tx.setData('result', 'success');
|
|
868
888
|
tx.setData('mimeType', artwork.mimeType);
|
|
869
889
|
tx.setData('size', artwork.data.length);
|
|
@@ -871,6 +891,8 @@ async function viaFileExtraction(device, opts) {
|
|
|
871
891
|
return artwork.data;
|
|
872
892
|
}
|
|
873
893
|
catch (error) {
|
|
894
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
895
|
+
logger.warn(`[artwork-nfs] NFS extraction failed: ${msg}`);
|
|
874
896
|
tx.setData('result', 'error');
|
|
875
897
|
tx.finish();
|
|
876
898
|
Telemetry.captureException(error);
|
|
@@ -1018,12 +1040,15 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
1018
1040
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1019
1041
|
exports.viaRemote = viaRemote;
|
|
1020
1042
|
exports.viaLocal = viaLocal;
|
|
1043
|
+
const logger_1 = __webpack_require__(/*! src/logger */ "./src/logger.ts");
|
|
1021
1044
|
const nfs_1 = __webpack_require__(/*! src/nfs */ "./src/nfs/index.ts");
|
|
1022
1045
|
const types_1 = __webpack_require__(/*! src/types */ "./src/types.ts");
|
|
1023
1046
|
const Telemetry = __importStar(__webpack_require__(/*! src/utils/telemetry */ "./src/utils/telemetry.ts"));
|
|
1024
1047
|
const CHUNK_SIZE = 8192; // Maximum allowed XDR read size
|
|
1025
1048
|
function viaRemote(_remote, _device, _opts) {
|
|
1026
|
-
|
|
1049
|
+
var _a;
|
|
1050
|
+
const logger = (_a = _opts.logger) !== null && _a !== void 0 ? _a : logger_1.noopLogger;
|
|
1051
|
+
logger.error('Getting a file from Rekordbox via ProDJ-Link is not yet supported.');
|
|
1027
1052
|
return null;
|
|
1028
1053
|
// const conn = await remote.get(deviceId);
|
|
1029
1054
|
// if (conn === null) {
|
|
@@ -1045,7 +1070,9 @@ function viaRemote(_remote, _device, _opts) {
|
|
|
1045
1070
|
// });
|
|
1046
1071
|
}
|
|
1047
1072
|
async function viaLocal(local, device, opts) {
|
|
1073
|
+
var _a;
|
|
1048
1074
|
const { deviceId, trackSlot, track } = opts;
|
|
1075
|
+
const logger = (_a = opts.logger) !== null && _a !== void 0 ? _a : logger_1.noopLogger;
|
|
1049
1076
|
if (trackSlot !== types_1.MediaSlot.USB && trackSlot !== types_1.MediaSlot.SD) {
|
|
1050
1077
|
throw new Error('Expected USB or SD or RB slot for remote database query');
|
|
1051
1078
|
}
|
|
@@ -1059,7 +1086,7 @@ async function viaLocal(local, device, opts) {
|
|
|
1059
1086
|
slot: trackSlot,
|
|
1060
1087
|
path: track.filePath,
|
|
1061
1088
|
onProgress: progress => {
|
|
1062
|
-
|
|
1089
|
+
logger.trace('%d %d', progress.read, progress.total);
|
|
1063
1090
|
},
|
|
1064
1091
|
chunkSize: CHUNK_SIZE,
|
|
1065
1092
|
});
|
|
@@ -1099,24 +1126,41 @@ async function viaRemote(remote, opts) {
|
|
|
1099
1126
|
trackType,
|
|
1100
1127
|
menuTarget: remotedb_1.MenuTarget.Main,
|
|
1101
1128
|
};
|
|
1129
|
+
const isUnanalyzed = trackType === types_1.TrackType.Unanalyzed || trackType === types_1.TrackType.AudioCD;
|
|
1130
|
+
const isStreaming = trackType === types_1.TrackType.Streaming;
|
|
1131
|
+
const skipLocalFileLookups = isUnanalyzed || isStreaming;
|
|
1132
|
+
// Unanalyzed tracks use GetGenericMetadata (reads ID3 tags from the audio file).
|
|
1133
|
+
// Streaming tracks (Beatport) use the regular GetMetadata query.
|
|
1102
1134
|
const track = await conn.query({
|
|
1103
1135
|
queryDescriptor,
|
|
1104
|
-
query: remotedb_1.Query.GetMetadata,
|
|
1105
|
-
args: { trackId },
|
|
1106
|
-
span,
|
|
1107
|
-
});
|
|
1108
|
-
track.filePath = await conn.query({
|
|
1109
|
-
queryDescriptor,
|
|
1110
|
-
query: remotedb_1.Query.GetTrackInfo,
|
|
1111
|
-
args: { trackId },
|
|
1112
|
-
span,
|
|
1113
|
-
});
|
|
1114
|
-
track.beatGrid = await conn.query({
|
|
1115
|
-
queryDescriptor,
|
|
1116
|
-
query: remotedb_1.Query.GetBeatGrid,
|
|
1136
|
+
query: isUnanalyzed ? remotedb_1.Query.GetGenericMetadata : remotedb_1.Query.GetMetadata,
|
|
1117
1137
|
args: { trackId },
|
|
1118
1138
|
span,
|
|
1119
1139
|
});
|
|
1140
|
+
// Try to get file path — for streaming tracks this returns the Beatport track ID
|
|
1141
|
+
// (e.g. "/26883657.m4a") which we use for Beatport API lookups
|
|
1142
|
+
try {
|
|
1143
|
+
track.filePath = await conn.query({
|
|
1144
|
+
queryDescriptor,
|
|
1145
|
+
query: remotedb_1.Query.GetTrackInfo,
|
|
1146
|
+
args: { trackId },
|
|
1147
|
+
span,
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1150
|
+
catch (err) {
|
|
1151
|
+
if (!skipLocalFileLookups) {
|
|
1152
|
+
throw err;
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
// Beat grid is only available for analyzed local tracks
|
|
1156
|
+
if (!skipLocalFileLookups) {
|
|
1157
|
+
track.beatGrid = await conn.query({
|
|
1158
|
+
queryDescriptor,
|
|
1159
|
+
query: remotedb_1.Query.GetBeatGrid,
|
|
1160
|
+
args: { trackId },
|
|
1161
|
+
span,
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1120
1164
|
return track;
|
|
1121
1165
|
}
|
|
1122
1166
|
async function viaLocal(local, device, opts) {
|
|
@@ -1128,13 +1172,16 @@ async function viaLocal(local, device, opts) {
|
|
|
1128
1172
|
if (adapter === null) {
|
|
1129
1173
|
return null;
|
|
1130
1174
|
}
|
|
1131
|
-
const
|
|
1132
|
-
if (
|
|
1175
|
+
const dbTrack = adapter.findTrack(trackId);
|
|
1176
|
+
if (dbTrack === null) {
|
|
1133
1177
|
return null;
|
|
1134
1178
|
}
|
|
1135
|
-
const anlz = await (0, rekordbox_1.loadAnlz)(
|
|
1136
|
-
track
|
|
1137
|
-
|
|
1179
|
+
const anlz = await (0, rekordbox_1.loadAnlz)(dbTrack, 'DAT', (0, utils_1.anlzLoader)({ device, slot: trackSlot }));
|
|
1180
|
+
const track = {
|
|
1181
|
+
...dbTrack,
|
|
1182
|
+
beatGrid: anlz.beatGrid,
|
|
1183
|
+
waveformHd: null,
|
|
1184
|
+
};
|
|
1138
1185
|
return track;
|
|
1139
1186
|
}
|
|
1140
1187
|
|
|
@@ -1215,6 +1262,48 @@ async function viaLocal(local, opts) {
|
|
|
1215
1262
|
}
|
|
1216
1263
|
|
|
1217
1264
|
|
|
1265
|
+
/***/ },
|
|
1266
|
+
|
|
1267
|
+
/***/ "./src/db/getTrackAnalysis.ts"
|
|
1268
|
+
/*!************************************!*\
|
|
1269
|
+
!*** ./src/db/getTrackAnalysis.ts ***!
|
|
1270
|
+
\************************************/
|
|
1271
|
+
(__unused_webpack_module, exports, __webpack_require__) {
|
|
1272
|
+
|
|
1273
|
+
"use strict";
|
|
1274
|
+
|
|
1275
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1276
|
+
exports.viaLocal = viaLocal;
|
|
1277
|
+
const rekordbox_1 = __webpack_require__(/*! src/localdb/rekordbox */ "./src/localdb/rekordbox.ts");
|
|
1278
|
+
const types_1 = __webpack_require__(/*! src/types */ "./src/types.ts");
|
|
1279
|
+
const utils_1 = __webpack_require__(/*! ./utils */ "./src/db/utils.ts");
|
|
1280
|
+
async function viaLocal(local, device, opts) {
|
|
1281
|
+
var _a, _b, _c, _d;
|
|
1282
|
+
const { deviceId, trackSlot, track } = opts;
|
|
1283
|
+
if (trackSlot !== types_1.MediaSlot.USB && trackSlot !== types_1.MediaSlot.SD) {
|
|
1284
|
+
throw new Error('Expected USB or SD slot for local database query');
|
|
1285
|
+
}
|
|
1286
|
+
const conn = await local.get(deviceId, trackSlot);
|
|
1287
|
+
if (conn === null) {
|
|
1288
|
+
return null;
|
|
1289
|
+
}
|
|
1290
|
+
const resolver = (0, utils_1.anlzLoader)({ device, slot: trackSlot });
|
|
1291
|
+
const [extResult, twoxResult] = await Promise.all([
|
|
1292
|
+
(0, rekordbox_1.loadAnlz)(track, 'EXT', resolver),
|
|
1293
|
+
(0, rekordbox_1.loadAnlz)(track, '2EX', resolver).catch(() => null),
|
|
1294
|
+
]);
|
|
1295
|
+
return {
|
|
1296
|
+
extendedCues: extResult.extendedCues,
|
|
1297
|
+
songStructure: extResult.songStructure,
|
|
1298
|
+
waveformColorPreview: (_a = extResult.waveformColorPreview) !== null && _a !== void 0 ? _a : undefined,
|
|
1299
|
+
waveformHd: extResult.waveformHd,
|
|
1300
|
+
waveform3BandPreview: (_b = twoxResult === null || twoxResult === void 0 ? void 0 : twoxResult.waveform3BandPreview) !== null && _b !== void 0 ? _b : null,
|
|
1301
|
+
waveform3BandDetail: (_c = twoxResult === null || twoxResult === void 0 ? void 0 : twoxResult.waveform3BandDetail) !== null && _c !== void 0 ? _c : null,
|
|
1302
|
+
vocalConfig: (_d = twoxResult === null || twoxResult === void 0 ? void 0 : twoxResult.vocalConfig) !== null && _d !== void 0 ? _d : null,
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
|
|
1218
1307
|
/***/ },
|
|
1219
1308
|
|
|
1220
1309
|
/***/ "./src/db/getWaveforms.ts"
|
|
@@ -1249,9 +1338,30 @@ async function viaRemote(remote, opts) {
|
|
|
1249
1338
|
args: { trackId: track.id },
|
|
1250
1339
|
span,
|
|
1251
1340
|
});
|
|
1252
|
-
|
|
1341
|
+
const isStreaming = trackType === types_1.TrackType.Streaming;
|
|
1342
|
+
if (!isStreaming) {
|
|
1343
|
+
return { waveformHd };
|
|
1344
|
+
}
|
|
1345
|
+
// Streaming tracks (e.g. Beatport LINK) have no local ANLZ file but the CDJ
|
|
1346
|
+
// serves waveform preview and detailed via remotedb.
|
|
1347
|
+
const [waveformPreview, waveformDetailed] = await Promise.all([
|
|
1348
|
+
conn.query({
|
|
1349
|
+
queryDescriptor,
|
|
1350
|
+
query: remotedb_1.Query.GetWaveformPreview,
|
|
1351
|
+
args: { trackId: track.id },
|
|
1352
|
+
span,
|
|
1353
|
+
}),
|
|
1354
|
+
conn.query({
|
|
1355
|
+
queryDescriptor,
|
|
1356
|
+
query: remotedb_1.Query.GetWaveformDetailed,
|
|
1357
|
+
args: { trackId: track.id },
|
|
1358
|
+
span,
|
|
1359
|
+
}),
|
|
1360
|
+
]);
|
|
1361
|
+
return { waveformHd, waveformPreview, waveformDetailed };
|
|
1253
1362
|
}
|
|
1254
1363
|
async function viaLocal(local, device, opts) {
|
|
1364
|
+
var _a;
|
|
1255
1365
|
const { deviceId, trackSlot, track } = opts;
|
|
1256
1366
|
if (trackSlot !== types_1.MediaSlot.USB && trackSlot !== types_1.MediaSlot.SD) {
|
|
1257
1367
|
throw new Error('Expected USB or SD slot for remote database query');
|
|
@@ -1261,7 +1371,10 @@ async function viaLocal(local, device, opts) {
|
|
|
1261
1371
|
return null;
|
|
1262
1372
|
}
|
|
1263
1373
|
const anlz = await (0, rekordbox_1.loadAnlz)(track, 'EXT', (0, utils_1.anlzLoader)({ device, slot: trackSlot }));
|
|
1264
|
-
return {
|
|
1374
|
+
return {
|
|
1375
|
+
waveformHd: anlz.waveformHd,
|
|
1376
|
+
waveformColorPreview: (_a = anlz.waveformColorPreview) !== null && _a !== void 0 ? _a : undefined,
|
|
1377
|
+
};
|
|
1265
1378
|
}
|
|
1266
1379
|
|
|
1267
1380
|
|
|
@@ -1319,7 +1432,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
1319
1432
|
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");
|
|
1320
1433
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
1321
1434
|
};
|
|
1322
|
-
var
|
|
1435
|
+
var _Database_deviceManager, _Database_localDatabase, _Database_remoteDatabase, _Database_getTrackLookupStrategy, _Database_getMediaLookupStrategy;
|
|
1323
1436
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1324
1437
|
const types_1 = __webpack_require__(/*! src/types */ "./src/types.ts");
|
|
1325
1438
|
const utils_1 = __webpack_require__(/*! src/utils */ "./src/utils/index.ts");
|
|
@@ -1330,6 +1443,7 @@ const GetArtworkThumbnail = __importStar(__webpack_require__(/*! ./getArtworkThu
|
|
|
1330
1443
|
const GetFile = __importStar(__webpack_require__(/*! ./getFile */ "./src/db/getFile.ts"));
|
|
1331
1444
|
const GetMetadata = __importStar(__webpack_require__(/*! ./getMetadata */ "./src/db/getMetadata.ts"));
|
|
1332
1445
|
const GetPlaylist = __importStar(__webpack_require__(/*! ./getPlaylist */ "./src/db/getPlaylist.ts"));
|
|
1446
|
+
const GetTrackAnalysis = __importStar(__webpack_require__(/*! ./getTrackAnalysis */ "./src/db/getTrackAnalysis.ts"));
|
|
1333
1447
|
const GetWaveforms = __importStar(__webpack_require__(/*! ./getWaveforms */ "./src/db/getWaveforms.ts"));
|
|
1334
1448
|
var LookupStrategy;
|
|
1335
1449
|
(function (LookupStrategy) {
|
|
@@ -1342,8 +1456,7 @@ var LookupStrategy;
|
|
|
1342
1456
|
* network for information from their databases.
|
|
1343
1457
|
*/
|
|
1344
1458
|
class Database {
|
|
1345
|
-
constructor(
|
|
1346
|
-
_Database_hostDevice.set(this, void 0);
|
|
1459
|
+
constructor(local, remote, deviceManager) {
|
|
1347
1460
|
_Database_deviceManager.set(this, void 0);
|
|
1348
1461
|
/**
|
|
1349
1462
|
* The local database service, used when querying media devices connected
|
|
@@ -1357,8 +1470,13 @@ class Database {
|
|
|
1357
1470
|
_Database_remoteDatabase.set(this, void 0);
|
|
1358
1471
|
_Database_getTrackLookupStrategy.set(this, (device, type) => {
|
|
1359
1472
|
const isUnanalyzed = type === types_1.TrackType.AudioCD || type === types_1.TrackType.Unanalyzed;
|
|
1360
|
-
const
|
|
1361
|
-
|
|
1473
|
+
const isStreaming = type === types_1.TrackType.Streaming;
|
|
1474
|
+
// Unanalyzed and streaming tracks on CDJs must use RemoteDB
|
|
1475
|
+
// (streaming services like Beatport have no local database)
|
|
1476
|
+
if (device.type === types_1.DeviceType.CDJ && (isUnanalyzed || isStreaming)) {
|
|
1477
|
+
return LookupStrategy.Remote;
|
|
1478
|
+
}
|
|
1479
|
+
return device.type === types_1.DeviceType.Rekordbox
|
|
1362
1480
|
? LookupStrategy.Remote
|
|
1363
1481
|
: device.type === types_1.DeviceType.CDJ && type === types_1.TrackType.RB
|
|
1364
1482
|
? LookupStrategy.Local
|
|
@@ -1369,19 +1487,10 @@ class Database {
|
|
|
1369
1487
|
: device.type === types_1.DeviceType.Rekordbox
|
|
1370
1488
|
? LookupStrategy.NoneAvailable
|
|
1371
1489
|
: LookupStrategy.Local);
|
|
1372
|
-
__classPrivateFieldSet(this, _Database_hostDevice, hostDevice, "f");
|
|
1373
1490
|
__classPrivateFieldSet(this, _Database_localDatabase, local, "f");
|
|
1374
1491
|
__classPrivateFieldSet(this, _Database_remoteDatabase, remote, "f");
|
|
1375
1492
|
__classPrivateFieldSet(this, _Database_deviceManager, deviceManager, "f");
|
|
1376
1493
|
}
|
|
1377
|
-
/**
|
|
1378
|
-
* Reports weather or not the CDJs can be communicated to over the remote
|
|
1379
|
-
* database protocol. This is important when trying to query for unanalyzed or
|
|
1380
|
-
* compact disc tracks.
|
|
1381
|
-
*/
|
|
1382
|
-
get cdjSupportsRemotedb() {
|
|
1383
|
-
return __classPrivateFieldGet(this, _Database_hostDevice, "f").id > 0 && __classPrivateFieldGet(this, _Database_hostDevice, "f").id < 7;
|
|
1384
|
-
}
|
|
1385
1494
|
/**
|
|
1386
1495
|
* Get the database type (oneLibrary or pdb) for a loaded device slot.
|
|
1387
1496
|
* Returns null if the slot uses remote database or no database is loaded.
|
|
@@ -1542,6 +1651,35 @@ class Database {
|
|
|
1542
1651
|
tx.finish();
|
|
1543
1652
|
return waveforms;
|
|
1544
1653
|
}
|
|
1654
|
+
/**
|
|
1655
|
+
* Retrieves all analysis data from the EXT file for a track.
|
|
1656
|
+
* Returns extended cues, song structure, waveform color preview, and HD waveform.
|
|
1657
|
+
*/
|
|
1658
|
+
async getTrackAnalysis(opts) {
|
|
1659
|
+
const { deviceId, trackType, trackSlot, span } = opts;
|
|
1660
|
+
const tx = span
|
|
1661
|
+
? span.startChild({ op: 'dbGetTrackAnalysis' })
|
|
1662
|
+
: Telemetry.startTransaction({ name: 'dbGetTrackAnalysis' });
|
|
1663
|
+
tx.setTag('deviceId', deviceId.toString());
|
|
1664
|
+
tx.setTag('trackType', (0, utils_1.getTrackTypeName)(trackType));
|
|
1665
|
+
tx.setTag('trackSlot', (0, utils_1.getSlotName)(trackSlot));
|
|
1666
|
+
const callOpts = { ...opts, span: tx };
|
|
1667
|
+
const device = await __classPrivateFieldGet(this, _Database_deviceManager, "f").getDeviceEnsured(deviceId);
|
|
1668
|
+
if (device === null) {
|
|
1669
|
+
tx.finish();
|
|
1670
|
+
return null;
|
|
1671
|
+
}
|
|
1672
|
+
const strategy = __classPrivateFieldGet(this, _Database_getTrackLookupStrategy, "f").call(this, device, trackType);
|
|
1673
|
+
let analysis = null;
|
|
1674
|
+
if (strategy === LookupStrategy.Local) {
|
|
1675
|
+
analysis = await GetTrackAnalysis.viaLocal(__classPrivateFieldGet(this, _Database_localDatabase, "f"), device, callOpts);
|
|
1676
|
+
}
|
|
1677
|
+
if (strategy === LookupStrategy.NoneAvailable || strategy === LookupStrategy.Remote) {
|
|
1678
|
+
tx.setStatus(telemetry_1.SpanStatus.Unavailable);
|
|
1679
|
+
}
|
|
1680
|
+
tx.finish();
|
|
1681
|
+
return analysis;
|
|
1682
|
+
}
|
|
1545
1683
|
/**
|
|
1546
1684
|
* Retrieve folders, playlists, and tracks within the playlist tree. The id
|
|
1547
1685
|
* may be left undefined to query the root of the playlist tree.
|
|
@@ -1576,7 +1714,7 @@ class Database {
|
|
|
1576
1714
|
return contents;
|
|
1577
1715
|
}
|
|
1578
1716
|
}
|
|
1579
|
-
|
|
1717
|
+
_Database_deviceManager = new WeakMap(), _Database_localDatabase = new WeakMap(), _Database_remoteDatabase = new WeakMap(), _Database_getTrackLookupStrategy = new WeakMap(), _Database_getMediaLookupStrategy = new WeakMap();
|
|
1580
1718
|
exports["default"] = Database;
|
|
1581
1719
|
|
|
1582
1720
|
|
|
@@ -1858,16 +1996,17 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
1858
1996
|
var _LocalDatabase_hostDevice, _LocalDatabase_deviceManager, _LocalDatabase_statusEmitter, _LocalDatabase_emitter, _LocalDatabase_slotLocks, _LocalDatabase_dbs, _LocalDatabase_preference, _LocalDatabase_handleDeviceRemoved, _LocalDatabase_fetchFileWithFallback, _LocalDatabase_tryLoadOneLibrary, _LocalDatabase_loadPdbDatabase, _LocalDatabase_hydrateDatabase;
|
|
1859
1997
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1860
1998
|
const async_mutex_1 = __webpack_require__(/*! async-mutex */ "async-mutex");
|
|
1999
|
+
const onelibrary_connect_1 = __webpack_require__(/*! onelibrary-connect */ "onelibrary-connect");
|
|
1861
2000
|
const crypto_1 = __webpack_require__(/*! crypto */ "crypto");
|
|
1862
2001
|
const events_1 = __webpack_require__(/*! events */ "events");
|
|
1863
2002
|
const fs = __importStar(__webpack_require__(/*! fs */ "fs"));
|
|
1864
2003
|
const os = __importStar(__webpack_require__(/*! os */ "os"));
|
|
1865
2004
|
const path = __importStar(__webpack_require__(/*! path */ "path"));
|
|
2005
|
+
const constants_1 = __webpack_require__(/*! src/constants */ "./src/constants.ts");
|
|
1866
2006
|
const nfs_1 = __webpack_require__(/*! src/nfs */ "./src/nfs/index.ts");
|
|
1867
2007
|
const types_1 = __webpack_require__(/*! src/types */ "./src/types.ts");
|
|
1868
2008
|
const utils_1 = __webpack_require__(/*! src/utils */ "./src/utils/index.ts");
|
|
1869
2009
|
const Telemetry = __importStar(__webpack_require__(/*! src/utils/telemetry */ "./src/utils/telemetry.ts"));
|
|
1870
|
-
const onelibrary_1 = __webpack_require__(/*! ./onelibrary */ "./src/localdb/onelibrary.ts");
|
|
1871
2010
|
const orm_1 = __webpack_require__(/*! ./orm */ "./src/localdb/orm.ts");
|
|
1872
2011
|
const rekordbox_1 = __webpack_require__(/*! ./rekordbox */ "./src/localdb/rekordbox.ts");
|
|
1873
2012
|
/**
|
|
@@ -1944,7 +2083,9 @@ class LocalDatabase {
|
|
|
1944
2083
|
* Helper to fetch a file from device, trying both dotted and non-dotted paths
|
|
1945
2084
|
*/
|
|
1946
2085
|
_LocalDatabase_fetchFileWithFallback.set(this, async (device, slot, basePath, tx) => {
|
|
1947
|
-
const attemptOrder = process.platform === 'win32'
|
|
2086
|
+
const attemptOrder = process.platform === 'win32'
|
|
2087
|
+
? [basePath, `.${basePath}`]
|
|
2088
|
+
: [`.${basePath}`, basePath];
|
|
1948
2089
|
try {
|
|
1949
2090
|
return await (0, nfs_1.fetchFile)({
|
|
1950
2091
|
device,
|
|
@@ -1955,7 +2096,7 @@ class LocalDatabase {
|
|
|
1955
2096
|
});
|
|
1956
2097
|
}
|
|
1957
2098
|
catch {
|
|
1958
|
-
return
|
|
2099
|
+
return (0, nfs_1.fetchFile)({
|
|
1959
2100
|
device,
|
|
1960
2101
|
slot,
|
|
1961
2102
|
path: attemptOrder[1],
|
|
@@ -1976,7 +2117,7 @@ class LocalDatabase {
|
|
|
1976
2117
|
const tempDir = os.tmpdir();
|
|
1977
2118
|
const tempFile = path.join(tempDir, `prolink-onelibrary-${device.id}-${slot}-${Date.now()}.db`);
|
|
1978
2119
|
fs.writeFileSync(tempFile, dbData);
|
|
1979
|
-
const adapter = new
|
|
2120
|
+
const adapter = new onelibrary_connect_1.OneLibraryAdapter(tempFile);
|
|
1980
2121
|
return { adapter, tempFile };
|
|
1981
2122
|
}
|
|
1982
2123
|
catch {
|
|
@@ -2089,8 +2230,10 @@ class LocalDatabase {
|
|
|
2089
2230
|
if (device === undefined) {
|
|
2090
2231
|
return null;
|
|
2091
2232
|
}
|
|
2092
|
-
if (device.type !== types_1.DeviceType.CDJ
|
|
2093
|
-
|
|
2233
|
+
if (device.type !== types_1.DeviceType.CDJ ||
|
|
2234
|
+
device.id < constants_1.MIN_CDJ_DEVICE_ID ||
|
|
2235
|
+
device.id > constants_1.MAX_CDJ_DEVICE_ID) {
|
|
2236
|
+
return null;
|
|
2094
2237
|
}
|
|
2095
2238
|
let media;
|
|
2096
2239
|
try {
|
|
@@ -2133,7 +2276,9 @@ class LocalDatabase {
|
|
|
2133
2276
|
*/
|
|
2134
2277
|
async preload() {
|
|
2135
2278
|
const allDevices = [...__classPrivateFieldGet(this, _LocalDatabase_deviceManager, "f").devices.values()];
|
|
2136
|
-
const cdjDevices = allDevices.filter(device => device.type === types_1.DeviceType.CDJ
|
|
2279
|
+
const cdjDevices = allDevices.filter(device => device.type === types_1.DeviceType.CDJ &&
|
|
2280
|
+
device.id >= constants_1.MIN_CDJ_DEVICE_ID &&
|
|
2281
|
+
device.id <= constants_1.MAX_CDJ_DEVICE_ID);
|
|
2137
2282
|
if (cdjDevices.length === 0) {
|
|
2138
2283
|
return;
|
|
2139
2284
|
}
|
|
@@ -2254,6 +2399,9 @@ var RekordboxAnlz = (function() {
|
|
|
2254
2399
|
WAVE_SCROLL: 1347900979,
|
|
2255
2400
|
WAVE_COLOR_PREVIEW: 1347900980,
|
|
2256
2401
|
WAVE_COLOR_SCROLL: 1347900981,
|
|
2402
|
+
WAVE_COLOR_3BAND_PREVIEW: 1347900982,
|
|
2403
|
+
WAVE_COLOR_3BAND_DETAIL: 1347900983,
|
|
2404
|
+
VOCAL_CONFIG: 1347900995,
|
|
2257
2405
|
|
|
2258
2406
|
1346588466: "CUES_2",
|
|
2259
2407
|
1346588482: "CUES",
|
|
@@ -2266,6 +2414,9 @@ var RekordboxAnlz = (function() {
|
|
|
2266
2414
|
1347900979: "WAVE_SCROLL",
|
|
2267
2415
|
1347900980: "WAVE_COLOR_PREVIEW",
|
|
2268
2416
|
1347900981: "WAVE_COLOR_SCROLL",
|
|
2417
|
+
1347900982: "WAVE_COLOR_3BAND_PREVIEW",
|
|
2418
|
+
1347900983: "WAVE_COLOR_3BAND_DETAIL",
|
|
2419
|
+
1347900995: "VOCAL_CONFIG",
|
|
2269
2420
|
});
|
|
2270
2421
|
|
|
2271
2422
|
RekordboxAnlz.TrackMood = Object.freeze({
|
|
@@ -2387,6 +2538,39 @@ var RekordboxAnlz = (function() {
|
|
|
2387
2538
|
return PathTag;
|
|
2388
2539
|
})();
|
|
2389
2540
|
|
|
2541
|
+
/**
|
|
2542
|
+
* A 3-band color waveform preview (PWV6, found in .2EX files).
|
|
2543
|
+
* Same resolution as PWV4 (typically 1200 entries) but with 3 bytes
|
|
2544
|
+
* per entry representing low, mid, and high frequency band amplitudes.
|
|
2545
|
+
* Layout is identical to wave_color_preview_tag (PWV4).
|
|
2546
|
+
* @see {@link https://djl-analysis.deepsymmetry.org/djl-analysis/track-metadata.html#color-3band-preview-waveform|Source}
|
|
2547
|
+
*/
|
|
2548
|
+
|
|
2549
|
+
var WaveColor3bandPreviewTag = RekordboxAnlz.WaveColor3bandPreviewTag = (function() {
|
|
2550
|
+
function WaveColor3bandPreviewTag(_io, _parent, _root) {
|
|
2551
|
+
this._io = _io;
|
|
2552
|
+
this._parent = _parent;
|
|
2553
|
+
this._root = _root || this;
|
|
2554
|
+
|
|
2555
|
+
this._read();
|
|
2556
|
+
}
|
|
2557
|
+
WaveColor3bandPreviewTag.prototype._read = function() {
|
|
2558
|
+
this.lenEntryBytes = this._io.readU4be();
|
|
2559
|
+
this.lenEntries = this._io.readU4be();
|
|
2560
|
+
this.entries = this._io.readBytes((this.lenEntries * this.lenEntryBytes));
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
/**
|
|
2564
|
+
* The size of each entry, in bytes. Always 3 (low, mid, high).
|
|
2565
|
+
*/
|
|
2566
|
+
|
|
2567
|
+
/**
|
|
2568
|
+
* The number of waveform data points. Typically 1200.
|
|
2569
|
+
*/
|
|
2570
|
+
|
|
2571
|
+
return WaveColor3bandPreviewTag;
|
|
2572
|
+
})();
|
|
2573
|
+
|
|
2390
2574
|
/**
|
|
2391
2575
|
* Stores a waveform preview image suitable for display above
|
|
2392
2576
|
* the touch strip for jumping to a track position.
|
|
@@ -2532,6 +2716,76 @@ var RekordboxAnlz = (function() {
|
|
|
2532
2716
|
return WaveColorPreviewTag;
|
|
2533
2717
|
})();
|
|
2534
2718
|
|
|
2719
|
+
/**
|
|
2720
|
+
* A 3-band color detail waveform (PWV7, found in .2EX files).
|
|
2721
|
+
* Higher resolution than PWV6. Each entry is 3 bytes (low, mid, high).
|
|
2722
|
+
* Layout is identical to wave_color_scroll_tag (PWV5).
|
|
2723
|
+
* @see {@link https://djl-analysis.deepsymmetry.org/djl-analysis/track-metadata.html#color-3band-detail-waveform|Source}
|
|
2724
|
+
*/
|
|
2725
|
+
|
|
2726
|
+
var WaveColor3bandDetailTag = RekordboxAnlz.WaveColor3bandDetailTag = (function() {
|
|
2727
|
+
function WaveColor3bandDetailTag(_io, _parent, _root) {
|
|
2728
|
+
this._io = _io;
|
|
2729
|
+
this._parent = _parent;
|
|
2730
|
+
this._root = _root || this;
|
|
2731
|
+
|
|
2732
|
+
this._read();
|
|
2733
|
+
}
|
|
2734
|
+
WaveColor3bandDetailTag.prototype._read = function() {
|
|
2735
|
+
this.lenEntryBytes = this._io.readU4be();
|
|
2736
|
+
this.lenEntries = this._io.readU4be();
|
|
2737
|
+
this._unnamed2 = this._io.readU4be();
|
|
2738
|
+
this.entries = this._io.readBytes((this.lenEntries * this.lenEntryBytes));
|
|
2739
|
+
}
|
|
2740
|
+
|
|
2741
|
+
/**
|
|
2742
|
+
* The size of each entry, in bytes. Always 3 (low, mid, high).
|
|
2743
|
+
*/
|
|
2744
|
+
|
|
2745
|
+
/**
|
|
2746
|
+
* The number of columns of waveform data.
|
|
2747
|
+
*/
|
|
2748
|
+
|
|
2749
|
+
return WaveColor3bandDetailTag;
|
|
2750
|
+
})();
|
|
2751
|
+
|
|
2752
|
+
/**
|
|
2753
|
+
* Vocal detection configuration (PWVC, found in .2EX files).
|
|
2754
|
+
* Contains threshold values used to classify frequency content
|
|
2755
|
+
* as vocal or non-vocal. Body is 8 bytes: unknown(2) + 3 thresholds(2 each).
|
|
2756
|
+
* Observed threshold ranges: low 80-114, mid 80-146, high 98-159.
|
|
2757
|
+
*/
|
|
2758
|
+
|
|
2759
|
+
var VocalConfigTag = RekordboxAnlz.VocalConfigTag = (function() {
|
|
2760
|
+
function VocalConfigTag(_io, _parent, _root) {
|
|
2761
|
+
this._io = _io;
|
|
2762
|
+
this._parent = _parent;
|
|
2763
|
+
this._root = _root || this;
|
|
2764
|
+
|
|
2765
|
+
this._read();
|
|
2766
|
+
}
|
|
2767
|
+
VocalConfigTag.prototype._read = function() {
|
|
2768
|
+
this._unnamed0 = this._io.readU2be();
|
|
2769
|
+
this.thresholdLow = this._io.readU2be();
|
|
2770
|
+
this.thresholdMid = this._io.readU2be();
|
|
2771
|
+
this.thresholdHigh = this._io.readU2be();
|
|
2772
|
+
}
|
|
2773
|
+
|
|
2774
|
+
/**
|
|
2775
|
+
* Low frequency vocal detection threshold. Observed range: 80-114.
|
|
2776
|
+
*/
|
|
2777
|
+
|
|
2778
|
+
/**
|
|
2779
|
+
* Mid frequency vocal detection threshold. Observed range: 80-146.
|
|
2780
|
+
*/
|
|
2781
|
+
|
|
2782
|
+
/**
|
|
2783
|
+
* High frequency vocal detection threshold. Observed range: 98-159.
|
|
2784
|
+
*/
|
|
2785
|
+
|
|
2786
|
+
return VocalConfigTag;
|
|
2787
|
+
})();
|
|
2788
|
+
|
|
2535
2789
|
var PhraseHigh = RekordboxAnlz.PhraseHigh = (function() {
|
|
2536
2790
|
function PhraseHigh(_io, _parent, _root) {
|
|
2537
2791
|
this._io = _io;
|
|
@@ -3039,6 +3293,11 @@ var RekordboxAnlz = (function() {
|
|
|
3039
3293
|
var _io__raw_body = new KaitaiStream(this._raw_body);
|
|
3040
3294
|
this.body = new BeatGridTag(_io__raw_body, this, this._root);
|
|
3041
3295
|
break;
|
|
3296
|
+
case RekordboxAnlz.SectionTags.VOCAL_CONFIG:
|
|
3297
|
+
this._raw_body = this._io.readBytes((this.lenTag - 12));
|
|
3298
|
+
var _io__raw_body = new KaitaiStream(this._raw_body);
|
|
3299
|
+
this.body = new VocalConfigTag(_io__raw_body, this, this._root);
|
|
3300
|
+
break;
|
|
3042
3301
|
case RekordboxAnlz.SectionTags.WAVE_PREVIEW:
|
|
3043
3302
|
this._raw_body = this._io.readBytes((this.lenTag - 12));
|
|
3044
3303
|
var _io__raw_body = new KaitaiStream(this._raw_body);
|
|
@@ -3059,6 +3318,16 @@ var RekordboxAnlz = (function() {
|
|
|
3059
3318
|
var _io__raw_body = new KaitaiStream(this._raw_body);
|
|
3060
3319
|
this.body = new WavePreviewTag(_io__raw_body, this, this._root);
|
|
3061
3320
|
break;
|
|
3321
|
+
case RekordboxAnlz.SectionTags.WAVE_COLOR_3BAND_DETAIL:
|
|
3322
|
+
this._raw_body = this._io.readBytes((this.lenTag - 12));
|
|
3323
|
+
var _io__raw_body = new KaitaiStream(this._raw_body);
|
|
3324
|
+
this.body = new WaveColor3bandDetailTag(_io__raw_body, this, this._root);
|
|
3325
|
+
break;
|
|
3326
|
+
case RekordboxAnlz.SectionTags.WAVE_COLOR_3BAND_PREVIEW:
|
|
3327
|
+
this._raw_body = this._io.readBytes((this.lenTag - 12));
|
|
3328
|
+
var _io__raw_body = new KaitaiStream(this._raw_body);
|
|
3329
|
+
this.body = new WaveColor3bandPreviewTag(_io__raw_body, this, this._root);
|
|
3330
|
+
break;
|
|
3062
3331
|
default:
|
|
3063
3332
|
this._raw_body = this._io.readBytes((this.lenTag - 12));
|
|
3064
3333
|
var _io__raw_body = new KaitaiStream(this._raw_body);
|
|
@@ -4874,141 +5143,14 @@ return RekordboxPdb;
|
|
|
4874
5143
|
|
|
4875
5144
|
/***/ },
|
|
4876
5145
|
|
|
4877
|
-
/***/ "./src/localdb/
|
|
4878
|
-
|
|
4879
|
-
!*** ./src/localdb/
|
|
4880
|
-
|
|
4881
|
-
(__unused_webpack_module, exports) {
|
|
4882
|
-
|
|
4883
|
-
"use strict";
|
|
4884
|
-
|
|
4885
|
-
/**
|
|
4886
|
-
* TypeScript interfaces for the OneLibrary (exportLibrary.db) SQLite schema.
|
|
4887
|
-
*
|
|
4888
|
-
* These match the actual column names in the SQLite database,
|
|
4889
|
-
* which differ from the legacy PDB format.
|
|
4890
|
-
*
|
|
4891
|
-
* Note: Column names use camelCase in the database (e.g., bpmx100, titleForSearch)
|
|
4892
|
-
*/
|
|
4893
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
4894
|
-
exports.MenuItemKind = exports.MyTagAttribute = exports.PlaylistAttribute = exports.CueKind = void 0;
|
|
4895
|
-
// ============================================================================
|
|
4896
|
-
// Cue Kind Constants
|
|
4897
|
-
// ============================================================================
|
|
4898
|
-
/**
|
|
4899
|
-
* Cue point types based on the 'kind' field in the cue table.
|
|
4900
|
-
*
|
|
4901
|
-
* Note: These values may vary. Verify with actual data.
|
|
4902
|
-
*/
|
|
4903
|
-
exports.CueKind = {
|
|
4904
|
-
MEMORY_CUE: 0,
|
|
4905
|
-
HOT_CUE: 1,
|
|
4906
|
-
// Additional types may exist
|
|
4907
|
-
};
|
|
4908
|
-
/**
|
|
4909
|
-
* Playlist attribute types based on the 'attribute' field.
|
|
4910
|
-
*/
|
|
4911
|
-
exports.PlaylistAttribute = {
|
|
4912
|
-
PLAYLIST: 0,
|
|
4913
|
-
FOLDER: 1,
|
|
4914
|
-
};
|
|
4915
|
-
/**
|
|
4916
|
-
* MyTag attribute types based on the 'attribute' field.
|
|
4917
|
-
*/
|
|
4918
|
-
exports.MyTagAttribute = {
|
|
4919
|
-
TAG: 0,
|
|
4920
|
-
FOLDER: 1,
|
|
4921
|
-
};
|
|
4922
|
-
// ============================================================================
|
|
4923
|
-
// Menu Item Kind Constants
|
|
4924
|
-
// ============================================================================
|
|
4925
|
-
/**
|
|
4926
|
-
* Menu item kinds for browsing categories.
|
|
4927
|
-
* These match the 'kind' field in the menuItem table.
|
|
4928
|
-
*/
|
|
4929
|
-
exports.MenuItemKind = {
|
|
4930
|
-
GENRE: 128,
|
|
4931
|
-
ARTIST: 129,
|
|
4932
|
-
ALBUM: 130,
|
|
4933
|
-
TRACK: 131,
|
|
4934
|
-
PLAYLIST: 132,
|
|
4935
|
-
BPM: 133,
|
|
4936
|
-
RATING: 134,
|
|
4937
|
-
YEAR: 135,
|
|
4938
|
-
REMIXER: 136,
|
|
4939
|
-
LABEL: 137,
|
|
4940
|
-
ORIGINAL_ARTIST: 138,
|
|
4941
|
-
KEY: 139,
|
|
4942
|
-
DATE_ADDED: 140,
|
|
4943
|
-
CUE: 141,
|
|
4944
|
-
COLOR: 142,
|
|
4945
|
-
FOLDER: 144,
|
|
4946
|
-
SEARCH: 145,
|
|
4947
|
-
TIME: 146,
|
|
4948
|
-
BITRATE: 147,
|
|
4949
|
-
FILE_NAME: 148,
|
|
4950
|
-
HISTORY: 149,
|
|
4951
|
-
COMMENTS: 150,
|
|
4952
|
-
DJ_PLAY_COUNT: 151,
|
|
4953
|
-
HOT_CUE_BANK: 152,
|
|
4954
|
-
DEFAULT: 161,
|
|
4955
|
-
ALPHABET: 162,
|
|
4956
|
-
MATCHING: 170,
|
|
4957
|
-
};
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
/***/ },
|
|
4961
|
-
|
|
4962
|
-
/***/ "./src/localdb/onelibrary.ts"
|
|
4963
|
-
/*!***********************************!*\
|
|
4964
|
-
!*** ./src/localdb/onelibrary.ts ***!
|
|
4965
|
-
\***********************************/
|
|
5146
|
+
/***/ "./src/localdb/orm.ts"
|
|
5147
|
+
/*!****************************!*\
|
|
5148
|
+
!*** ./src/localdb/orm.ts ***!
|
|
5149
|
+
\****************************/
|
|
4966
5150
|
(__unused_webpack_module, exports, __webpack_require__) {
|
|
4967
5151
|
|
|
4968
5152
|
"use strict";
|
|
4969
5153
|
|
|
4970
|
-
/**
|
|
4971
|
-
* OneLibrary Database Adapter
|
|
4972
|
-
*
|
|
4973
|
-
* Provides an interface for reading the OneLibrary (exportLibrary.db) SQLite database
|
|
4974
|
-
* used by modern rekordbox versions and Pioneer DJ devices.
|
|
4975
|
-
*
|
|
4976
|
-
* The database is encrypted with SQLCipher 4. The encryption key is derived from
|
|
4977
|
-
* a hardcoded obfuscated blob.
|
|
4978
|
-
*/
|
|
4979
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4980
|
-
if (k2 === undefined) k2 = k;
|
|
4981
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
4982
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
4983
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
4984
|
-
}
|
|
4985
|
-
Object.defineProperty(o, k2, desc);
|
|
4986
|
-
}) : (function(o, m, k, k2) {
|
|
4987
|
-
if (k2 === undefined) k2 = k;
|
|
4988
|
-
o[k2] = m[k];
|
|
4989
|
-
}));
|
|
4990
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
4991
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
4992
|
-
}) : function(o, v) {
|
|
4993
|
-
o["default"] = v;
|
|
4994
|
-
});
|
|
4995
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
4996
|
-
var ownKeys = function(o) {
|
|
4997
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
4998
|
-
var ar = [];
|
|
4999
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
5000
|
-
return ar;
|
|
5001
|
-
};
|
|
5002
|
-
return ownKeys(o);
|
|
5003
|
-
};
|
|
5004
|
-
return function (mod) {
|
|
5005
|
-
if (mod && mod.__esModule) return mod;
|
|
5006
|
-
var result = {};
|
|
5007
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
5008
|
-
__setModuleDefault(result, mod);
|
|
5009
|
-
return result;
|
|
5010
|
-
};
|
|
5011
|
-
})();
|
|
5012
5154
|
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
5013
5155
|
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
5014
5156
|
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
@@ -5023,673 +5165,14 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
5023
5165
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5024
5166
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5025
5167
|
};
|
|
5026
|
-
var
|
|
5168
|
+
var _MetadataORM_conn, _MetadataORM_stmtCache, _MetadataORM_inTransaction;
|
|
5027
5169
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
5028
|
-
exports.
|
|
5029
|
-
exports.getEncryptionKey = getEncryptionKey;
|
|
5030
|
-
exports.openOneLibraryDb = openOneLibraryDb;
|
|
5170
|
+
exports.MetadataORM = exports.Table = void 0;
|
|
5031
5171
|
const better_sqlite3_multiple_ciphers_1 = __importDefault(__webpack_require__(/*! better-sqlite3-multiple-ciphers */ "better-sqlite3-multiple-ciphers"));
|
|
5032
|
-
const
|
|
5033
|
-
const
|
|
5034
|
-
// ============================================================================
|
|
5035
|
-
// Encryption Key Derivation
|
|
5036
|
-
// ============================================================================
|
|
5037
|
-
/**
|
|
5038
|
-
* The obfuscated encryption key blob from pyrekordbox
|
|
5039
|
-
*/
|
|
5040
|
-
const BLOB = Buffer.from('PN_1dH8$oLJY)16j_RvM6qphWw`476>;C1cWmI#se(PG`j}~xAjlufj?`#0i{;=glh(SkW)y0>n?YEiD`l%t(', 'ascii');
|
|
5041
|
-
/**
|
|
5042
|
-
* XOR key used for deobfuscation
|
|
5043
|
-
*/
|
|
5044
|
-
const BLOB_KEY = Buffer.from('657f48f84c437cc1', 'ascii');
|
|
5045
|
-
/**
|
|
5046
|
-
* Base85 (RFC 1924) decode
|
|
5047
|
-
*/
|
|
5048
|
-
function base85Decode(input) {
|
|
5049
|
-
const alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~';
|
|
5050
|
-
const charToValue = new Map();
|
|
5051
|
-
for (let i = 0; i < alphabet.length; i++) {
|
|
5052
|
-
charToValue.set(alphabet[i], i);
|
|
5053
|
-
}
|
|
5054
|
-
const inputStr = input.toString('ascii');
|
|
5055
|
-
const result = [];
|
|
5056
|
-
for (let i = 0; i < inputStr.length; i += 5) {
|
|
5057
|
-
const chunk = inputStr.slice(i, i + 5);
|
|
5058
|
-
let value = 0;
|
|
5059
|
-
for (const char of chunk) {
|
|
5060
|
-
const v = charToValue.get(char);
|
|
5061
|
-
if (v === undefined) {
|
|
5062
|
-
throw new Error(`Invalid base85 character: ${char}`);
|
|
5063
|
-
}
|
|
5064
|
-
value = value * 85 + v;
|
|
5065
|
-
}
|
|
5066
|
-
const bytes = [
|
|
5067
|
-
(value >> 24) & 0xff,
|
|
5068
|
-
(value >> 16) & 0xff,
|
|
5069
|
-
(value >> 8) & 0xff,
|
|
5070
|
-
value & 0xff,
|
|
5071
|
-
];
|
|
5072
|
-
const numBytes = chunk.length === 5 ? 4 : chunk.length - 1;
|
|
5073
|
-
result.push(...bytes.slice(0, numBytes));
|
|
5074
|
-
}
|
|
5075
|
-
return Buffer.from(result);
|
|
5076
|
-
}
|
|
5077
|
-
/**
|
|
5078
|
-
* Deobfuscate the blob to get the encryption key
|
|
5079
|
-
*/
|
|
5080
|
-
function deobfuscate(blob) {
|
|
5081
|
-
const decoded = base85Decode(blob);
|
|
5082
|
-
const xored = Buffer.alloc(decoded.length);
|
|
5083
|
-
for (let i = 0; i < decoded.length; i++) {
|
|
5084
|
-
xored[i] = decoded[i] ^ BLOB_KEY[i % BLOB_KEY.length];
|
|
5085
|
-
}
|
|
5086
|
-
const decompressed = zlib.inflateSync(xored);
|
|
5087
|
-
return decompressed.toString('utf-8');
|
|
5088
|
-
}
|
|
5089
|
-
/**
|
|
5090
|
-
* Get the SQLCipher encryption key for OneLibrary databases
|
|
5091
|
-
*/
|
|
5092
|
-
function getEncryptionKey() {
|
|
5093
|
-
const key = deobfuscate(BLOB);
|
|
5094
|
-
if (!key.startsWith('r8gd')) {
|
|
5095
|
-
throw new Error('Invalid encryption key derived');
|
|
5096
|
-
}
|
|
5097
|
-
return key;
|
|
5098
|
-
}
|
|
5099
|
-
// ============================================================================
|
|
5100
|
-
// Database Connection
|
|
5101
|
-
// ============================================================================
|
|
5172
|
+
const lodash_1 = __webpack_require__(/*! lodash */ "lodash");
|
|
5173
|
+
const schema_1 = __webpack_require__(/*! ./schema */ "./src/localdb/schema.ts");
|
|
5102
5174
|
/**
|
|
5103
|
-
*
|
|
5104
|
-
*/
|
|
5105
|
-
function openOneLibraryDb(dbPath) {
|
|
5106
|
-
const key = getEncryptionKey();
|
|
5107
|
-
const db = new better_sqlite3_multiple_ciphers_1.default(dbPath, { readonly: true });
|
|
5108
|
-
db.pragma('cipher = sqlcipher');
|
|
5109
|
-
db.pragma('legacy = 4');
|
|
5110
|
-
db.pragma(`key = '${key}'`);
|
|
5111
|
-
return db;
|
|
5112
|
-
}
|
|
5113
|
-
// ============================================================================
|
|
5114
|
-
// OneLibrary Adapter
|
|
5115
|
-
// ============================================================================
|
|
5116
|
-
/**
|
|
5117
|
-
* Adapter for OneLibrary database that matches the ORM interface.
|
|
5118
|
-
* Queries the SQLite file directly instead of hydrating into memory.
|
|
5119
|
-
*/
|
|
5120
|
-
class OneLibraryAdapter {
|
|
5121
|
-
constructor(dbPath) {
|
|
5122
|
-
_OneLibraryAdapter_instances.add(this);
|
|
5123
|
-
this.type = 'oneLibrary';
|
|
5124
|
-
_OneLibraryAdapter_db.set(this, void 0);
|
|
5125
|
-
_OneLibraryAdapter_stmtCache.set(this, new Map());
|
|
5126
|
-
__classPrivateFieldSet(this, _OneLibraryAdapter_db, openOneLibraryDb(dbPath), "f");
|
|
5127
|
-
}
|
|
5128
|
-
/**
|
|
5129
|
-
* Close the database connection
|
|
5130
|
-
*/
|
|
5131
|
-
close() {
|
|
5132
|
-
__classPrivateFieldGet(this, _OneLibraryAdapter_stmtCache, "f").clear();
|
|
5133
|
-
__classPrivateFieldGet(this, _OneLibraryAdapter_db, "f").close();
|
|
5134
|
-
}
|
|
5135
|
-
// ==========================================================================
|
|
5136
|
-
// Track Queries
|
|
5137
|
-
// ==========================================================================
|
|
5138
|
-
/**
|
|
5139
|
-
* Find a track by ID
|
|
5140
|
-
*/
|
|
5141
|
-
findTrack(id) {
|
|
5142
|
-
const row = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5143
|
-
SELECT c.*,
|
|
5144
|
-
a.name as artistName,
|
|
5145
|
-
al.name as albumName,
|
|
5146
|
-
g.name as genreName,
|
|
5147
|
-
k.name as keyName,
|
|
5148
|
-
col.name as colorName,
|
|
5149
|
-
lbl.name as labelName,
|
|
5150
|
-
img.path as artworkPath,
|
|
5151
|
-
remix.name as remixerName,
|
|
5152
|
-
orig.name as originalArtistName,
|
|
5153
|
-
comp.name as composerName
|
|
5154
|
-
FROM content c
|
|
5155
|
-
LEFT JOIN artist a ON c.artist_id_artist = a.artist_id
|
|
5156
|
-
LEFT JOIN album al ON c.album_id = al.album_id
|
|
5157
|
-
LEFT JOIN genre g ON c.genre_id = g.genre_id
|
|
5158
|
-
LEFT JOIN key k ON c.key_id = k.key_id
|
|
5159
|
-
LEFT JOIN color col ON c.color_id = col.color_id
|
|
5160
|
-
LEFT JOIN label lbl ON c.label_id = lbl.label_id
|
|
5161
|
-
LEFT JOIN image img ON c.image_id = img.image_id
|
|
5162
|
-
LEFT JOIN artist remix ON c.artist_id_remixer = remix.artist_id
|
|
5163
|
-
LEFT JOIN artist orig ON c.artist_id_originalArtist = orig.artist_id
|
|
5164
|
-
LEFT JOIN artist comp ON c.artist_id_composer = comp.artist_id
|
|
5165
|
-
WHERE c.content_id = ?
|
|
5166
|
-
`).get(id);
|
|
5167
|
-
if (!row) {
|
|
5168
|
-
return null;
|
|
5169
|
-
}
|
|
5170
|
-
return __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_contentToTrack).call(this, row);
|
|
5171
|
-
}
|
|
5172
|
-
/**
|
|
5173
|
-
* Find all tracks in the database
|
|
5174
|
-
*/
|
|
5175
|
-
findAllTracks() {
|
|
5176
|
-
const rows = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5177
|
-
SELECT c.*,
|
|
5178
|
-
a.name as artistName,
|
|
5179
|
-
al.name as albumName,
|
|
5180
|
-
g.name as genreName,
|
|
5181
|
-
k.name as keyName,
|
|
5182
|
-
col.name as colorName,
|
|
5183
|
-
lbl.name as labelName,
|
|
5184
|
-
img.path as artworkPath,
|
|
5185
|
-
remix.name as remixerName,
|
|
5186
|
-
orig.name as originalArtistName,
|
|
5187
|
-
comp.name as composerName
|
|
5188
|
-
FROM content c
|
|
5189
|
-
LEFT JOIN artist a ON c.artist_id_artist = a.artist_id
|
|
5190
|
-
LEFT JOIN album al ON c.album_id = al.album_id
|
|
5191
|
-
LEFT JOIN genre g ON c.genre_id = g.genre_id
|
|
5192
|
-
LEFT JOIN key k ON c.key_id = k.key_id
|
|
5193
|
-
LEFT JOIN color col ON c.color_id = col.color_id
|
|
5194
|
-
LEFT JOIN label lbl ON c.label_id = lbl.label_id
|
|
5195
|
-
LEFT JOIN image img ON c.image_id = img.image_id
|
|
5196
|
-
LEFT JOIN artist remix ON c.artist_id_remixer = remix.artist_id
|
|
5197
|
-
LEFT JOIN artist orig ON c.artist_id_originalArtist = orig.artist_id
|
|
5198
|
-
LEFT JOIN artist comp ON c.artist_id_composer = comp.artist_id
|
|
5199
|
-
`).all();
|
|
5200
|
-
return rows.map(row => __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_contentToTrack).call(this, row));
|
|
5201
|
-
}
|
|
5202
|
-
// ==========================================================================
|
|
5203
|
-
// Cue Queries
|
|
5204
|
-
// ==========================================================================
|
|
5205
|
-
/**
|
|
5206
|
-
* Find cue points for a track
|
|
5207
|
-
*/
|
|
5208
|
-
findCues(trackId) {
|
|
5209
|
-
const rows = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5210
|
-
SELECT * FROM cue WHERE content_id = ?
|
|
5211
|
-
`).all(trackId);
|
|
5212
|
-
return rows.map(row => __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_cueToCueAndLoop).call(this, row));
|
|
5213
|
-
}
|
|
5214
|
-
// ==========================================================================
|
|
5215
|
-
// Playlist Queries
|
|
5216
|
-
// ==========================================================================
|
|
5217
|
-
/**
|
|
5218
|
-
* Find a playlist by ID
|
|
5219
|
-
*/
|
|
5220
|
-
findPlaylistById(playlistId) {
|
|
5221
|
-
const row = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5222
|
-
SELECT * FROM playlist WHERE playlist_id = ?
|
|
5223
|
-
`).get(playlistId);
|
|
5224
|
-
return row ? __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_playlistRowToPlaylist).call(this, row) : null;
|
|
5225
|
-
}
|
|
5226
|
-
/**
|
|
5227
|
-
* Query for a list of {folders, playlists, tracks} given a playlist ID.
|
|
5228
|
-
* If no ID is provided the root list is queried.
|
|
5229
|
-
*/
|
|
5230
|
-
findPlaylist(playlistId) {
|
|
5231
|
-
const parentCondition = playlistId === undefined ? 'playlist_id_parent IS NULL' : 'playlist_id_parent = ?';
|
|
5232
|
-
// Lookup playlists / folders for this playlist ID
|
|
5233
|
-
const playlistRows = (playlistId === undefined
|
|
5234
|
-
? __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `SELECT * FROM playlist WHERE ${parentCondition}`).all()
|
|
5235
|
-
: __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `SELECT * FROM playlist WHERE ${parentCondition}`).all(playlistId));
|
|
5236
|
-
const folders = [];
|
|
5237
|
-
const playlists = [];
|
|
5238
|
-
for (const row of playlistRows) {
|
|
5239
|
-
const playlist = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_playlistRowToPlaylist).call(this, row);
|
|
5240
|
-
if (playlist.isFolder) {
|
|
5241
|
-
folders.push(playlist);
|
|
5242
|
-
}
|
|
5243
|
-
else {
|
|
5244
|
-
playlists.push(playlist);
|
|
5245
|
-
}
|
|
5246
|
-
}
|
|
5247
|
-
// Get track entries for this playlist
|
|
5248
|
-
const entryRows = playlistId === undefined
|
|
5249
|
-
? []
|
|
5250
|
-
: __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5251
|
-
SELECT * FROM playlist_content WHERE playlist_id = ? ORDER BY sequenceNo
|
|
5252
|
-
`).all(playlistId);
|
|
5253
|
-
const trackEntries = entryRows.map((row, index) => ({
|
|
5254
|
-
id: index, // playlist_content doesn't have a unique ID, use index
|
|
5255
|
-
sortIndex: row.sequenceNo,
|
|
5256
|
-
playlistId: row.playlist_id,
|
|
5257
|
-
trackId: row.content_id,
|
|
5258
|
-
}));
|
|
5259
|
-
return { folders, playlists, trackEntries };
|
|
5260
|
-
}
|
|
5261
|
-
/**
|
|
5262
|
-
* Get track IDs for a playlist in order
|
|
5263
|
-
*/
|
|
5264
|
-
findPlaylistContents(playlistId) {
|
|
5265
|
-
const rows = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5266
|
-
SELECT content_id FROM playlist_content
|
|
5267
|
-
WHERE playlist_id = ? ORDER BY sequenceNo
|
|
5268
|
-
`).all(playlistId);
|
|
5269
|
-
return rows.map(r => r.content_id);
|
|
5270
|
-
}
|
|
5271
|
-
// ==========================================================================
|
|
5272
|
-
// Reference Table Queries
|
|
5273
|
-
// ==========================================================================
|
|
5274
|
-
findArtist(artistId) {
|
|
5275
|
-
var _a;
|
|
5276
|
-
const row = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5277
|
-
SELECT * FROM artist WHERE artist_id = ?
|
|
5278
|
-
`).get(artistId);
|
|
5279
|
-
return row ? { id: row.artist_id, name: (_a = row.name) !== null && _a !== void 0 ? _a : '' } : null;
|
|
5280
|
-
}
|
|
5281
|
-
findAlbum(albumId) {
|
|
5282
|
-
var _a;
|
|
5283
|
-
const row = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5284
|
-
SELECT * FROM album WHERE album_id = ?
|
|
5285
|
-
`).get(albumId);
|
|
5286
|
-
return row ? { id: row.album_id, name: (_a = row.name) !== null && _a !== void 0 ? _a : '' } : null;
|
|
5287
|
-
}
|
|
5288
|
-
findGenre(genreId) {
|
|
5289
|
-
var _a;
|
|
5290
|
-
const row = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5291
|
-
SELECT * FROM genre WHERE genre_id = ?
|
|
5292
|
-
`).get(genreId);
|
|
5293
|
-
return row ? { id: row.genre_id, name: (_a = row.name) !== null && _a !== void 0 ? _a : '' } : null;
|
|
5294
|
-
}
|
|
5295
|
-
findKey(keyId) {
|
|
5296
|
-
var _a;
|
|
5297
|
-
const row = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5298
|
-
SELECT * FROM key WHERE key_id = ?
|
|
5299
|
-
`).get(keyId);
|
|
5300
|
-
return row ? { id: row.key_id, name: (_a = row.name) !== null && _a !== void 0 ? _a : '' } : null;
|
|
5301
|
-
}
|
|
5302
|
-
findColor(colorId) {
|
|
5303
|
-
var _a;
|
|
5304
|
-
const row = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5305
|
-
SELECT * FROM color WHERE color_id = ?
|
|
5306
|
-
`).get(colorId);
|
|
5307
|
-
return row ? { id: row.color_id, name: (_a = row.name) !== null && _a !== void 0 ? _a : '' } : null;
|
|
5308
|
-
}
|
|
5309
|
-
findLabel(labelId) {
|
|
5310
|
-
var _a;
|
|
5311
|
-
const row = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5312
|
-
SELECT * FROM label WHERE label_id = ?
|
|
5313
|
-
`).get(labelId);
|
|
5314
|
-
return row ? { id: row.label_id, name: (_a = row.name) !== null && _a !== void 0 ? _a : '' } : null;
|
|
5315
|
-
}
|
|
5316
|
-
findArtwork(imageId) {
|
|
5317
|
-
var _a;
|
|
5318
|
-
const row = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5319
|
-
SELECT * FROM image WHERE image_id = ?
|
|
5320
|
-
`).get(imageId);
|
|
5321
|
-
return row ? { id: row.image_id, path: (_a = row.path) !== null && _a !== void 0 ? _a : undefined } : null;
|
|
5322
|
-
}
|
|
5323
|
-
// ==========================================================================
|
|
5324
|
-
// MyTag (User Tags) Queries
|
|
5325
|
-
// ==========================================================================
|
|
5326
|
-
/**
|
|
5327
|
-
* Find all root-level MyTags (folders and tags with no parent)
|
|
5328
|
-
*/
|
|
5329
|
-
findMyTags(parentId) {
|
|
5330
|
-
const parentCondition = parentId === undefined
|
|
5331
|
-
? 'myTag_id_parent IS NULL OR myTag_id_parent = 0'
|
|
5332
|
-
: 'myTag_id_parent = ?';
|
|
5333
|
-
const rows = (parentId === undefined
|
|
5334
|
-
? __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `SELECT * FROM myTag WHERE ${parentCondition} ORDER BY sequenceNo`).all()
|
|
5335
|
-
: __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `SELECT * FROM myTag WHERE ${parentCondition} ORDER BY sequenceNo`).all(parentId));
|
|
5336
|
-
const folders = [];
|
|
5337
|
-
const tags = [];
|
|
5338
|
-
for (const row of rows) {
|
|
5339
|
-
const tag = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_myTagRowToMyTag).call(this, row);
|
|
5340
|
-
if (tag.isFolder) {
|
|
5341
|
-
folders.push(tag);
|
|
5342
|
-
}
|
|
5343
|
-
else {
|
|
5344
|
-
tags.push(tag);
|
|
5345
|
-
}
|
|
5346
|
-
}
|
|
5347
|
-
return { folders, tags };
|
|
5348
|
-
}
|
|
5349
|
-
/**
|
|
5350
|
-
* Find a MyTag by ID
|
|
5351
|
-
*/
|
|
5352
|
-
findMyTagById(myTagId) {
|
|
5353
|
-
const row = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5354
|
-
SELECT * FROM myTag WHERE myTag_id = ?
|
|
5355
|
-
`).get(myTagId);
|
|
5356
|
-
return row ? __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_myTagRowToMyTag).call(this, row) : null;
|
|
5357
|
-
}
|
|
5358
|
-
/**
|
|
5359
|
-
* Get track IDs for a MyTag
|
|
5360
|
-
*/
|
|
5361
|
-
findMyTagContents(myTagId) {
|
|
5362
|
-
const rows = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5363
|
-
SELECT content_id FROM myTag_content WHERE myTag_id = ?
|
|
5364
|
-
`).all(myTagId);
|
|
5365
|
-
return rows.map(r => r.content_id);
|
|
5366
|
-
}
|
|
5367
|
-
/**
|
|
5368
|
-
* Get all MyTags assigned to a track
|
|
5369
|
-
*/
|
|
5370
|
-
findMyTagsForTrack(trackId) {
|
|
5371
|
-
const rows = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5372
|
-
SELECT t.* FROM myTag t
|
|
5373
|
-
INNER JOIN myTag_content tc ON t.myTag_id = tc.myTag_id
|
|
5374
|
-
WHERE tc.content_id = ?
|
|
5375
|
-
`).all(trackId);
|
|
5376
|
-
return rows.map(row => __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_myTagRowToMyTag).call(this, row));
|
|
5377
|
-
}
|
|
5378
|
-
// ==========================================================================
|
|
5379
|
-
// History Queries
|
|
5380
|
-
// ==========================================================================
|
|
5381
|
-
/**
|
|
5382
|
-
* Find all history sessions
|
|
5383
|
-
*/
|
|
5384
|
-
findHistorySessions() {
|
|
5385
|
-
const rows = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5386
|
-
SELECT * FROM history ORDER BY sequenceNo
|
|
5387
|
-
`).all();
|
|
5388
|
-
return rows.map(row => {
|
|
5389
|
-
var _a;
|
|
5390
|
-
return ({
|
|
5391
|
-
id: row.history_id,
|
|
5392
|
-
name: (_a = row.name) !== null && _a !== void 0 ? _a : '',
|
|
5393
|
-
parentId: row.history_id_parent,
|
|
5394
|
-
});
|
|
5395
|
-
});
|
|
5396
|
-
}
|
|
5397
|
-
/**
|
|
5398
|
-
* Get track IDs for a history session in order
|
|
5399
|
-
*/
|
|
5400
|
-
findHistoryContents(historyId) {
|
|
5401
|
-
const rows = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5402
|
-
SELECT content_id FROM history_content
|
|
5403
|
-
WHERE history_id = ? ORDER BY sequenceNo
|
|
5404
|
-
`).all(historyId);
|
|
5405
|
-
return rows.map(r => r.content_id);
|
|
5406
|
-
}
|
|
5407
|
-
// ==========================================================================
|
|
5408
|
-
// Hot Cue Bank Queries
|
|
5409
|
-
// ==========================================================================
|
|
5410
|
-
/**
|
|
5411
|
-
* Find all hot cue bank lists
|
|
5412
|
-
*/
|
|
5413
|
-
findHotCueBankLists() {
|
|
5414
|
-
const rows = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5415
|
-
SELECT * FROM hotCueBankList ORDER BY sequenceNo
|
|
5416
|
-
`).all();
|
|
5417
|
-
return rows.map(row => {
|
|
5418
|
-
var _a;
|
|
5419
|
-
return ({
|
|
5420
|
-
id: row.hotCueBankList_id,
|
|
5421
|
-
name: (_a = row.name) !== null && _a !== void 0 ? _a : '',
|
|
5422
|
-
parentId: row.hotCueBankList_id_parent,
|
|
5423
|
-
});
|
|
5424
|
-
});
|
|
5425
|
-
}
|
|
5426
|
-
/**
|
|
5427
|
-
* Get cue IDs for a hot cue bank list
|
|
5428
|
-
*/
|
|
5429
|
-
findHotCueBankListCues(bankListId) {
|
|
5430
|
-
const rows = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5431
|
-
SELECT cue_id FROM hotCueBankList_cue
|
|
5432
|
-
WHERE hotCueBankList_id = ? ORDER BY sequenceNo
|
|
5433
|
-
`).all(bankListId);
|
|
5434
|
-
return rows.map(r => r.cue_id);
|
|
5435
|
-
}
|
|
5436
|
-
// ==========================================================================
|
|
5437
|
-
// Menu Configuration Queries
|
|
5438
|
-
// ==========================================================================
|
|
5439
|
-
/**
|
|
5440
|
-
* Get all menu items (browse categories)
|
|
5441
|
-
*/
|
|
5442
|
-
findMenuItems() {
|
|
5443
|
-
const rows = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5444
|
-
SELECT * FROM menuItem ORDER BY menuItem_id
|
|
5445
|
-
`).all();
|
|
5446
|
-
return rows.map(row => {
|
|
5447
|
-
var _a, _b;
|
|
5448
|
-
return ({
|
|
5449
|
-
id: row.menuItem_id,
|
|
5450
|
-
kind: (_a = row.kind) !== null && _a !== void 0 ? _a : 0,
|
|
5451
|
-
name: (_b = row.name) !== null && _b !== void 0 ? _b : '',
|
|
5452
|
-
});
|
|
5453
|
-
});
|
|
5454
|
-
}
|
|
5455
|
-
/**
|
|
5456
|
-
* Get visible categories with their menu item info
|
|
5457
|
-
*/
|
|
5458
|
-
findVisibleCategories() {
|
|
5459
|
-
const rows = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5460
|
-
SELECT c.*, m.name as menuName, m.kind as menuKind
|
|
5461
|
-
FROM category c
|
|
5462
|
-
LEFT JOIN menuItem m ON c.menuItem_id = m.menuItem_id
|
|
5463
|
-
WHERE c.isVisible = 1
|
|
5464
|
-
ORDER BY c.sequenceNo
|
|
5465
|
-
`).all();
|
|
5466
|
-
return rows.map(row => {
|
|
5467
|
-
var _a, _b, _c;
|
|
5468
|
-
return ({
|
|
5469
|
-
id: row.category_id,
|
|
5470
|
-
menuItemId: (_a = row.menuItem_id) !== null && _a !== void 0 ? _a : 0,
|
|
5471
|
-
name: (_b = row.menuName) !== null && _b !== void 0 ? _b : '',
|
|
5472
|
-
kind: (_c = row.menuKind) !== null && _c !== void 0 ? _c : 0,
|
|
5473
|
-
isVisible: !!row.isVisible,
|
|
5474
|
-
});
|
|
5475
|
-
});
|
|
5476
|
-
}
|
|
5477
|
-
/**
|
|
5478
|
-
* Get visible sort options
|
|
5479
|
-
*/
|
|
5480
|
-
findVisibleSortOptions() {
|
|
5481
|
-
const rows = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5482
|
-
SELECT s.*, m.name as menuName, m.kind as menuKind
|
|
5483
|
-
FROM sort s
|
|
5484
|
-
LEFT JOIN menuItem m ON s.menuItem_id = m.menuItem_id
|
|
5485
|
-
WHERE s.isVisible = 1
|
|
5486
|
-
ORDER BY s.sequenceNo
|
|
5487
|
-
`).all();
|
|
5488
|
-
return rows.map(row => {
|
|
5489
|
-
var _a, _b, _c;
|
|
5490
|
-
return ({
|
|
5491
|
-
id: row.sort_id,
|
|
5492
|
-
menuItemId: (_a = row.menuItem_id) !== null && _a !== void 0 ? _a : 0,
|
|
5493
|
-
name: (_b = row.menuName) !== null && _b !== void 0 ? _b : '',
|
|
5494
|
-
kind: (_c = row.menuKind) !== null && _c !== void 0 ? _c : 0,
|
|
5495
|
-
isVisible: !!row.isVisible,
|
|
5496
|
-
isSelectedAsSubColumn: !!row.isSelectedAsSubColumn,
|
|
5497
|
-
});
|
|
5498
|
-
});
|
|
5499
|
-
}
|
|
5500
|
-
// ==========================================================================
|
|
5501
|
-
// Device Property Queries
|
|
5502
|
-
// ==========================================================================
|
|
5503
|
-
/**
|
|
5504
|
-
* Get device properties
|
|
5505
|
-
*/
|
|
5506
|
-
getProperty() {
|
|
5507
|
-
var _a, _b, _c, _d, _e;
|
|
5508
|
-
const row = __classPrivateFieldGet(this, _OneLibraryAdapter_instances, "m", _OneLibraryAdapter_getStmt).call(this, `
|
|
5509
|
-
SELECT * FROM property LIMIT 1
|
|
5510
|
-
`).get();
|
|
5511
|
-
if (!row) {
|
|
5512
|
-
return null;
|
|
5513
|
-
}
|
|
5514
|
-
return {
|
|
5515
|
-
deviceName: (_a = row.deviceName) !== null && _a !== void 0 ? _a : '',
|
|
5516
|
-
dbVersion: (_b = row.dbVersion) !== null && _b !== void 0 ? _b : '',
|
|
5517
|
-
numberOfContents: (_c = row.numberOfContents) !== null && _c !== void 0 ? _c : 0,
|
|
5518
|
-
createdDate: (_d = row.createdDate) !== null && _d !== void 0 ? _d : '',
|
|
5519
|
-
backgroundColorType: (_e = row.backGroundColorType) !== null && _e !== void 0 ? _e : 0,
|
|
5520
|
-
};
|
|
5521
|
-
}
|
|
5522
|
-
}
|
|
5523
|
-
exports.OneLibraryAdapter = OneLibraryAdapter;
|
|
5524
|
-
_OneLibraryAdapter_db = new WeakMap(), _OneLibraryAdapter_stmtCache = new WeakMap(), _OneLibraryAdapter_instances = new WeakSet(), _OneLibraryAdapter_getStmt = function _OneLibraryAdapter_getStmt(sql) {
|
|
5525
|
-
let stmt = __classPrivateFieldGet(this, _OneLibraryAdapter_stmtCache, "f").get(sql);
|
|
5526
|
-
if (!stmt) {
|
|
5527
|
-
stmt = __classPrivateFieldGet(this, _OneLibraryAdapter_db, "f").prepare(sql);
|
|
5528
|
-
__classPrivateFieldGet(this, _OneLibraryAdapter_stmtCache, "f").set(sql, stmt);
|
|
5529
|
-
}
|
|
5530
|
-
return stmt;
|
|
5531
|
-
}, _OneLibraryAdapter_contentToTrack = function _OneLibraryAdapter_contentToTrack(row) {
|
|
5532
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0;
|
|
5533
|
-
const track = {
|
|
5534
|
-
id: row.content_id,
|
|
5535
|
-
title: (_a = row.title) !== null && _a !== void 0 ? _a : '',
|
|
5536
|
-
duration: row.length ? row.length / 1000 : 0, // ms to seconds
|
|
5537
|
-
bitrate: (_b = row.bitrate) !== null && _b !== void 0 ? _b : undefined,
|
|
5538
|
-
tempo: row.bpmx100 ? row.bpmx100 / 100 : 0,
|
|
5539
|
-
rating: (_c = row.rating) !== null && _c !== void 0 ? _c : 0,
|
|
5540
|
-
comment: (_d = row.djComment) !== null && _d !== void 0 ? _d : '',
|
|
5541
|
-
filePath: (_e = row.path) !== null && _e !== void 0 ? _e : '',
|
|
5542
|
-
fileName: (_f = row.fileName) !== null && _f !== void 0 ? _f : '',
|
|
5543
|
-
trackNumber: (_g = row.trackNo) !== null && _g !== void 0 ? _g : undefined,
|
|
5544
|
-
discNumber: (_h = row.discNo) !== null && _h !== void 0 ? _h : undefined,
|
|
5545
|
-
sampleRate: (_j = row.samplingRate) !== null && _j !== void 0 ? _j : undefined,
|
|
5546
|
-
sampleDepth: (_k = row.bitDepth) !== null && _k !== void 0 ? _k : undefined,
|
|
5547
|
-
playCount: (_l = row.djPlayCount) !== null && _l !== void 0 ? _l : undefined,
|
|
5548
|
-
year: (_m = row.releaseYear) !== null && _m !== void 0 ? _m : undefined,
|
|
5549
|
-
mixName: (_o = row.subtitle) !== null && _o !== void 0 ? _o : undefined,
|
|
5550
|
-
autoloadHotcues: !!row.isHotCueAutoLoadOn,
|
|
5551
|
-
kuvoPublic: !!row.isKuvoDeliverStatusOn,
|
|
5552
|
-
fileSize: (_p = row.fileSize) !== null && _p !== void 0 ? _p : undefined,
|
|
5553
|
-
// Normalize analyzePath by trimming the .DAT extension (same as rekordbox.ts)
|
|
5554
|
-
// loadAnlz() will append the appropriate extension (.DAT or .EXT) when loading
|
|
5555
|
-
analyzePath: row.analysisDataFilePath
|
|
5556
|
-
? row.analysisDataFilePath.substring(0, row.analysisDataFilePath.length - 4)
|
|
5557
|
-
: undefined,
|
|
5558
|
-
releaseDate: (_q = row.releaseDate) !== null && _q !== void 0 ? _q : undefined,
|
|
5559
|
-
dateAdded: row.dateAdded ? new Date(row.dateAdded) : undefined,
|
|
5560
|
-
beatGrid: null,
|
|
5561
|
-
cueAndLoops: null,
|
|
5562
|
-
waveformHd: null,
|
|
5563
|
-
artwork: row.image_id
|
|
5564
|
-
? { id: row.image_id, path: (_r = row.artworkPath) !== null && _r !== void 0 ? _r : undefined }
|
|
5565
|
-
: null,
|
|
5566
|
-
artist: row.artist_id_artist
|
|
5567
|
-
? { id: row.artist_id_artist, name: (_s = row.artistName) !== null && _s !== void 0 ? _s : '' }
|
|
5568
|
-
: null,
|
|
5569
|
-
originalArtist: row.artist_id_originalArtist
|
|
5570
|
-
? {
|
|
5571
|
-
id: row.artist_id_originalArtist,
|
|
5572
|
-
name: (_t = row.originalArtistName) !== null && _t !== void 0 ? _t : '',
|
|
5573
|
-
}
|
|
5574
|
-
: null,
|
|
5575
|
-
remixer: row.artist_id_remixer
|
|
5576
|
-
? { id: row.artist_id_remixer, name: (_u = row.remixerName) !== null && _u !== void 0 ? _u : '' }
|
|
5577
|
-
: null,
|
|
5578
|
-
composer: row.artist_id_composer
|
|
5579
|
-
? { id: row.artist_id_composer, name: (_v = row.composerName) !== null && _v !== void 0 ? _v : '' }
|
|
5580
|
-
: null,
|
|
5581
|
-
album: row.album_id
|
|
5582
|
-
? { id: row.album_id, name: (_w = row.albumName) !== null && _w !== void 0 ? _w : '' }
|
|
5583
|
-
: null,
|
|
5584
|
-
label: row.label_id
|
|
5585
|
-
? { id: row.label_id, name: (_x = row.labelName) !== null && _x !== void 0 ? _x : '' }
|
|
5586
|
-
: null,
|
|
5587
|
-
genre: row.genre_id
|
|
5588
|
-
? { id: row.genre_id, name: (_y = row.genreName) !== null && _y !== void 0 ? _y : '' }
|
|
5589
|
-
: null,
|
|
5590
|
-
color: row.color_id
|
|
5591
|
-
? { id: row.color_id, name: (_z = row.colorName) !== null && _z !== void 0 ? _z : '' }
|
|
5592
|
-
: null,
|
|
5593
|
-
key: row.key_id ? { id: row.key_id, name: (_0 = row.keyName) !== null && _0 !== void 0 ? _0 : '' } : null,
|
|
5594
|
-
};
|
|
5595
|
-
return track;
|
|
5596
|
-
}, _OneLibraryAdapter_cueToCueAndLoop = function _OneLibraryAdapter_cueToCueAndLoop(row) {
|
|
5597
|
-
var _a, _b, _c, _d, _e, _f;
|
|
5598
|
-
const offset = row.inUsec ? row.inUsec / 1000 : 0; // microseconds to ms
|
|
5599
|
-
const outOffset = (_a = row.outUsec) !== null && _a !== void 0 ? _a : null;
|
|
5600
|
-
const isLoop = outOffset !== null && outOffset > 0;
|
|
5601
|
-
const length = isLoop ? (outOffset - ((_b = row.inUsec) !== null && _b !== void 0 ? _b : 0)) / 1000 : 0;
|
|
5602
|
-
// Determine if this is a hot cue (kind value mapping may vary)
|
|
5603
|
-
// Based on observation: kind 0 = memory cue, kind >= 1 may be hot cue
|
|
5604
|
-
const isHotCue = ((_c = row.kind) !== null && _c !== void 0 ? _c : 0) >= 1 && ((_d = row.kind) !== null && _d !== void 0 ? _d : 0) <= 8;
|
|
5605
|
-
const button = isHotCue ? ((_e = row.kind) !== null && _e !== void 0 ? _e : 0) : undefined;
|
|
5606
|
-
const label = (_f = row.cueComment) !== null && _f !== void 0 ? _f : undefined;
|
|
5607
|
-
const color = row.colorTableIndex;
|
|
5608
|
-
if (isLoop && isHotCue) {
|
|
5609
|
-
return {
|
|
5610
|
-
type: 'hot_loop',
|
|
5611
|
-
offset,
|
|
5612
|
-
length,
|
|
5613
|
-
button: button,
|
|
5614
|
-
label,
|
|
5615
|
-
color,
|
|
5616
|
-
};
|
|
5617
|
-
}
|
|
5618
|
-
if (isLoop) {
|
|
5619
|
-
return {
|
|
5620
|
-
type: 'loop',
|
|
5621
|
-
offset,
|
|
5622
|
-
length,
|
|
5623
|
-
label,
|
|
5624
|
-
color,
|
|
5625
|
-
};
|
|
5626
|
-
}
|
|
5627
|
-
if (isHotCue) {
|
|
5628
|
-
return {
|
|
5629
|
-
type: 'hot_cue',
|
|
5630
|
-
offset,
|
|
5631
|
-
button: button,
|
|
5632
|
-
label,
|
|
5633
|
-
color,
|
|
5634
|
-
};
|
|
5635
|
-
}
|
|
5636
|
-
return {
|
|
5637
|
-
type: 'cue_point',
|
|
5638
|
-
offset,
|
|
5639
|
-
label,
|
|
5640
|
-
color,
|
|
5641
|
-
};
|
|
5642
|
-
}, _OneLibraryAdapter_playlistRowToPlaylist = function _OneLibraryAdapter_playlistRowToPlaylist(row) {
|
|
5643
|
-
var _a;
|
|
5644
|
-
return {
|
|
5645
|
-
id: row.playlist_id,
|
|
5646
|
-
name: (_a = row.name) !== null && _a !== void 0 ? _a : '',
|
|
5647
|
-
isFolder: row.attribute === onelibrary_schema_1.PlaylistAttribute.FOLDER,
|
|
5648
|
-
parentId: row.playlist_id_parent,
|
|
5649
|
-
};
|
|
5650
|
-
}, _OneLibraryAdapter_myTagRowToMyTag = function _OneLibraryAdapter_myTagRowToMyTag(row) {
|
|
5651
|
-
var _a;
|
|
5652
|
-
return {
|
|
5653
|
-
id: row.myTag_id,
|
|
5654
|
-
name: (_a = row.name) !== null && _a !== void 0 ? _a : '',
|
|
5655
|
-
isFolder: row.attribute === onelibrary_schema_1.MyTagAttribute.FOLDER,
|
|
5656
|
-
parentId: row.myTag_id_parent,
|
|
5657
|
-
};
|
|
5658
|
-
};
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
/***/ },
|
|
5662
|
-
|
|
5663
|
-
/***/ "./src/localdb/orm.ts"
|
|
5664
|
-
/*!****************************!*\
|
|
5665
|
-
!*** ./src/localdb/orm.ts ***!
|
|
5666
|
-
\****************************/
|
|
5667
|
-
(__unused_webpack_module, exports, __webpack_require__) {
|
|
5668
|
-
|
|
5669
|
-
"use strict";
|
|
5670
|
-
|
|
5671
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
5672
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
5673
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5674
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5675
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
5676
|
-
};
|
|
5677
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
5678
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
5679
|
-
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");
|
|
5680
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5681
|
-
};
|
|
5682
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5683
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5684
|
-
};
|
|
5685
|
-
var _MetadataORM_conn, _MetadataORM_stmtCache, _MetadataORM_inTransaction;
|
|
5686
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
5687
|
-
exports.MetadataORM = exports.Table = void 0;
|
|
5688
|
-
const better_sqlite3_multiple_ciphers_1 = __importDefault(__webpack_require__(/*! better-sqlite3-multiple-ciphers */ "better-sqlite3-multiple-ciphers"));
|
|
5689
|
-
const lodash_1 = __webpack_require__(/*! lodash */ "lodash");
|
|
5690
|
-
const schema_1 = __webpack_require__(/*! ./schema */ "./src/localdb/schema.ts");
|
|
5691
|
-
/**
|
|
5692
|
-
* Table names available
|
|
5175
|
+
* Table names available
|
|
5693
5176
|
*/
|
|
5694
5177
|
var Table;
|
|
5695
5178
|
(function (Table) {
|
|
@@ -5863,215 +5346,245 @@ _MetadataORM_conn = new WeakMap(), _MetadataORM_stmtCache = new WeakMap(), _Meta
|
|
|
5863
5346
|
|
|
5864
5347
|
"use strict";
|
|
5865
5348
|
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
Object.defineProperty(o, k2, desc);
|
|
5873
|
-
}) : (function(o, m, k, k2) {
|
|
5874
|
-
if (k2 === undefined) k2 = k;
|
|
5875
|
-
o[k2] = m[k];
|
|
5876
|
-
}));
|
|
5877
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
5878
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
5879
|
-
}) : function(o, v) {
|
|
5880
|
-
o["default"] = v;
|
|
5881
|
-
});
|
|
5882
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
5883
|
-
var ownKeys = function(o) {
|
|
5884
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
5885
|
-
var ar = [];
|
|
5886
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
5887
|
-
return ar;
|
|
5888
|
-
};
|
|
5889
|
-
return ownKeys(o);
|
|
5890
|
-
};
|
|
5891
|
-
return function (mod) {
|
|
5892
|
-
if (mod && mod.__esModule) return mod;
|
|
5893
|
-
var result = {};
|
|
5894
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
5895
|
-
__setModuleDefault(result, mod);
|
|
5896
|
-
return result;
|
|
5897
|
-
};
|
|
5898
|
-
})();
|
|
5899
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
5900
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
5901
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5902
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5903
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
5904
|
-
};
|
|
5905
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
5906
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
5907
|
-
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");
|
|
5908
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5909
|
-
};
|
|
5910
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5911
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5912
|
-
};
|
|
5913
|
-
var _RekordboxHydrator_orm, _RekordboxHydrator_onProgress;
|
|
5349
|
+
/**
|
|
5350
|
+
* Rekordbox Database Utilities
|
|
5351
|
+
*
|
|
5352
|
+
* Re-exports from the rekordbox/ folder for backward compatibility.
|
|
5353
|
+
* @see ./rekordbox/index.ts for the implementation
|
|
5354
|
+
*/
|
|
5914
5355
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
5915
|
-
exports.hydrateDatabase =
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5356
|
+
exports.loadAnlz = exports.hydrateDatabase = void 0;
|
|
5357
|
+
var index_1 = __webpack_require__(/*! ./rekordbox/index */ "./src/localdb/rekordbox/index.ts");
|
|
5358
|
+
Object.defineProperty(exports, "hydrateDatabase", ({ enumerable: true, get: function () { return index_1.hydrateDatabase; } }));
|
|
5359
|
+
Object.defineProperty(exports, "loadAnlz", ({ enumerable: true, get: function () { return index_1.loadAnlz; } }));
|
|
5360
|
+
|
|
5361
|
+
|
|
5362
|
+
/***/ },
|
|
5363
|
+
|
|
5364
|
+
/***/ "./src/localdb/rekordbox/anlz-parsers.ts"
|
|
5365
|
+
/*!***********************************************!*\
|
|
5366
|
+
!*** ./src/localdb/rekordbox/anlz-parsers.ts ***!
|
|
5367
|
+
\***********************************************/
|
|
5368
|
+
(__unused_webpack_module, exports, __webpack_require__) {
|
|
5369
|
+
|
|
5370
|
+
"use strict";
|
|
5371
|
+
|
|
5372
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
5373
|
+
exports.makeBeatGrid = makeBeatGrid;
|
|
5374
|
+
exports.makeCueAndLoop = makeCueAndLoop;
|
|
5375
|
+
exports.makeWaveformHd = makeWaveformHd;
|
|
5376
|
+
exports.makeExtendedCues = makeExtendedCues;
|
|
5377
|
+
exports.makeSongStructure = makeSongStructure;
|
|
5378
|
+
exports.makeWaveformPreview = makeWaveformPreview;
|
|
5379
|
+
exports.makeWaveform3BandPreview = makeWaveform3BandPreview;
|
|
5380
|
+
exports.makeWaveform3BandDetail = makeWaveform3BandDetail;
|
|
5381
|
+
exports.makeVocalConfig = makeVocalConfig;
|
|
5921
5382
|
const utils_1 = __webpack_require__(/*! src/localdb/utils */ "./src/localdb/utils.ts");
|
|
5922
5383
|
const converters_1 = __webpack_require__(/*! src/utils/converters */ "./src/utils/converters.ts");
|
|
5923
|
-
const Telemetry = __importStar(__webpack_require__(/*! src/utils/telemetry */ "./src/utils/telemetry.ts"));
|
|
5924
5384
|
/**
|
|
5925
|
-
*
|
|
5926
|
-
* database with all entities from the Rekordbox database. This includes all
|
|
5927
|
-
* track metadata, including analyzed metadata (such as beatgrids and waveforms).
|
|
5385
|
+
* Fill beatgrid data from the ANLZ section
|
|
5928
5386
|
*/
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5387
|
+
function makeBeatGrid(data) {
|
|
5388
|
+
return data.body.beats.map((beat) => ({
|
|
5389
|
+
offset: beat.time,
|
|
5390
|
+
bpm: beat.tempo / 100,
|
|
5391
|
+
count: beat.beatNumber,
|
|
5392
|
+
}));
|
|
5932
5393
|
}
|
|
5933
5394
|
/**
|
|
5934
|
-
*
|
|
5395
|
+
* Fill cue and loop data from the ANLZ section
|
|
5935
5396
|
*/
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
resultDat.beatGrid = makeBeatGrid(section);
|
|
5948
|
-
break;
|
|
5949
|
-
case SectionTags.CUES:
|
|
5950
|
-
resultDat.cueAndLoops = makeCueAndLoop(section);
|
|
5951
|
-
break;
|
|
5952
|
-
case SectionTags.CUES_2:
|
|
5953
|
-
resultExt.extendedCues = makeExtendedCues(section);
|
|
5954
|
-
break;
|
|
5955
|
-
case SectionTags.WAVE_PREVIEW:
|
|
5956
|
-
resultDat.waveformPreview = makeWaveformPreview(section);
|
|
5957
|
-
break;
|
|
5958
|
-
case SectionTags.WAVE_TINY:
|
|
5959
|
-
resultDat.waveformTiny = makeWaveformPreview(section);
|
|
5960
|
-
break;
|
|
5961
|
-
case SectionTags.WAVE_SCROLL:
|
|
5962
|
-
resultExt.waveformDetail = Buffer.from(section.body.entries);
|
|
5963
|
-
break;
|
|
5964
|
-
case SectionTags.WAVE_COLOR_PREVIEW:
|
|
5965
|
-
resultExt.waveformColorPreview = Buffer.from(section.body.entries);
|
|
5966
|
-
break;
|
|
5967
|
-
case SectionTags.WAVE_COLOR_SCROLL:
|
|
5968
|
-
resultExt.waveformHd = makeWaveformHd(section);
|
|
5969
|
-
break;
|
|
5970
|
-
case SectionTags.SONG_STRUCTURE:
|
|
5971
|
-
resultExt.songStructure = makeSongStructure(section);
|
|
5972
|
-
break;
|
|
5973
|
-
// VBR and PATH tags are defined but not currently extracted
|
|
5974
|
-
// as they're not commonly needed in the application
|
|
5975
|
-
case SectionTags.VBR:
|
|
5976
|
-
case SectionTags.PATH:
|
|
5977
|
-
break;
|
|
5978
|
-
}
|
|
5979
|
-
}
|
|
5980
|
-
return result;
|
|
5397
|
+
function makeCueAndLoop(data) {
|
|
5398
|
+
return data.body.cues.map((entry) => {
|
|
5399
|
+
// Cues with the status 0 are likely leftovers that were removed
|
|
5400
|
+
const button = entry.hotCue === 0 ? false : entry.type;
|
|
5401
|
+
const isCue = entry.type === 0x01;
|
|
5402
|
+
const isLoop = entry.type === 0x02;
|
|
5403
|
+
// NOTE: Unlike the remotedb, these entries are already in milliseconds.
|
|
5404
|
+
const offset = entry.time;
|
|
5405
|
+
const length = entry.loopTime - offset;
|
|
5406
|
+
return (0, utils_1.makeCueLoopEntry)(isCue, isLoop, offset, length, button);
|
|
5407
|
+
});
|
|
5981
5408
|
}
|
|
5982
5409
|
/**
|
|
5983
|
-
*
|
|
5984
|
-
* analysis (ANLZ) files into the common entity types used in this library.
|
|
5410
|
+
* Fill waveform HD data from the ANLZ section
|
|
5985
5411
|
*/
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
const db = new rekordbox_pdb_ksy_1.default(stream);
|
|
6004
|
-
parseTx.finish();
|
|
6005
|
-
const hydrateTx = tx.startChild({ op: 'hydration' });
|
|
6006
|
-
await Promise.all(db.tables.map((table) => this.hydrateFromTable(table, hydrateTx)));
|
|
6007
|
-
hydrateTx.finish();
|
|
6008
|
-
tx.finish();
|
|
6009
|
-
}
|
|
6010
|
-
/**
|
|
6011
|
-
* Hydrate the database with entities from the provided RekordboxPdb table.
|
|
6012
|
-
* See pdbEntityCreators for how tables are mapped into database entities.
|
|
6013
|
-
*/
|
|
6014
|
-
async hydrateFromTable(table, span) {
|
|
6015
|
-
const tableName = pdbTables[table.type];
|
|
6016
|
-
const createObject = pdbEntityCreators[table.type];
|
|
6017
|
-
const tx = span.startChild({ op: 'hydrateFromTable', description: tableName });
|
|
6018
|
-
if (createObject === undefined) {
|
|
6019
|
-
return;
|
|
5412
|
+
function makeWaveformHd(data) {
|
|
5413
|
+
return (0, converters_1.convertWaveformHDData)(Buffer.from(data.body.entries));
|
|
5414
|
+
}
|
|
5415
|
+
/**
|
|
5416
|
+
* Parse extended cues (PCO2) with colors and comments
|
|
5417
|
+
*/
|
|
5418
|
+
function makeExtendedCues(data) {
|
|
5419
|
+
return data.body.cues.map((entry) => {
|
|
5420
|
+
var _a, _b, _c, _d;
|
|
5421
|
+
const cue = {
|
|
5422
|
+
hotCue: entry.hotCue,
|
|
5423
|
+
type: entry.type,
|
|
5424
|
+
time: entry.time,
|
|
5425
|
+
};
|
|
5426
|
+
// Add loop end time if this is a loop
|
|
5427
|
+
if (entry.type === 2 && entry.loopTime !== undefined) {
|
|
5428
|
+
cue.loopTime = entry.loopTime;
|
|
6020
5429
|
}
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
void _row; // Intentionally unused - just counting
|
|
6025
|
-
totalItems++;
|
|
5430
|
+
// Add color ID for memory points/loops
|
|
5431
|
+
if (entry.colorId !== undefined && entry.colorId > 0) {
|
|
5432
|
+
cue.colorId = entry.colorId;
|
|
6026
5433
|
}
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
// Report progress and yield every 100 rows (instead of every row)
|
|
6036
|
-
if (totalSaved % 100 === 0 || totalSaved === totalItems) {
|
|
6037
|
-
__classPrivateFieldGet(this, _RekordboxHydrator_onProgress, "f").call(this, { complete: totalSaved, table: tableName, total: totalItems });
|
|
6038
|
-
// Yield to event loop periodically to keep UI responsive
|
|
6039
|
-
await new Promise(r => setTimeout(r, 0));
|
|
6040
|
-
}
|
|
6041
|
-
}
|
|
5434
|
+
// Add hot cue color information
|
|
5435
|
+
if (entry.colorCode !== undefined && entry.colorCode > 0) {
|
|
5436
|
+
cue.colorCode = entry.colorCode;
|
|
5437
|
+
cue.colorRgb = {
|
|
5438
|
+
r: (_a = entry.colorRed) !== null && _a !== void 0 ? _a : 0,
|
|
5439
|
+
g: (_b = entry.colorGreen) !== null && _b !== void 0 ? _b : 0,
|
|
5440
|
+
b: (_c = entry.colorBlue) !== null && _c !== void 0 ? _c : 0,
|
|
5441
|
+
};
|
|
6042
5442
|
}
|
|
6043
|
-
|
|
6044
|
-
|
|
5443
|
+
// Add comment if present
|
|
5444
|
+
if (entry.lenComment > 0 && entry.comment) {
|
|
5445
|
+
cue.comment = entry.comment;
|
|
6045
5446
|
}
|
|
6046
|
-
|
|
6047
|
-
|
|
5447
|
+
// Add quantized loop information if present
|
|
5448
|
+
if (entry.loopNumerator !== undefined && entry.loopNumerator > 0) {
|
|
5449
|
+
cue.loopNumerator = entry.loopNumerator;
|
|
5450
|
+
cue.loopDenominator = (_d = entry.loopDenominator) !== null && _d !== void 0 ? _d : 1;
|
|
5451
|
+
}
|
|
5452
|
+
return cue;
|
|
5453
|
+
});
|
|
6048
5454
|
}
|
|
6049
|
-
_RekordboxHydrator_orm = new WeakMap(), _RekordboxHydrator_onProgress = new WeakMap();
|
|
6050
5455
|
/**
|
|
6051
|
-
*
|
|
6052
|
-
* This flattens the concept of rowGroups and refs.
|
|
5456
|
+
* Parse song structure (PSSI) with phrase analysis
|
|
6053
5457
|
*/
|
|
6054
|
-
function
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
5458
|
+
function makeSongStructure(data) {
|
|
5459
|
+
var _a, _b;
|
|
5460
|
+
const moodMap = {
|
|
5461
|
+
1: 'high',
|
|
5462
|
+
2: 'mid',
|
|
5463
|
+
3: 'low',
|
|
5464
|
+
};
|
|
5465
|
+
const bankMap = {
|
|
5466
|
+
0: 'default',
|
|
5467
|
+
1: 'cool',
|
|
5468
|
+
2: 'natural',
|
|
5469
|
+
3: 'hot',
|
|
5470
|
+
4: 'subtle',
|
|
5471
|
+
5: 'warm',
|
|
5472
|
+
6: 'vivid',
|
|
5473
|
+
7: 'club_1',
|
|
5474
|
+
8: 'club_2',
|
|
5475
|
+
};
|
|
5476
|
+
// Phrase type mappings based on mood
|
|
5477
|
+
const phraseTypeMap = {
|
|
5478
|
+
high: {
|
|
5479
|
+
1: 'Intro',
|
|
5480
|
+
2: 'Up',
|
|
5481
|
+
3: 'Down',
|
|
5482
|
+
5: 'Chorus',
|
|
5483
|
+
6: 'Outro',
|
|
5484
|
+
},
|
|
5485
|
+
mid: {
|
|
5486
|
+
1: 'Intro',
|
|
5487
|
+
2: 'Verse 1',
|
|
5488
|
+
3: 'Verse 2',
|
|
5489
|
+
4: 'Verse 3',
|
|
5490
|
+
5: 'Verse 4',
|
|
5491
|
+
6: 'Verse 5',
|
|
5492
|
+
7: 'Verse 6',
|
|
5493
|
+
8: 'Bridge',
|
|
5494
|
+
9: 'Chorus',
|
|
5495
|
+
10: 'Outro',
|
|
5496
|
+
},
|
|
5497
|
+
low: {
|
|
5498
|
+
1: 'Intro',
|
|
5499
|
+
2: 'Verse 1',
|
|
5500
|
+
3: 'Verse 1',
|
|
5501
|
+
4: 'Verse 1',
|
|
5502
|
+
5: 'Verse 2',
|
|
5503
|
+
6: 'Verse 2',
|
|
5504
|
+
7: 'Verse 2',
|
|
5505
|
+
8: 'Bridge',
|
|
5506
|
+
9: 'Chorus',
|
|
5507
|
+
10: 'Outro',
|
|
5508
|
+
},
|
|
5509
|
+
};
|
|
5510
|
+
const mood = (_a = moodMap[data.body.mood]) !== null && _a !== void 0 ? _a : 'high';
|
|
5511
|
+
const bank = (_b = bankMap[data.body.rawBank]) !== null && _b !== void 0 ? _b : 'default';
|
|
5512
|
+
const phrases = data.body.entries.map((entry) => {
|
|
5513
|
+
var _a;
|
|
5514
|
+
const phrase = {
|
|
5515
|
+
index: entry.index,
|
|
5516
|
+
beat: entry.beat,
|
|
5517
|
+
kind: entry.kind,
|
|
5518
|
+
phraseType: (_a = phraseTypeMap[mood][entry.kind]) !== null && _a !== void 0 ? _a : 'Unknown',
|
|
5519
|
+
};
|
|
5520
|
+
// Add fill-in information if present
|
|
5521
|
+
if (entry.fill > 0) {
|
|
5522
|
+
phrase.fill = entry.fill;
|
|
5523
|
+
phrase.fillBeat = entry.beatFill;
|
|
6072
5524
|
}
|
|
6073
|
-
|
|
5525
|
+
return phrase;
|
|
5526
|
+
});
|
|
5527
|
+
return {
|
|
5528
|
+
mood,
|
|
5529
|
+
bank,
|
|
5530
|
+
endBeat: data.body.endBeat,
|
|
5531
|
+
phrases,
|
|
5532
|
+
};
|
|
6074
5533
|
}
|
|
5534
|
+
/**
|
|
5535
|
+
* Parse waveform preview data (PWAV/PWV2)
|
|
5536
|
+
*/
|
|
5537
|
+
function makeWaveformPreview(data) {
|
|
5538
|
+
return {
|
|
5539
|
+
data: Buffer.from(data.body.data),
|
|
5540
|
+
};
|
|
5541
|
+
}
|
|
5542
|
+
/**
|
|
5543
|
+
* Parse 3-band color waveform preview (PWV6 from .2EX files)
|
|
5544
|
+
*/
|
|
5545
|
+
function makeWaveform3BandPreview(data) {
|
|
5546
|
+
return {
|
|
5547
|
+
numEntries: data.body.lenEntries,
|
|
5548
|
+
data: Buffer.from(data.body.entries),
|
|
5549
|
+
};
|
|
5550
|
+
}
|
|
5551
|
+
/**
|
|
5552
|
+
* Parse 3-band color detail waveform (PWV7 from .2EX files)
|
|
5553
|
+
*/
|
|
5554
|
+
function makeWaveform3BandDetail(data) {
|
|
5555
|
+
return {
|
|
5556
|
+
numEntries: data.body.lenEntries,
|
|
5557
|
+
data: Buffer.from(data.body.entries),
|
|
5558
|
+
};
|
|
5559
|
+
}
|
|
5560
|
+
/**
|
|
5561
|
+
* Parse vocal detection config (PWVC from .2EX files)
|
|
5562
|
+
*/
|
|
5563
|
+
function makeVocalConfig(data) {
|
|
5564
|
+
return {
|
|
5565
|
+
thresholdLow: data.body.thresholdLow,
|
|
5566
|
+
thresholdMid: data.body.thresholdMid,
|
|
5567
|
+
thresholdHigh: data.body.thresholdHigh,
|
|
5568
|
+
};
|
|
5569
|
+
}
|
|
5570
|
+
|
|
5571
|
+
|
|
5572
|
+
/***/ },
|
|
5573
|
+
|
|
5574
|
+
/***/ "./src/localdb/rekordbox/entity-creators.ts"
|
|
5575
|
+
/*!**************************************************!*\
|
|
5576
|
+
!*** ./src/localdb/rekordbox/entity-creators.ts ***!
|
|
5577
|
+
\**************************************************/
|
|
5578
|
+
(__unused_webpack_module, exports) {
|
|
5579
|
+
|
|
5580
|
+
"use strict";
|
|
5581
|
+
|
|
5582
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
5583
|
+
exports.makeIdNameHydrator = void 0;
|
|
5584
|
+
exports.createTrack = createTrack;
|
|
5585
|
+
exports.createPlaylist = createPlaylist;
|
|
5586
|
+
exports.createPlaylistEntry = createPlaylistEntry;
|
|
5587
|
+
exports.createArtworkEntry = createArtworkEntry;
|
|
6075
5588
|
const ensureDate = (date) => date instanceof Date && !isNaN(date.valueOf()) ? date : undefined;
|
|
6076
5589
|
/**
|
|
6077
5590
|
* Utility to create a hydrator that hydrates the provided entity with the id
|
|
@@ -6084,6 +5597,7 @@ const makeIdNameHydrator = () => (row) => {
|
|
|
6084
5597
|
name: (_a = row.name.body.text) !== null && _a !== void 0 ? _a : '',
|
|
6085
5598
|
});
|
|
6086
5599
|
};
|
|
5600
|
+
exports.makeIdNameHydrator = makeIdNameHydrator;
|
|
6087
5601
|
/**
|
|
6088
5602
|
* Translates a pdb track row entry to a {@link Track} entity.
|
|
6089
5603
|
*/
|
|
@@ -6176,170 +5690,282 @@ function createArtworkEntry(artworkRow) {
|
|
|
6176
5690
|
};
|
|
6177
5691
|
return art;
|
|
6178
5692
|
}
|
|
5693
|
+
|
|
5694
|
+
|
|
5695
|
+
/***/ },
|
|
5696
|
+
|
|
5697
|
+
/***/ "./src/localdb/rekordbox/hydrator.ts"
|
|
5698
|
+
/*!*******************************************!*\
|
|
5699
|
+
!*** ./src/localdb/rekordbox/hydrator.ts ***!
|
|
5700
|
+
\*******************************************/
|
|
5701
|
+
(__unused_webpack_module, exports, __webpack_require__) {
|
|
5702
|
+
|
|
5703
|
+
"use strict";
|
|
5704
|
+
|
|
5705
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
5706
|
+
if (k2 === undefined) k2 = k;
|
|
5707
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5708
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
5709
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
5710
|
+
}
|
|
5711
|
+
Object.defineProperty(o, k2, desc);
|
|
5712
|
+
}) : (function(o, m, k, k2) {
|
|
5713
|
+
if (k2 === undefined) k2 = k;
|
|
5714
|
+
o[k2] = m[k];
|
|
5715
|
+
}));
|
|
5716
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
5717
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
5718
|
+
}) : function(o, v) {
|
|
5719
|
+
o["default"] = v;
|
|
5720
|
+
});
|
|
5721
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
5722
|
+
var ownKeys = function(o) {
|
|
5723
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
5724
|
+
var ar = [];
|
|
5725
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
5726
|
+
return ar;
|
|
5727
|
+
};
|
|
5728
|
+
return ownKeys(o);
|
|
5729
|
+
};
|
|
5730
|
+
return function (mod) {
|
|
5731
|
+
if (mod && mod.__esModule) return mod;
|
|
5732
|
+
var result = {};
|
|
5733
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
5734
|
+
__setModuleDefault(result, mod);
|
|
5735
|
+
return result;
|
|
5736
|
+
};
|
|
5737
|
+
})();
|
|
5738
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
5739
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
5740
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5741
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5742
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
5743
|
+
};
|
|
5744
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
5745
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
5746
|
+
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");
|
|
5747
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5748
|
+
};
|
|
5749
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5750
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5751
|
+
};
|
|
5752
|
+
var _RekordboxHydrator_orm, _RekordboxHydrator_onProgress;
|
|
5753
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
5754
|
+
exports.RekordboxHydrator = void 0;
|
|
5755
|
+
const kaitai_struct_1 = __webpack_require__(/*! kaitai-struct */ "kaitai-struct");
|
|
5756
|
+
const rekordbox_pdb_ksy_1 = __importDefault(__webpack_require__(/*! src/localdb/kaitai/rekordbox_pdb.ksy */ "./src/localdb/kaitai/rekordbox_pdb.ksy"));
|
|
5757
|
+
const Telemetry = __importStar(__webpack_require__(/*! src/utils/telemetry */ "./src/utils/telemetry.ts"));
|
|
5758
|
+
const table_mappings_1 = __webpack_require__(/*! ./table-mappings */ "./src/localdb/rekordbox/table-mappings.ts");
|
|
6179
5759
|
/**
|
|
6180
|
-
*
|
|
6181
|
-
|
|
6182
|
-
function makeBeatGrid(data) {
|
|
6183
|
-
return data.body.beats.map((beat) => ({
|
|
6184
|
-
offset: beat.time,
|
|
6185
|
-
bpm: beat.tempo / 100,
|
|
6186
|
-
count: beat.beatNumber,
|
|
6187
|
-
}));
|
|
6188
|
-
}
|
|
6189
|
-
/**
|
|
6190
|
-
* Fill cue and loop data from the ANLZ section
|
|
6191
|
-
*/
|
|
6192
|
-
function makeCueAndLoop(data) {
|
|
6193
|
-
return data.body.cues.map((entry) => {
|
|
6194
|
-
// Cues with the status 0 are likely leftovers that were removed
|
|
6195
|
-
const button = entry.hotCue === 0 ? false : entry.type;
|
|
6196
|
-
const isCue = entry.type === 0x01;
|
|
6197
|
-
const isLoop = entry.type === 0x02;
|
|
6198
|
-
// NOTE: Unlike the remotedb, these entries are already in milliseconds.
|
|
6199
|
-
const offset = entry.time;
|
|
6200
|
-
const length = entry.loopTime - offset;
|
|
6201
|
-
return (0, utils_1.makeCueLoopEntry)(isCue, isLoop, offset, length, button);
|
|
6202
|
-
});
|
|
6203
|
-
}
|
|
6204
|
-
/**
|
|
6205
|
-
* Fill waveform HD data from the ANLZ section
|
|
6206
|
-
*/
|
|
6207
|
-
function makeWaveformHd(data) {
|
|
6208
|
-
return (0, converters_1.convertWaveformHDData)(Buffer.from(data.body.entries));
|
|
6209
|
-
}
|
|
6210
|
-
/**
|
|
6211
|
-
* Parse extended cues (PCO2) with colors and comments
|
|
5760
|
+
* This service provides utilities for translating rekordbox database (pdb_ and
|
|
5761
|
+
* analysis (ANLZ) files into the common entity types used in this library.
|
|
6212
5762
|
*/
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
|
|
6223
|
-
|
|
5763
|
+
class RekordboxHydrator {
|
|
5764
|
+
constructor({ orm, onProgress }) {
|
|
5765
|
+
_RekordboxHydrator_orm.set(this, void 0);
|
|
5766
|
+
_RekordboxHydrator_onProgress.set(this, void 0);
|
|
5767
|
+
__classPrivateFieldSet(this, _RekordboxHydrator_orm, orm, "f");
|
|
5768
|
+
__classPrivateFieldSet(this, _RekordboxHydrator_onProgress, onProgress !== null && onProgress !== void 0 ? onProgress : (() => null), "f");
|
|
5769
|
+
}
|
|
5770
|
+
/**
|
|
5771
|
+
* Extract entries from a rekordbox pdb file and hydrate the passed database
|
|
5772
|
+
* connection with entities derived from the rekordbox entries.
|
|
5773
|
+
*/
|
|
5774
|
+
async hydrateFromPdb(pdbData, span) {
|
|
5775
|
+
const tx = span
|
|
5776
|
+
? span.startChild({ op: 'hydrateFromPdb' })
|
|
5777
|
+
: Telemetry.startTransaction({ name: 'hydrateFromPdb' });
|
|
5778
|
+
const parseTx = tx.startChild({ op: 'parsePdbData', data: { size: pdbData.length } });
|
|
5779
|
+
const stream = new kaitai_struct_1.KaitaiStream(pdbData);
|
|
5780
|
+
const db = new rekordbox_pdb_ksy_1.default(stream);
|
|
5781
|
+
parseTx.finish();
|
|
5782
|
+
const hydrateTx = tx.startChild({ op: 'hydration' });
|
|
5783
|
+
await Promise.all(db.tables.map((table) => this.hydrateFromTable(table, hydrateTx)));
|
|
5784
|
+
hydrateTx.finish();
|
|
5785
|
+
tx.finish();
|
|
5786
|
+
}
|
|
5787
|
+
/**
|
|
5788
|
+
* Hydrate the database with entities from the provided RekordboxPdb table.
|
|
5789
|
+
* See pdbEntityCreators for how tables are mapped into database entities.
|
|
5790
|
+
*/
|
|
5791
|
+
async hydrateFromTable(table, span) {
|
|
5792
|
+
const tableName = table_mappings_1.pdbTables[table.type];
|
|
5793
|
+
const createObject = table_mappings_1.pdbEntityCreators[table.type];
|
|
5794
|
+
const tx = span.startChild({ op: 'hydrateFromTable', description: tableName });
|
|
5795
|
+
if (createObject === undefined) {
|
|
5796
|
+
return;
|
|
6224
5797
|
}
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
5798
|
+
let totalSaved = 0;
|
|
5799
|
+
let totalItems = 0;
|
|
5800
|
+
for (const _row of tableRows(table)) {
|
|
5801
|
+
void _row; // Intentionally unused - just counting
|
|
5802
|
+
totalItems++;
|
|
6228
5803
|
}
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
5804
|
+
tx.setData('items', totalItems);
|
|
5805
|
+
// Use transaction for bulk inserts (10-100x faster)
|
|
5806
|
+
__classPrivateFieldGet(this, _RekordboxHydrator_orm, "f").beginTransaction();
|
|
5807
|
+
try {
|
|
5808
|
+
for (const row of tableRows(table)) {
|
|
5809
|
+
const entity = createObject(row);
|
|
5810
|
+
__classPrivateFieldGet(this, _RekordboxHydrator_orm, "f").insertEntity(tableName, entity);
|
|
5811
|
+
totalSaved++;
|
|
5812
|
+
// Report progress and yield every 100 rows (instead of every row)
|
|
5813
|
+
if (totalSaved % 100 === 0 || totalSaved === totalItems) {
|
|
5814
|
+
__classPrivateFieldGet(this, _RekordboxHydrator_onProgress, "f").call(this, { complete: totalSaved, table: tableName, total: totalItems });
|
|
5815
|
+
// Yield to event loop periodically to keep UI responsive
|
|
5816
|
+
await new Promise(r => setTimeout(r, 0));
|
|
5817
|
+
}
|
|
5818
|
+
}
|
|
6237
5819
|
}
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
cue.comment = entry.comment;
|
|
5820
|
+
finally {
|
|
5821
|
+
__classPrivateFieldGet(this, _RekordboxHydrator_orm, "f").commit();
|
|
6241
5822
|
}
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
5823
|
+
tx.finish();
|
|
5824
|
+
}
|
|
5825
|
+
}
|
|
5826
|
+
exports.RekordboxHydrator = RekordboxHydrator;
|
|
5827
|
+
_RekordboxHydrator_orm = new WeakMap(), _RekordboxHydrator_onProgress = new WeakMap();
|
|
5828
|
+
/**
|
|
5829
|
+
* Utility generator that pages through a table and yields every present row.
|
|
5830
|
+
* This flattens the concept of rowGroups and refs.
|
|
5831
|
+
*/
|
|
5832
|
+
function* tableRows(table) {
|
|
5833
|
+
const { firstPage, lastPage } = table;
|
|
5834
|
+
let pageRef = firstPage;
|
|
5835
|
+
do {
|
|
5836
|
+
const page = pageRef.body;
|
|
5837
|
+
// Adjust our page ref for the next iteration. We do this early in our loop
|
|
5838
|
+
// so we can break without having to remember to update for the next iter.
|
|
5839
|
+
pageRef = page.nextPage;
|
|
5840
|
+
// Ignore non-data pages. Not sure what these are for?
|
|
5841
|
+
if (!page.isDataPage) {
|
|
5842
|
+
continue;
|
|
6246
5843
|
}
|
|
6247
|
-
|
|
6248
|
-
|
|
5844
|
+
const rows = page.rowGroups
|
|
5845
|
+
.map((group) => group.rows)
|
|
5846
|
+
.flat()
|
|
5847
|
+
.filter((row) => row.present);
|
|
5848
|
+
for (const row of rows) {
|
|
5849
|
+
yield row.body;
|
|
5850
|
+
}
|
|
5851
|
+
} while (pageRef.index <= lastPage.index);
|
|
6249
5852
|
}
|
|
5853
|
+
|
|
5854
|
+
|
|
5855
|
+
/***/ },
|
|
5856
|
+
|
|
5857
|
+
/***/ "./src/localdb/rekordbox/index.ts"
|
|
5858
|
+
/*!****************************************!*\
|
|
5859
|
+
!*** ./src/localdb/rekordbox/index.ts ***!
|
|
5860
|
+
\****************************************/
|
|
5861
|
+
(__unused_webpack_module, exports, __webpack_require__) {
|
|
5862
|
+
|
|
5863
|
+
"use strict";
|
|
5864
|
+
|
|
5865
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5866
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5867
|
+
};
|
|
5868
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
5869
|
+
exports.hydrateDatabase = hydrateDatabase;
|
|
5870
|
+
exports.loadAnlz = loadAnlz;
|
|
5871
|
+
const kaitai_struct_1 = __webpack_require__(/*! kaitai-struct */ "kaitai-struct");
|
|
5872
|
+
const rekordbox_anlz_ksy_1 = __importDefault(__webpack_require__(/*! src/localdb/kaitai/rekordbox_anlz.ksy */ "./src/localdb/kaitai/rekordbox_anlz.ksy"));
|
|
5873
|
+
const anlz_parsers_1 = __webpack_require__(/*! ./anlz-parsers */ "./src/localdb/rekordbox/anlz-parsers.ts");
|
|
5874
|
+
const hydrator_1 = __webpack_require__(/*! ./hydrator */ "./src/localdb/rekordbox/hydrator.ts");
|
|
5875
|
+
const { SectionTags } = rekordbox_anlz_ksy_1.default;
|
|
6250
5876
|
/**
|
|
6251
|
-
*
|
|
5877
|
+
* Given a rekordbox pdb file contents. This function will hydrate the provided
|
|
5878
|
+
* database with all entities from the Rekordbox database. This includes all
|
|
5879
|
+
* track metadata, including analyzed metadata (such as beatgrids and waveforms).
|
|
6252
5880
|
*/
|
|
6253
|
-
function
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
1: 'high',
|
|
6257
|
-
2: 'mid',
|
|
6258
|
-
3: 'low',
|
|
6259
|
-
};
|
|
6260
|
-
const bankMap = {
|
|
6261
|
-
0: 'default',
|
|
6262
|
-
1: 'cool',
|
|
6263
|
-
2: 'natural',
|
|
6264
|
-
3: 'hot',
|
|
6265
|
-
4: 'subtle',
|
|
6266
|
-
5: 'warm',
|
|
6267
|
-
6: 'vivid',
|
|
6268
|
-
7: 'club_1',
|
|
6269
|
-
8: 'club_2',
|
|
6270
|
-
};
|
|
6271
|
-
// Phrase type mappings based on mood
|
|
6272
|
-
const phraseTypeMap = {
|
|
6273
|
-
high: {
|
|
6274
|
-
1: 'Intro',
|
|
6275
|
-
2: 'Up',
|
|
6276
|
-
3: 'Down',
|
|
6277
|
-
5: 'Chorus',
|
|
6278
|
-
6: 'Outro',
|
|
6279
|
-
},
|
|
6280
|
-
mid: {
|
|
6281
|
-
1: 'Intro',
|
|
6282
|
-
2: 'Verse 1',
|
|
6283
|
-
3: 'Verse 2',
|
|
6284
|
-
4: 'Verse 3',
|
|
6285
|
-
5: 'Verse 4',
|
|
6286
|
-
6: 'Verse 5',
|
|
6287
|
-
7: 'Verse 6',
|
|
6288
|
-
8: 'Bridge',
|
|
6289
|
-
9: 'Chorus',
|
|
6290
|
-
10: 'Outro',
|
|
6291
|
-
},
|
|
6292
|
-
low: {
|
|
6293
|
-
1: 'Intro',
|
|
6294
|
-
2: 'Verse 1',
|
|
6295
|
-
3: 'Verse 1',
|
|
6296
|
-
4: 'Verse 1',
|
|
6297
|
-
5: 'Verse 2',
|
|
6298
|
-
6: 'Verse 2',
|
|
6299
|
-
7: 'Verse 2',
|
|
6300
|
-
8: 'Bridge',
|
|
6301
|
-
9: 'Chorus',
|
|
6302
|
-
10: 'Outro',
|
|
6303
|
-
},
|
|
6304
|
-
};
|
|
6305
|
-
const mood = (_a = moodMap[data.body.mood]) !== null && _a !== void 0 ? _a : 'high';
|
|
6306
|
-
const bank = (_b = bankMap[data.body.rawBank]) !== null && _b !== void 0 ? _b : 'default';
|
|
6307
|
-
const phrases = data.body.entries.map((entry) => {
|
|
6308
|
-
var _a;
|
|
6309
|
-
const phrase = {
|
|
6310
|
-
index: entry.index,
|
|
6311
|
-
beat: entry.beat,
|
|
6312
|
-
kind: entry.kind,
|
|
6313
|
-
phraseType: (_a = phraseTypeMap[mood][entry.kind]) !== null && _a !== void 0 ? _a : 'Unknown',
|
|
6314
|
-
};
|
|
6315
|
-
// Add fill-in information if present
|
|
6316
|
-
if (entry.fill > 0) {
|
|
6317
|
-
phrase.fill = entry.fill;
|
|
6318
|
-
phrase.fillBeat = entry.beatFill;
|
|
6319
|
-
}
|
|
6320
|
-
return phrase;
|
|
6321
|
-
});
|
|
6322
|
-
return {
|
|
6323
|
-
mood,
|
|
6324
|
-
bank,
|
|
6325
|
-
endBeat: data.body.endBeat,
|
|
6326
|
-
phrases,
|
|
6327
|
-
};
|
|
5881
|
+
async function hydrateDatabase({ pdbData, span, ...options }) {
|
|
5882
|
+
const hydrator = new hydrator_1.RekordboxHydrator(options);
|
|
5883
|
+
await hydrator.hydrateFromPdb(pdbData, span);
|
|
6328
5884
|
}
|
|
6329
5885
|
/**
|
|
6330
|
-
*
|
|
5886
|
+
* Loads the ANLZ data of a Track entity from the analyzePath.
|
|
6331
5887
|
*/
|
|
6332
|
-
function
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
5888
|
+
async function loadAnlz(track, type, anlzResolver) {
|
|
5889
|
+
const path = `${track.analyzePath}.${type}`;
|
|
5890
|
+
const anlzData = await anlzResolver(path);
|
|
5891
|
+
const stream = new kaitai_struct_1.KaitaiStream(anlzData);
|
|
5892
|
+
const anlz = new rekordbox_anlz_ksy_1.default(stream);
|
|
5893
|
+
const result = {};
|
|
5894
|
+
const resultDat = result;
|
|
5895
|
+
const resultExt = result;
|
|
5896
|
+
const result2ex = result;
|
|
5897
|
+
for (const section of anlz.sections) {
|
|
5898
|
+
switch (section.fourcc) {
|
|
5899
|
+
case SectionTags.BEAT_GRID:
|
|
5900
|
+
resultDat.beatGrid = (0, anlz_parsers_1.makeBeatGrid)(section);
|
|
5901
|
+
break;
|
|
5902
|
+
case SectionTags.CUES:
|
|
5903
|
+
resultDat.cueAndLoops = (0, anlz_parsers_1.makeCueAndLoop)(section);
|
|
5904
|
+
break;
|
|
5905
|
+
case SectionTags.CUES_2:
|
|
5906
|
+
resultExt.extendedCues = (0, anlz_parsers_1.makeExtendedCues)(section);
|
|
5907
|
+
break;
|
|
5908
|
+
case SectionTags.WAVE_PREVIEW:
|
|
5909
|
+
resultDat.waveformPreview = (0, anlz_parsers_1.makeWaveformPreview)(section);
|
|
5910
|
+
break;
|
|
5911
|
+
case SectionTags.WAVE_TINY:
|
|
5912
|
+
resultDat.waveformTiny = (0, anlz_parsers_1.makeWaveformPreview)(section);
|
|
5913
|
+
break;
|
|
5914
|
+
case SectionTags.WAVE_SCROLL:
|
|
5915
|
+
resultExt.waveformDetail = Buffer.from(section.body.entries);
|
|
5916
|
+
break;
|
|
5917
|
+
case SectionTags.WAVE_COLOR_PREVIEW:
|
|
5918
|
+
resultExt.waveformColorPreview = Buffer.from(section.body.entries);
|
|
5919
|
+
break;
|
|
5920
|
+
case SectionTags.WAVE_COLOR_SCROLL:
|
|
5921
|
+
resultExt.waveformHd = (0, anlz_parsers_1.makeWaveformHd)(section);
|
|
5922
|
+
break;
|
|
5923
|
+
case SectionTags.SONG_STRUCTURE:
|
|
5924
|
+
resultExt.songStructure = (0, anlz_parsers_1.makeSongStructure)(section);
|
|
5925
|
+
break;
|
|
5926
|
+
case SectionTags.WAVE_COLOR_3BAND_PREVIEW:
|
|
5927
|
+
result2ex.waveform3BandPreview = (0, anlz_parsers_1.makeWaveform3BandPreview)(section);
|
|
5928
|
+
break;
|
|
5929
|
+
case SectionTags.WAVE_COLOR_3BAND_DETAIL:
|
|
5930
|
+
result2ex.waveform3BandDetail = (0, anlz_parsers_1.makeWaveform3BandDetail)(section);
|
|
5931
|
+
break;
|
|
5932
|
+
case SectionTags.VOCAL_CONFIG:
|
|
5933
|
+
result2ex.vocalConfig = (0, anlz_parsers_1.makeVocalConfig)(section);
|
|
5934
|
+
break;
|
|
5935
|
+
// VBR and PATH tags are defined but not currently extracted
|
|
5936
|
+
// as they're not commonly needed in the application
|
|
5937
|
+
case SectionTags.VBR:
|
|
5938
|
+
case SectionTags.PATH:
|
|
5939
|
+
break;
|
|
5940
|
+
}
|
|
5941
|
+
}
|
|
5942
|
+
return result;
|
|
6336
5943
|
}
|
|
5944
|
+
|
|
5945
|
+
|
|
5946
|
+
/***/ },
|
|
5947
|
+
|
|
5948
|
+
/***/ "./src/localdb/rekordbox/table-mappings.ts"
|
|
5949
|
+
/*!*************************************************!*\
|
|
5950
|
+
!*** ./src/localdb/rekordbox/table-mappings.ts ***!
|
|
5951
|
+
\*************************************************/
|
|
5952
|
+
(__unused_webpack_module, exports, __webpack_require__) {
|
|
5953
|
+
|
|
5954
|
+
"use strict";
|
|
5955
|
+
|
|
5956
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5957
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5958
|
+
};
|
|
5959
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
5960
|
+
exports.pdbEntityCreators = exports.pdbTables = void 0;
|
|
5961
|
+
const rekordbox_pdb_ksy_1 = __importDefault(__webpack_require__(/*! src/localdb/kaitai/rekordbox_pdb.ksy */ "./src/localdb/kaitai/rekordbox_pdb.ksy"));
|
|
5962
|
+
const orm_1 = __webpack_require__(/*! src/localdb/orm */ "./src/localdb/orm.ts");
|
|
5963
|
+
const entity_creators_1 = __webpack_require__(/*! ./entity-creators */ "./src/localdb/rekordbox/entity-creators.ts");
|
|
6337
5964
|
const { PageType } = rekordbox_pdb_ksy_1.default;
|
|
6338
|
-
const { SectionTags } = rekordbox_anlz_ksy_1.default;
|
|
6339
5965
|
/**
|
|
6340
5966
|
* Maps rekordbox pdb table types to orm table names.
|
|
6341
5967
|
*/
|
|
6342
|
-
|
|
5968
|
+
exports.pdbTables = {
|
|
6343
5969
|
[PageType.TRACKS]: orm_1.Table.Track,
|
|
6344
5970
|
[PageType.ARTISTS]: orm_1.Table.Artist,
|
|
6345
5971
|
[PageType.GENRES]: orm_1.Table.Genre,
|
|
@@ -6355,17 +5981,17 @@ const pdbTables = {
|
|
|
6355
5981
|
* Maps rekordbox pdb table types to functions that create entity objects for
|
|
6356
5982
|
* the passed pdb row.
|
|
6357
5983
|
*/
|
|
6358
|
-
|
|
6359
|
-
[PageType.TRACKS]: createTrack,
|
|
6360
|
-
[PageType.ARTISTS]: makeIdNameHydrator(),
|
|
6361
|
-
[PageType.GENRES]: makeIdNameHydrator(),
|
|
6362
|
-
[PageType.ALBUMS]: makeIdNameHydrator(),
|
|
6363
|
-
[PageType.LABELS]: makeIdNameHydrator(),
|
|
6364
|
-
[PageType.COLORS]: makeIdNameHydrator(),
|
|
6365
|
-
[PageType.KEYS]: makeIdNameHydrator(),
|
|
6366
|
-
[PageType.ARTWORK]: createArtworkEntry,
|
|
6367
|
-
[PageType.PLAYLIST_TREE]: createPlaylist,
|
|
6368
|
-
[PageType.PLAYLIST_ENTRIES]: createPlaylistEntry,
|
|
5984
|
+
exports.pdbEntityCreators = {
|
|
5985
|
+
[PageType.TRACKS]: entity_creators_1.createTrack,
|
|
5986
|
+
[PageType.ARTISTS]: (0, entity_creators_1.makeIdNameHydrator)(),
|
|
5987
|
+
[PageType.GENRES]: (0, entity_creators_1.makeIdNameHydrator)(),
|
|
5988
|
+
[PageType.ALBUMS]: (0, entity_creators_1.makeIdNameHydrator)(),
|
|
5989
|
+
[PageType.LABELS]: (0, entity_creators_1.makeIdNameHydrator)(),
|
|
5990
|
+
[PageType.COLORS]: (0, entity_creators_1.makeIdNameHydrator)(),
|
|
5991
|
+
[PageType.KEYS]: (0, entity_creators_1.makeIdNameHydrator)(),
|
|
5992
|
+
[PageType.ARTWORK]: entity_creators_1.createArtworkEntry,
|
|
5993
|
+
[PageType.PLAYLIST_TREE]: entity_creators_1.createPlaylist,
|
|
5994
|
+
[PageType.PLAYLIST_ENTRIES]: entity_creators_1.createPlaylistEntry,
|
|
6369
5995
|
// TODO: Register PageType.HISTORY
|
|
6370
5996
|
};
|
|
6371
5997
|
|
|
@@ -6506,6 +6132,27 @@ const makeCueLoopEntry = (isCue, isLoop, offset, length, button) => button !== f
|
|
|
6506
6132
|
exports.makeCueLoopEntry = makeCueLoopEntry;
|
|
6507
6133
|
|
|
6508
6134
|
|
|
6135
|
+
/***/ },
|
|
6136
|
+
|
|
6137
|
+
/***/ "./src/logger.ts"
|
|
6138
|
+
/*!***********************!*\
|
|
6139
|
+
!*** ./src/logger.ts ***!
|
|
6140
|
+
\***********************/
|
|
6141
|
+
(__unused_webpack_module, exports) {
|
|
6142
|
+
|
|
6143
|
+
"use strict";
|
|
6144
|
+
|
|
6145
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
6146
|
+
exports.noopLogger = void 0;
|
|
6147
|
+
exports.noopLogger = {
|
|
6148
|
+
trace() { },
|
|
6149
|
+
debug() { },
|
|
6150
|
+
info() { },
|
|
6151
|
+
warn() { },
|
|
6152
|
+
error() { },
|
|
6153
|
+
};
|
|
6154
|
+
|
|
6155
|
+
|
|
6509
6156
|
/***/ },
|
|
6510
6157
|
|
|
6511
6158
|
/***/ "./src/mixstatus/index.ts"
|
|
@@ -6527,7 +6174,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
6527
6174
|
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");
|
|
6528
6175
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6529
6176
|
};
|
|
6530
|
-
var _MixstatusProcessor_emitter, _MixstatusProcessor_lastState, _MixstatusProcessor_lastStartTime, _MixstatusProcessor_lastStoppedTimes, _MixstatusProcessor_livePlayers, _MixstatusProcessor_isSetActive, _MixstatusProcessor_cancelSetEnding, _MixstatusProcessor_config, _MixstatusProcessor_onAir, _MixstatusProcessor_promotePlayer, _MixstatusProcessor_promoteNextPlayer, _MixstatusProcessor_markPlayerStopped, _MixstatusProcessor_setMayStop, _MixstatusProcessor_playerMayBeFirst, _MixstatusProcessor_playerMayStop, _MixstatusProcessor_handlePlaystateChange, _MixstatusProcessor_handleOnairChange;
|
|
6177
|
+
var _MixstatusProcessor_emitter, _MixstatusProcessor_lastState, _MixstatusProcessor_lastStartTime, _MixstatusProcessor_lastStoppedTimes, _MixstatusProcessor_livePlayers, _MixstatusProcessor_lastEmittedTrackId, _MixstatusProcessor_isSetActive, _MixstatusProcessor_cancelSetEnding, _MixstatusProcessor_config, _MixstatusProcessor_onAir, _MixstatusProcessor_promotePlayer, _MixstatusProcessor_promoteNextPlayer, _MixstatusProcessor_markPlayerStopped, _MixstatusProcessor_setMayStop, _MixstatusProcessor_playerMayBeFirst, _MixstatusProcessor_playerMayStop, _MixstatusProcessor_handlePlaystateChange, _MixstatusProcessor_handleOnairChange;
|
|
6531
6178
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
6532
6179
|
exports.MixstatusProcessor = void 0;
|
|
6533
6180
|
const events_1 = __webpack_require__(/*! events */ "events");
|
|
@@ -6583,6 +6230,14 @@ class MixstatusProcessor {
|
|
|
6583
6230
|
* Records which players have been reported as 'live'
|
|
6584
6231
|
*/
|
|
6585
6232
|
_MixstatusProcessor_livePlayers.set(this, new Set());
|
|
6233
|
+
/**
|
|
6234
|
+
* Records the last trackId emitted as 'nowPlaying' for each device. Used to
|
|
6235
|
+
* suppress duplicate emissions when a deck is briefly cued/loaded and then
|
|
6236
|
+
* resumed on the same track (a common pattern for House/Techno DJs who
|
|
6237
|
+
* cue-juggle mid-track). The entry is replaced when a different trackId is
|
|
6238
|
+
* emitted on the same deck, so subsequent track loads still emit normally.
|
|
6239
|
+
*/
|
|
6240
|
+
_MixstatusProcessor_lastEmittedTrackId.set(this, new Map());
|
|
6586
6241
|
/**
|
|
6587
6242
|
* Incidates if we're currentiny in an active DJ set
|
|
6588
6243
|
*/
|
|
@@ -6617,6 +6272,14 @@ class MixstatusProcessor {
|
|
|
6617
6272
|
if (__classPrivateFieldGet(this, _MixstatusProcessor_livePlayers, "f").has(deviceId)) {
|
|
6618
6273
|
return;
|
|
6619
6274
|
}
|
|
6275
|
+
// Suppress duplicate nowPlaying for the same track on the same deck. This
|
|
6276
|
+
// can happen when a DJ cues mid-track and resumes (Cued/Loading/Ended are
|
|
6277
|
+
// all stoppingStates that drop the deck from livePlayers, so the next play
|
|
6278
|
+
// of the same trackId would otherwise re-emit). When a different track is
|
|
6279
|
+
// loaded, the trackId differs and the new emission proceeds normally.
|
|
6280
|
+
if (__classPrivateFieldGet(this, _MixstatusProcessor_lastEmittedTrackId, "f").get(deviceId) === state.trackId) {
|
|
6281
|
+
return;
|
|
6282
|
+
}
|
|
6620
6283
|
if (!__classPrivateFieldGet(this, _MixstatusProcessor_isSetActive, "f")) {
|
|
6621
6284
|
__classPrivateFieldSet(this, _MixstatusProcessor_isSetActive, true, "f");
|
|
6622
6285
|
__classPrivateFieldGet(this, _MixstatusProcessor_emitter, "f").emit('setStarted');
|
|
@@ -6625,6 +6288,7 @@ class MixstatusProcessor {
|
|
|
6625
6288
|
__classPrivateFieldGet(this, _MixstatusProcessor_cancelSetEnding, "f").call(this);
|
|
6626
6289
|
}
|
|
6627
6290
|
__classPrivateFieldGet(this, _MixstatusProcessor_livePlayers, "f").add(deviceId);
|
|
6291
|
+
__classPrivateFieldGet(this, _MixstatusProcessor_lastEmittedTrackId, "f").set(deviceId, state.trackId);
|
|
6628
6292
|
__classPrivateFieldGet(this, _MixstatusProcessor_emitter, "f").emit('nowPlaying', state);
|
|
6629
6293
|
});
|
|
6630
6294
|
/**
|
|
@@ -6818,7 +6482,7 @@ class MixstatusProcessor {
|
|
|
6818
6482
|
}
|
|
6819
6483
|
}
|
|
6820
6484
|
exports.MixstatusProcessor = MixstatusProcessor;
|
|
6821
|
-
_MixstatusProcessor_emitter = new WeakMap(), _MixstatusProcessor_lastState = new WeakMap(), _MixstatusProcessor_lastStartTime = new WeakMap(), _MixstatusProcessor_lastStoppedTimes = new WeakMap(), _MixstatusProcessor_livePlayers = new WeakMap(), _MixstatusProcessor_isSetActive = new WeakMap(), _MixstatusProcessor_cancelSetEnding = new WeakMap(), _MixstatusProcessor_config = new WeakMap(), _MixstatusProcessor_onAir = new WeakMap(), _MixstatusProcessor_promotePlayer = new WeakMap(), _MixstatusProcessor_promoteNextPlayer = new WeakMap(), _MixstatusProcessor_markPlayerStopped = new WeakMap(), _MixstatusProcessor_setMayStop = new WeakMap(), _MixstatusProcessor_playerMayBeFirst = new WeakMap(), _MixstatusProcessor_playerMayStop = new WeakMap(), _MixstatusProcessor_handlePlaystateChange = new WeakMap(), _MixstatusProcessor_handleOnairChange = new WeakMap();
|
|
6485
|
+
_MixstatusProcessor_emitter = new WeakMap(), _MixstatusProcessor_lastState = new WeakMap(), _MixstatusProcessor_lastStartTime = new WeakMap(), _MixstatusProcessor_lastStoppedTimes = new WeakMap(), _MixstatusProcessor_livePlayers = new WeakMap(), _MixstatusProcessor_lastEmittedTrackId = new WeakMap(), _MixstatusProcessor_isSetActive = new WeakMap(), _MixstatusProcessor_cancelSetEnding = new WeakMap(), _MixstatusProcessor_config = new WeakMap(), _MixstatusProcessor_onAir = new WeakMap(), _MixstatusProcessor_promotePlayer = new WeakMap(), _MixstatusProcessor_promoteNextPlayer = new WeakMap(), _MixstatusProcessor_markPlayerStopped = new WeakMap(), _MixstatusProcessor_setMayStop = new WeakMap(), _MixstatusProcessor_playerMayBeFirst = new WeakMap(), _MixstatusProcessor_playerMayStop = new WeakMap(), _MixstatusProcessor_handlePlaystateChange = new WeakMap(), _MixstatusProcessor_handleOnairChange = new WeakMap();
|
|
6822
6486
|
|
|
6823
6487
|
|
|
6824
6488
|
/***/ },
|
|
@@ -6909,7 +6573,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
6909
6573
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6910
6574
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
6911
6575
|
};
|
|
6912
|
-
var _ProlinkNetwork_state, _ProlinkNetwork_announceSocket, _ProlinkNetwork_beatSocket, _ProlinkNetwork_statusSocket, _ProlinkNetwork_deviceManager, _ProlinkNetwork_statusEmitter, _ProlinkNetwork_positionEmitter, _ProlinkNetwork_config, _ProlinkNetwork_connection, _ProlinkNetwork_mixstatus;
|
|
6576
|
+
var _ProlinkNetwork_state, _ProlinkNetwork_announceSocket, _ProlinkNetwork_beatSocket, _ProlinkNetwork_statusSocket, _ProlinkNetwork_deviceManager, _ProlinkNetwork_statusEmitter, _ProlinkNetwork_positionEmitter, _ProlinkNetwork_logger, _ProlinkNetwork_config, _ProlinkNetwork_connection, _ProlinkNetwork_mixstatus;
|
|
6913
6577
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
6914
6578
|
exports.ProlinkNetwork = void 0;
|
|
6915
6579
|
exports.bringOnline = bringOnline;
|
|
@@ -6919,6 +6583,7 @@ const constants_1 = __webpack_require__(/*! src/constants */ "./src/constants.ts
|
|
|
6919
6583
|
const control_1 = __importDefault(__webpack_require__(/*! src/control */ "./src/control/index.ts"));
|
|
6920
6584
|
const db_1 = __importDefault(__webpack_require__(/*! src/db */ "./src/db/index.ts"));
|
|
6921
6585
|
const devices_1 = __importDefault(__webpack_require__(/*! src/devices */ "./src/devices/index.ts"));
|
|
6586
|
+
const logger_1 = __webpack_require__(/*! src/logger */ "./src/logger.ts");
|
|
6922
6587
|
const localdb_1 = __importDefault(__webpack_require__(/*! src/localdb */ "./src/localdb/index.ts"));
|
|
6923
6588
|
const mixstatus_1 = __webpack_require__(/*! src/mixstatus */ "./src/mixstatus/index.ts");
|
|
6924
6589
|
const remotedb_1 = __importDefault(__webpack_require__(/*! src/remotedb */ "./src/remotedb/index.ts"));
|
|
@@ -6949,6 +6614,9 @@ async function bringOnline(config) {
|
|
|
6949
6614
|
await (0, udp_1.udpBind)(announceSocket, constants_1.ANNOUNCE_PORT, '0.0.0.0');
|
|
6950
6615
|
await (0, udp_1.udpBind)(beatSocket, constants_1.BEAT_PORT, '0.0.0.0');
|
|
6951
6616
|
await (0, udp_1.udpBind)(statusSocket, constants_1.STATUS_PORT, '0.0.0.0');
|
|
6617
|
+
// Enable broadcast so the announcer can reach the whole subnet before any
|
|
6618
|
+
// devices have been discovered.
|
|
6619
|
+
announceSocket.setBroadcast(true);
|
|
6952
6620
|
}
|
|
6953
6621
|
catch (err) {
|
|
6954
6622
|
Telemetry.captureException(err);
|
|
@@ -6976,6 +6644,7 @@ class ProlinkNetwork {
|
|
|
6976
6644
|
* @internal
|
|
6977
6645
|
*/
|
|
6978
6646
|
constructor({ config, announceSocket, beatSocket, statusSocket, deviceManager, statusEmitter, positionEmitter, }) {
|
|
6647
|
+
var _a;
|
|
6979
6648
|
_ProlinkNetwork_state.set(this, types_1.NetworkState.Online);
|
|
6980
6649
|
_ProlinkNetwork_announceSocket.set(this, void 0);
|
|
6981
6650
|
_ProlinkNetwork_beatSocket.set(this, void 0);
|
|
@@ -6983,10 +6652,12 @@ class ProlinkNetwork {
|
|
|
6983
6652
|
_ProlinkNetwork_deviceManager.set(this, void 0);
|
|
6984
6653
|
_ProlinkNetwork_statusEmitter.set(this, void 0);
|
|
6985
6654
|
_ProlinkNetwork_positionEmitter.set(this, void 0);
|
|
6655
|
+
_ProlinkNetwork_logger.set(this, void 0);
|
|
6986
6656
|
_ProlinkNetwork_config.set(this, void 0);
|
|
6987
6657
|
_ProlinkNetwork_connection.set(this, void 0);
|
|
6988
6658
|
_ProlinkNetwork_mixstatus.set(this, void 0);
|
|
6989
6659
|
__classPrivateFieldSet(this, _ProlinkNetwork_config, config !== null && config !== void 0 ? config : null, "f");
|
|
6660
|
+
__classPrivateFieldSet(this, _ProlinkNetwork_logger, (_a = config === null || config === void 0 ? void 0 : config.logger) !== null && _a !== void 0 ? _a : logger_1.noopLogger, "f");
|
|
6990
6661
|
__classPrivateFieldSet(this, _ProlinkNetwork_announceSocket, announceSocket, "f");
|
|
6991
6662
|
__classPrivateFieldSet(this, _ProlinkNetwork_beatSocket, beatSocket, "f");
|
|
6992
6663
|
__classPrivateFieldSet(this, _ProlinkNetwork_statusSocket, statusSocket, "f");
|
|
@@ -7006,6 +6677,9 @@ class ProlinkNetwork {
|
|
|
7006
6677
|
*/
|
|
7007
6678
|
configure(config) {
|
|
7008
6679
|
__classPrivateFieldSet(this, _ProlinkNetwork_config, { ...__classPrivateFieldGet(this, _ProlinkNetwork_config, "f"), ...config }, "f");
|
|
6680
|
+
if (config.logger !== undefined) {
|
|
6681
|
+
__classPrivateFieldSet(this, _ProlinkNetwork_logger, config.logger, "f");
|
|
6682
|
+
}
|
|
7009
6683
|
}
|
|
7010
6684
|
/**
|
|
7011
6685
|
* Wait for another device to show up on the network to determine which network
|
|
@@ -7039,7 +6713,7 @@ class ProlinkNetwork {
|
|
|
7039
6713
|
* or manual configuration). This will then initialize all the network services.
|
|
7040
6714
|
*/
|
|
7041
6715
|
connect() {
|
|
7042
|
-
var _a, _b;
|
|
6716
|
+
var _a, _b, _c;
|
|
7043
6717
|
if (__classPrivateFieldGet(this, _ProlinkNetwork_config, "f") === null) {
|
|
7044
6718
|
throw new Error(connectErrorHelp);
|
|
7045
6719
|
}
|
|
@@ -7051,13 +6725,13 @@ class ProlinkNetwork {
|
|
|
7051
6725
|
__classPrivateFieldGet(this, _ProlinkNetwork_deviceManager, "f").reconfigure({ vcdjName: __classPrivateFieldGet(this, _ProlinkNetwork_config, "f").vcdjName });
|
|
7052
6726
|
}
|
|
7053
6727
|
// Start announcing
|
|
7054
|
-
const announcer = new virtualcdj_1.Announcer(vcdj, __classPrivateFieldGet(this, _ProlinkNetwork_announceSocket, "f"), this.deviceManager, __classPrivateFieldGet(this, _ProlinkNetwork_config, "f").iface, (_a = __classPrivateFieldGet(this, _ProlinkNetwork_config, "f").fullStartup) !== null && _a !== void 0 ? _a : false);
|
|
6728
|
+
const announcer = new virtualcdj_1.Announcer(vcdj, __classPrivateFieldGet(this, _ProlinkNetwork_announceSocket, "f"), this.deviceManager, __classPrivateFieldGet(this, _ProlinkNetwork_config, "f").iface, (_a = __classPrivateFieldGet(this, _ProlinkNetwork_config, "f").fullStartup) !== null && _a !== void 0 ? _a : false, (_b = __classPrivateFieldGet(this, _ProlinkNetwork_config, "f").announceToStagehand) !== null && _b !== void 0 ? _b : false, __classPrivateFieldGet(this, _ProlinkNetwork_logger, "f"));
|
|
7055
6729
|
announcer.start();
|
|
7056
6730
|
// Create remote and local databases
|
|
7057
6731
|
const remotedb = new remotedb_1.default(__classPrivateFieldGet(this, _ProlinkNetwork_deviceManager, "f"), vcdj);
|
|
7058
|
-
const localdb = new localdb_1.default(vcdj, __classPrivateFieldGet(this, _ProlinkNetwork_deviceManager, "f"), __classPrivateFieldGet(this, _ProlinkNetwork_statusEmitter, "f"), (
|
|
6732
|
+
const localdb = new localdb_1.default(vcdj, __classPrivateFieldGet(this, _ProlinkNetwork_deviceManager, "f"), __classPrivateFieldGet(this, _ProlinkNetwork_statusEmitter, "f"), (_c = __classPrivateFieldGet(this, _ProlinkNetwork_config, "f").databasePreference) !== null && _c !== void 0 ? _c : 'auto');
|
|
7059
6733
|
// Create unified database
|
|
7060
|
-
const database = new db_1.default(
|
|
6734
|
+
const database = new db_1.default(localdb, remotedb, __classPrivateFieldGet(this, _ProlinkNetwork_deviceManager, "f"));
|
|
7061
6735
|
// Create controller service
|
|
7062
6736
|
const control = new control_1.default(__classPrivateFieldGet(this, _ProlinkNetwork_beatSocket, "f"), vcdj);
|
|
7063
6737
|
__classPrivateFieldSet(this, _ProlinkNetwork_state, types_1.NetworkState.Connected, "f");
|
|
@@ -7187,6 +6861,14 @@ class ProlinkNetwork {
|
|
|
7187
6861
|
var _a, _b;
|
|
7188
6862
|
return (_b = (_a = __classPrivateFieldGet(this, _ProlinkNetwork_connection, "f")) === null || _a === void 0 ? void 0 : _a.remotedb) !== null && _b !== void 0 ? _b : null;
|
|
7189
6863
|
}
|
|
6864
|
+
/**
|
|
6865
|
+
* Returns a promise that resolves when the full startup protocol completes.
|
|
6866
|
+
* Resolves immediately if fullStartup is disabled or not connected.
|
|
6867
|
+
*/
|
|
6868
|
+
get startupReady() {
|
|
6869
|
+
var _a, _b;
|
|
6870
|
+
return (_b = (_a = __classPrivateFieldGet(this, _ProlinkNetwork_connection, "f")) === null || _a === void 0 ? void 0 : _a.announcer.ready) !== null && _b !== void 0 ? _b : Promise.resolve();
|
|
6871
|
+
}
|
|
7190
6872
|
/**
|
|
7191
6873
|
* Get (and initialize) the {@link MixstatusProcessor} service. This service can
|
|
7192
6874
|
* be used to monitor the 'status' of devices on the network as a whole.
|
|
@@ -7205,7 +6887,7 @@ class ProlinkNetwork {
|
|
|
7205
6887
|
}
|
|
7206
6888
|
}
|
|
7207
6889
|
exports.ProlinkNetwork = ProlinkNetwork;
|
|
7208
|
-
_ProlinkNetwork_state = new WeakMap(), _ProlinkNetwork_announceSocket = new WeakMap(), _ProlinkNetwork_beatSocket = new WeakMap(), _ProlinkNetwork_statusSocket = new WeakMap(), _ProlinkNetwork_deviceManager = new WeakMap(), _ProlinkNetwork_statusEmitter = new WeakMap(), _ProlinkNetwork_positionEmitter = new WeakMap(), _ProlinkNetwork_config = new WeakMap(), _ProlinkNetwork_connection = new WeakMap(), _ProlinkNetwork_mixstatus = new WeakMap();
|
|
6890
|
+
_ProlinkNetwork_state = new WeakMap(), _ProlinkNetwork_announceSocket = new WeakMap(), _ProlinkNetwork_beatSocket = new WeakMap(), _ProlinkNetwork_statusSocket = new WeakMap(), _ProlinkNetwork_deviceManager = new WeakMap(), _ProlinkNetwork_statusEmitter = new WeakMap(), _ProlinkNetwork_positionEmitter = new WeakMap(), _ProlinkNetwork_logger = new WeakMap(), _ProlinkNetwork_config = new WeakMap(), _ProlinkNetwork_connection = new WeakMap(), _ProlinkNetwork_mixstatus = new WeakMap();
|
|
7209
6891
|
|
|
7210
6892
|
|
|
7211
6893
|
/***/ },
|
|
@@ -7252,6 +6934,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
7252
6934
|
};
|
|
7253
6935
|
})();
|
|
7254
6936
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
6937
|
+
exports.parseWindowsPath = parseWindowsPath;
|
|
6938
|
+
exports.resolveNfsPath = resolveNfsPath;
|
|
6939
|
+
exports.getPortmapPort = getPortmapPort;
|
|
7255
6940
|
exports.fetchFileRange = fetchFileRange;
|
|
7256
6941
|
exports.getFileInfo = getFileInfo;
|
|
7257
6942
|
exports.fetchFile = fetchFile;
|
|
@@ -7269,8 +6954,54 @@ const xdr_1 = __webpack_require__(/*! ./xdr */ "./src/nfs/xdr.ts");
|
|
|
7269
6954
|
const slotMountMapping = {
|
|
7270
6955
|
[types_1.MediaSlot.USB]: '/C/',
|
|
7271
6956
|
[types_1.MediaSlot.SD]: '/B/',
|
|
7272
|
-
[types_1.MediaSlot.RB]: '/',
|
|
7273
6957
|
};
|
|
6958
|
+
/**
|
|
6959
|
+
* Parse a Windows absolute path (e.g. `C:\Users\chris\Music\track.mp3`)
|
|
6960
|
+
* into the NFS mount path and relative file path.
|
|
6961
|
+
*
|
|
6962
|
+
* @internal Exported for testing
|
|
6963
|
+
*/
|
|
6964
|
+
function parseWindowsPath(filePath) {
|
|
6965
|
+
const match = filePath.match(/^([A-Za-z]):[/\\](.*)$/);
|
|
6966
|
+
if (!match) {
|
|
6967
|
+
return null;
|
|
6968
|
+
}
|
|
6969
|
+
return {
|
|
6970
|
+
mountPath: `/${match[1].toUpperCase()}/`,
|
|
6971
|
+
nfsPath: match[2].replace(/\\/g, '/'),
|
|
6972
|
+
};
|
|
6973
|
+
}
|
|
6974
|
+
/**
|
|
6975
|
+
* Resolve the NFS mount path and file path for a given slot.
|
|
6976
|
+
*
|
|
6977
|
+
* For USB/SD slots, the mount path is well-known (/C/ and /B/).
|
|
6978
|
+
* For the RB slot, the mount path is extracted from the file path returned
|
|
6979
|
+
* by remotedb:
|
|
6980
|
+
* - Windows: `C:\Users\chris\Music\track.mp3` → mount `/C/`, path `Users/chris/Music/track.mp3`
|
|
6981
|
+
* - macOS: `/Users/chris/Music/track.mp3` → mount `/`, path `Users/chris/Music/track.mp3`
|
|
6982
|
+
*
|
|
6983
|
+
* @internal Exported for testing
|
|
6984
|
+
*/
|
|
6985
|
+
function resolveNfsPath(slot, filePath) {
|
|
6986
|
+
if (slot === types_1.MediaSlot.RB) {
|
|
6987
|
+
// Windows: C:\Users\chris\Music\track.mp3 → mount /C/, path Users/...
|
|
6988
|
+
const parsed = parseWindowsPath(filePath);
|
|
6989
|
+
if (parsed) {
|
|
6990
|
+
return parsed;
|
|
6991
|
+
}
|
|
6992
|
+
// macOS: /Users/chris/Music/track.mp3 → mount /, path Users/...
|
|
6993
|
+
if (filePath.startsWith('/')) {
|
|
6994
|
+
return {
|
|
6995
|
+
mountPath: '/',
|
|
6996
|
+
nfsPath: filePath.slice(1),
|
|
6997
|
+
};
|
|
6998
|
+
}
|
|
6999
|
+
}
|
|
7000
|
+
return {
|
|
7001
|
+
mountPath: slotMountMapping[slot],
|
|
7002
|
+
nfsPath: filePath,
|
|
7003
|
+
};
|
|
7004
|
+
}
|
|
7274
7005
|
/**
|
|
7275
7006
|
* The module-level retry configuration for newly created RpcConnections.
|
|
7276
7007
|
*/
|
|
@@ -7281,6 +7012,18 @@ let retryConfig = {};
|
|
|
7281
7012
|
* still be connected.
|
|
7282
7013
|
*/
|
|
7283
7014
|
const clientsCache = new Map();
|
|
7015
|
+
/**
|
|
7016
|
+
* Get the portmapper port for the given device. Rekordbox software uses a
|
|
7017
|
+
* non-standard port (50111) while CDJs and other hardware use the standard
|
|
7018
|
+
* port (111).
|
|
7019
|
+
*
|
|
7020
|
+
* @internal Exported for testing
|
|
7021
|
+
*/
|
|
7022
|
+
function getPortmapPort(device) {
|
|
7023
|
+
return device.type === types_1.DeviceType.Rekordbox
|
|
7024
|
+
? programs_1.REKORDBOX_PORTMAP_PORT
|
|
7025
|
+
: programs_1.STANDARD_PORTMAP_PORT;
|
|
7026
|
+
}
|
|
7284
7027
|
/**
|
|
7285
7028
|
* Given a device address running a nfs and mountd RPC server, provide
|
|
7286
7029
|
* RpcProgram clients that may be used to call these services.
|
|
@@ -7288,7 +7031,8 @@ const clientsCache = new Map();
|
|
|
7288
7031
|
* NOTE: This function will cache the clients for the address, recreating the
|
|
7289
7032
|
* connections if the cached clients have disconnected.
|
|
7290
7033
|
*/
|
|
7291
|
-
async function getClients(
|
|
7034
|
+
async function getClients(device) {
|
|
7035
|
+
const { address } = device.ip;
|
|
7292
7036
|
const cachedSet = clientsCache.get(address);
|
|
7293
7037
|
if (cachedSet !== undefined && cachedSet.conn.connected) {
|
|
7294
7038
|
return cachedSet;
|
|
@@ -7298,49 +7042,50 @@ async function getClients(address) {
|
|
|
7298
7042
|
clientsCache.delete(address);
|
|
7299
7043
|
}
|
|
7300
7044
|
const conn = new rpc_1.RpcConnection(address, retryConfig);
|
|
7045
|
+
const portmapPort = getPortmapPort(device);
|
|
7301
7046
|
const mountClient = await (0, programs_1.makeProgramClient)(conn, {
|
|
7302
7047
|
id: xdr_1.mount.Program,
|
|
7303
7048
|
version: xdr_1.mount.Version,
|
|
7304
|
-
});
|
|
7049
|
+
}, portmapPort);
|
|
7305
7050
|
const nfsClient = await (0, programs_1.makeProgramClient)(conn, {
|
|
7306
7051
|
id: xdr_1.nfs.Program,
|
|
7307
7052
|
version: xdr_1.nfs.Version,
|
|
7308
|
-
});
|
|
7053
|
+
}, portmapPort);
|
|
7309
7054
|
const set = { conn, mountClient, nfsClient };
|
|
7310
7055
|
clientsCache.set(address, set);
|
|
7311
7056
|
return set;
|
|
7312
7057
|
}
|
|
7313
7058
|
/**
|
|
7314
|
-
* This module maintains a singleton cached list of (device address +
|
|
7059
|
+
* This module maintains a singleton cached list of (device address + mount path) -> file
|
|
7315
7060
|
* handles. The file handles may become stale in this list should the devices
|
|
7316
7061
|
* connected to the players slot change.
|
|
7317
7062
|
*/
|
|
7318
7063
|
const rootHandleCache = new Map();
|
|
7319
7064
|
/**
|
|
7320
|
-
* Locate the root filehandle of the given device
|
|
7065
|
+
* Locate the root filehandle of the given device mount path.
|
|
7321
7066
|
*
|
|
7322
|
-
* NOTE: This function will cache the root handle for the device +
|
|
7067
|
+
* NOTE: This function will cache the root handle for the device + mount path. Should
|
|
7323
7068
|
* the device have changed the slot will not longer be valid (TODO,
|
|
7324
7069
|
* verify this). It is up to the caller to clear the cache and get the
|
|
7325
7070
|
* new root handle in that case.
|
|
7326
7071
|
*/
|
|
7327
|
-
async function getRootHandle({ device,
|
|
7072
|
+
async function getRootHandle({ device, mountPath, mountClient, span, }) {
|
|
7328
7073
|
var _a;
|
|
7329
7074
|
const tx = span === null || span === void 0 ? void 0 : span.startChild({ op: 'getRootHandle' });
|
|
7330
7075
|
const { address } = device.ip;
|
|
7331
|
-
const
|
|
7332
|
-
const cachedRootHandle =
|
|
7076
|
+
const deviceMountCache = (_a = rootHandleCache.get(address)) !== null && _a !== void 0 ? _a : new Map();
|
|
7077
|
+
const cachedRootHandle = deviceMountCache.get(mountPath);
|
|
7333
7078
|
if (cachedRootHandle !== undefined) {
|
|
7334
7079
|
return cachedRootHandle;
|
|
7335
7080
|
}
|
|
7336
7081
|
const exports = await (0, programs_1.getExports)(mountClient, tx);
|
|
7337
|
-
const targetExport = exports.find(e => e.filesystem ===
|
|
7082
|
+
const targetExport = exports.find(e => e.filesystem === mountPath);
|
|
7338
7083
|
if (targetExport === undefined) {
|
|
7339
7084
|
return null;
|
|
7340
7085
|
}
|
|
7341
7086
|
const rootHandle = await (0, programs_1.mountFilesystem)(mountClient, targetExport, tx);
|
|
7342
|
-
|
|
7343
|
-
rootHandleCache.set(address,
|
|
7087
|
+
deviceMountCache.set(mountPath, rootHandle);
|
|
7088
|
+
rootHandleCache.set(address, deviceMountCache);
|
|
7344
7089
|
tx === null || tx === void 0 ? void 0 : tx.finish();
|
|
7345
7090
|
return rootHandle;
|
|
7346
7091
|
}
|
|
@@ -7353,22 +7098,23 @@ async function fetchFileRange({ device, slot, path, offset, length, span, }) {
|
|
|
7353
7098
|
const tx = span
|
|
7354
7099
|
? span.startChild({ op: 'fetchFileRange' })
|
|
7355
7100
|
: Telemetry.startTransaction({ name: 'fetchFileRange' });
|
|
7356
|
-
const {
|
|
7357
|
-
const
|
|
7101
|
+
const { mountPath, nfsPath } = resolveNfsPath(slot, path);
|
|
7102
|
+
const { mountClient, nfsClient } = await getClients(device);
|
|
7103
|
+
const rootHandle = await getRootHandle({ device, mountPath, mountClient, span: tx });
|
|
7358
7104
|
if (rootHandle === null) {
|
|
7359
7105
|
throw badRoothandleError(slot, device.id);
|
|
7360
7106
|
}
|
|
7361
7107
|
let fileInfo = null;
|
|
7362
7108
|
try {
|
|
7363
|
-
fileInfo = await (0, programs_1.lookupPath)(nfsClient, rootHandle,
|
|
7109
|
+
fileInfo = await (0, programs_1.lookupPath)(nfsClient, rootHandle, nfsPath, tx);
|
|
7364
7110
|
}
|
|
7365
7111
|
catch {
|
|
7366
7112
|
rootHandleCache.delete(device.ip.address);
|
|
7367
|
-
const newRootHandle = await getRootHandle({ device,
|
|
7113
|
+
const newRootHandle = await getRootHandle({ device, mountPath, mountClient, span: tx });
|
|
7368
7114
|
if (newRootHandle === null) {
|
|
7369
7115
|
throw badRoothandleError(slot, device.id);
|
|
7370
7116
|
}
|
|
7371
|
-
fileInfo = await (0, programs_1.lookupPath)(nfsClient, newRootHandle,
|
|
7117
|
+
fileInfo = await (0, programs_1.lookupPath)(nfsClient, newRootHandle, nfsPath, tx);
|
|
7372
7118
|
}
|
|
7373
7119
|
const actualOffset = Math.min(offset, fileInfo.size);
|
|
7374
7120
|
const actualLength = Math.min(length, fileInfo.size - actualOffset);
|
|
@@ -7392,22 +7138,23 @@ async function getFileInfo({ device, slot, path, span, }) {
|
|
|
7392
7138
|
const tx = span
|
|
7393
7139
|
? span.startChild({ op: 'getFileInfo' })
|
|
7394
7140
|
: Telemetry.startTransaction({ name: 'getFileInfo' });
|
|
7395
|
-
const {
|
|
7396
|
-
const
|
|
7141
|
+
const { mountPath, nfsPath } = resolveNfsPath(slot, path);
|
|
7142
|
+
const { mountClient, nfsClient } = await getClients(device);
|
|
7143
|
+
const rootHandle = await getRootHandle({ device, mountPath, mountClient, span: tx });
|
|
7397
7144
|
if (rootHandle === null) {
|
|
7398
7145
|
throw badRoothandleError(slot, device.id);
|
|
7399
7146
|
}
|
|
7400
7147
|
let fileInfo;
|
|
7401
7148
|
try {
|
|
7402
|
-
fileInfo = await (0, programs_1.lookupPath)(nfsClient, rootHandle,
|
|
7149
|
+
fileInfo = await (0, programs_1.lookupPath)(nfsClient, rootHandle, nfsPath, tx);
|
|
7403
7150
|
}
|
|
7404
7151
|
catch {
|
|
7405
7152
|
rootHandleCache.delete(device.ip.address);
|
|
7406
|
-
const newRootHandle = await getRootHandle({ device,
|
|
7153
|
+
const newRootHandle = await getRootHandle({ device, mountPath, mountClient, span: tx });
|
|
7407
7154
|
if (newRootHandle === null) {
|
|
7408
7155
|
throw badRoothandleError(slot, device.id);
|
|
7409
7156
|
}
|
|
7410
|
-
fileInfo = await (0, programs_1.lookupPath)(nfsClient, newRootHandle,
|
|
7157
|
+
fileInfo = await (0, programs_1.lookupPath)(nfsClient, newRootHandle, nfsPath, tx);
|
|
7411
7158
|
}
|
|
7412
7159
|
tx.setData('path', path);
|
|
7413
7160
|
tx.setData('slot', (0, utils_1.getSlotName)(slot));
|
|
@@ -7427,8 +7174,9 @@ async function fetchFile({ device, slot, path, onProgress, span, chunkSize, }) {
|
|
|
7427
7174
|
const tx = span
|
|
7428
7175
|
? span.startChild({ op: 'fetchFile' })
|
|
7429
7176
|
: Telemetry.startTransaction({ name: 'fetchFile' });
|
|
7430
|
-
const {
|
|
7431
|
-
const
|
|
7177
|
+
const { mountPath, nfsPath } = resolveNfsPath(slot, path);
|
|
7178
|
+
const { mountClient, nfsClient } = await getClients(device);
|
|
7179
|
+
const rootHandle = await getRootHandle({ device, mountPath, mountClient, span: tx });
|
|
7432
7180
|
if (rootHandle === null) {
|
|
7433
7181
|
throw badRoothandleError(slot, device.id);
|
|
7434
7182
|
}
|
|
@@ -7436,16 +7184,16 @@ async function fetchFile({ device, slot, path, onProgress, span, chunkSize, }) {
|
|
|
7436
7184
|
// a path lets first try and clear our roothandle cache
|
|
7437
7185
|
let fileInfo = null;
|
|
7438
7186
|
try {
|
|
7439
|
-
fileInfo = await (0, programs_1.lookupPath)(nfsClient, rootHandle,
|
|
7187
|
+
fileInfo = await (0, programs_1.lookupPath)(nfsClient, rootHandle, nfsPath, tx);
|
|
7440
7188
|
}
|
|
7441
7189
|
catch {
|
|
7442
7190
|
rootHandleCache.delete(device.ip.address);
|
|
7443
|
-
const rootHandle = await getRootHandle({ device,
|
|
7191
|
+
const rootHandle = await getRootHandle({ device, mountPath, mountClient, span: tx });
|
|
7444
7192
|
if (rootHandle === null) {
|
|
7445
7193
|
throw badRoothandleError(slot, device.id);
|
|
7446
7194
|
}
|
|
7447
7195
|
// Desperately try once more to lookup the file
|
|
7448
|
-
fileInfo = await (0, programs_1.lookupPath)(nfsClient, rootHandle,
|
|
7196
|
+
fileInfo = await (0, programs_1.lookupPath)(nfsClient, rootHandle, nfsPath, tx);
|
|
7449
7197
|
}
|
|
7450
7198
|
const file = await (0, programs_1.fetchFile)(nfsClient, fileInfo, onProgress, tx, chunkSize);
|
|
7451
7199
|
tx.setData('path', path);
|
|
@@ -7483,6 +7231,7 @@ function configureRetryStrategy(config) {
|
|
|
7483
7231
|
"use strict";
|
|
7484
7232
|
|
|
7485
7233
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
7234
|
+
exports.REKORDBOX_PORTMAP_PORT = exports.STANDARD_PORTMAP_PORT = void 0;
|
|
7486
7235
|
exports.makeProgramClient = makeProgramClient;
|
|
7487
7236
|
exports.getExports = getExports;
|
|
7488
7237
|
exports.mountFilesystem = mountFilesystem;
|
|
@@ -7497,10 +7246,19 @@ const xdr_1 = __webpack_require__(/*! ./xdr */ "./src/nfs/xdr.ts");
|
|
|
7497
7246
|
* How many bytes of a file should we read at once.
|
|
7498
7247
|
*/
|
|
7499
7248
|
const READ_SIZE = 8192;
|
|
7249
|
+
/**
|
|
7250
|
+
* Standard portmapper port used by CDJs and other hardware.
|
|
7251
|
+
*/
|
|
7252
|
+
exports.STANDARD_PORTMAP_PORT = 111;
|
|
7253
|
+
/**
|
|
7254
|
+
* Non-standard portmapper port used by rekordbox software.
|
|
7255
|
+
* Rekordbox registers its RPC services on this port instead of 111.
|
|
7256
|
+
*/
|
|
7257
|
+
exports.REKORDBOX_PORTMAP_PORT = 50111;
|
|
7500
7258
|
/**
|
|
7501
7259
|
* Queries for the listening port of a RPC program
|
|
7502
7260
|
*/
|
|
7503
|
-
async function makeProgramClient(conn, program) {
|
|
7261
|
+
async function makeProgramClient(conn, program, portmapPort = exports.STANDARD_PORTMAP_PORT) {
|
|
7504
7262
|
const getPortData = new xdr_1.portmap.GetPort({
|
|
7505
7263
|
program: program.id,
|
|
7506
7264
|
version: program.version,
|
|
@@ -7508,7 +7266,7 @@ async function makeProgramClient(conn, program) {
|
|
|
7508
7266
|
port: 0,
|
|
7509
7267
|
});
|
|
7510
7268
|
const data = await conn.call({
|
|
7511
|
-
port:
|
|
7269
|
+
port: portmapPort,
|
|
7512
7270
|
program: xdr_1.portmap.Program,
|
|
7513
7271
|
version: xdr_1.portmap.Version,
|
|
7514
7272
|
procedure: xdr_1.portmap.Procedure.getPort().value,
|
|
@@ -8565,10 +8323,14 @@ class QueryInterface {
|
|
|
8565
8323
|
const anyArgs = args;
|
|
8566
8324
|
const handler = queries_1.queryHandlers[query];
|
|
8567
8325
|
const releaseLock = await __classPrivateFieldGet(this, _QueryInterface_lock, "f").acquire();
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
|
|
8326
|
+
try {
|
|
8327
|
+
const response = await handler({ conn, lookupDescriptor, span: tx, args: anyArgs });
|
|
8328
|
+
tx.finish();
|
|
8329
|
+
return response;
|
|
8330
|
+
}
|
|
8331
|
+
finally {
|
|
8332
|
+
releaseLock();
|
|
8333
|
+
}
|
|
8572
8334
|
}
|
|
8573
8335
|
}
|
|
8574
8336
|
exports.QueryInterface = QueryInterface;
|
|
@@ -8596,6 +8358,11 @@ class RemoteDatabase {
|
|
|
8596
8358
|
const { ip } = device;
|
|
8597
8359
|
const dbPort = await getRemoteDBServerPort(ip);
|
|
8598
8360
|
const socket = new promise_socket_1.default(new net_1.Socket());
|
|
8361
|
+
// Set a connection timeout to prevent hanging forever
|
|
8362
|
+
socket.stream.setTimeout(10000);
|
|
8363
|
+
socket.stream.once('timeout', () => {
|
|
8364
|
+
socket.stream.destroy(new Error(`RemoteDB connection to ${ip.address}:${dbPort} timed out`));
|
|
8365
|
+
});
|
|
8599
8366
|
await socket.connect(dbPort, ip.address);
|
|
8600
8367
|
// Send required preamble to open communications with the device
|
|
8601
8368
|
const preamble = new fields_1.UInt32(0x01);
|
|
@@ -8617,6 +8384,9 @@ class RemoteDatabase {
|
|
|
8617
8384
|
if (resp.type !== types_1.MessageType.Success) {
|
|
8618
8385
|
throw new Error(`Failed to introduce self to device ID: ${device.id}`);
|
|
8619
8386
|
}
|
|
8387
|
+
// Clear socket timeout after successful connection — the per-device mutex
|
|
8388
|
+
// and application-level Promise.race timeouts handle query timeouts
|
|
8389
|
+
socket.stream.setTimeout(0);
|
|
8620
8390
|
__classPrivateFieldGet(this, _RemoteDatabase_connections, "f").set(device.id, new Connection(device, socket));
|
|
8621
8391
|
tx.finish();
|
|
8622
8392
|
};
|
|
@@ -8658,15 +8428,19 @@ class RemoteDatabase {
|
|
|
8658
8428
|
}
|
|
8659
8429
|
const lock = (_a = __classPrivateFieldGet(this, _RemoteDatabase_deviceLocks, "f").get(device.id)) !== null && _a !== void 0 ? _a : __classPrivateFieldGet(this, _RemoteDatabase_deviceLocks, "f").set(device.id, new async_mutex_1.Mutex()).get(device.id);
|
|
8660
8430
|
const releaseLock = await lock.acquire();
|
|
8661
|
-
|
|
8662
|
-
|
|
8663
|
-
|
|
8431
|
+
try {
|
|
8432
|
+
let conn = __classPrivateFieldGet(this, _RemoteDatabase_connections, "f").get(deviceId);
|
|
8433
|
+
if (conn === undefined) {
|
|
8434
|
+
await this.connectToDevice(device);
|
|
8435
|
+
}
|
|
8436
|
+
conn = __classPrivateFieldGet(this, _RemoteDatabase_connections, "f").get(deviceId);
|
|
8437
|
+
// NOTE: We pass the same lock we use for this device to the query
|
|
8438
|
+
// interface to ensure all query interfaces use the same lock.
|
|
8439
|
+
return new QueryInterface(conn, lock, __classPrivateFieldGet(this, _RemoteDatabase_hostDevice, "f"));
|
|
8440
|
+
}
|
|
8441
|
+
finally {
|
|
8442
|
+
releaseLock();
|
|
8664
8443
|
}
|
|
8665
|
-
conn = __classPrivateFieldGet(this, _RemoteDatabase_connections, "f").get(deviceId);
|
|
8666
|
-
releaseLock();
|
|
8667
|
-
// NOTE: We pass the same lock we use for this device to the query
|
|
8668
|
-
// interface to ensure all query interfaces use the same lock.
|
|
8669
|
-
return new QueryInterface(conn, lock, __classPrivateFieldGet(this, _RemoteDatabase_hostDevice, "f"));
|
|
8670
8444
|
}
|
|
8671
8445
|
}
|
|
8672
8446
|
_RemoteDatabase_hostDevice = new WeakMap(), _RemoteDatabase_deviceManager = new WeakMap(), _RemoteDatabase_connections = new WeakMap(), _RemoteDatabase_deviceLocks = new WeakMap();
|
|
@@ -9368,7 +9142,7 @@ const utils_1 = __webpack_require__(/*! ./utils */ "./src/remotedb/utils.ts");
|
|
|
9368
9142
|
* Lookup track metadata from rekordbox and coerce it into a Track entity
|
|
9369
9143
|
*/
|
|
9370
9144
|
async function getMetadata(opts) {
|
|
9371
|
-
var _a, _b, _c, _d, _e;
|
|
9145
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
|
|
9372
9146
|
const { conn, lookupDescriptor, span, args } = opts;
|
|
9373
9147
|
const { trackId } = args;
|
|
9374
9148
|
const request = new message_1.Message({
|
|
@@ -9385,24 +9159,25 @@ async function getMetadata(opts) {
|
|
|
9385
9159
|
trackItems[item.type] = item;
|
|
9386
9160
|
}
|
|
9387
9161
|
// Translate our trackItems into a (partial) Track entity.
|
|
9162
|
+
// Use optional chaining for all fields since streaming tracks may omit some.
|
|
9388
9163
|
const track = {
|
|
9389
|
-
id: trackItems[item_1.ItemType.TrackTitle].id,
|
|
9390
|
-
title: trackItems[item_1.ItemType.TrackTitle].title,
|
|
9391
|
-
duration: trackItems[item_1.ItemType.Duration].duration,
|
|
9392
|
-
tempo: trackItems[item_1.ItemType.Tempo].bpm,
|
|
9393
|
-
comment: trackItems[item_1.ItemType.Comment].comment,
|
|
9394
|
-
rating: trackItems[item_1.ItemType.Rating].rating,
|
|
9395
|
-
year: (
|
|
9396
|
-
bitrate: (
|
|
9397
|
-
artwork: { id: trackItems[item_1.ItemType.TrackTitle].artworkId },
|
|
9398
|
-
album: trackItems[item_1.ItemType.AlbumTitle],
|
|
9399
|
-
artist: trackItems[item_1.ItemType.Artist],
|
|
9400
|
-
genre: trackItems[item_1.ItemType.Genre],
|
|
9401
|
-
key: trackItems[item_1.ItemType.Key],
|
|
9402
|
-
color: (0, utils_1.findColor)(Object.values(trackItems)),
|
|
9403
|
-
label: (
|
|
9404
|
-
remixer: (
|
|
9405
|
-
originalArtist: (
|
|
9164
|
+
id: (_b = (_a = trackItems[item_1.ItemType.TrackTitle]) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : 0,
|
|
9165
|
+
title: (_d = (_c = trackItems[item_1.ItemType.TrackTitle]) === null || _c === void 0 ? void 0 : _c.title) !== null && _d !== void 0 ? _d : '',
|
|
9166
|
+
duration: (_f = (_e = trackItems[item_1.ItemType.Duration]) === null || _e === void 0 ? void 0 : _e.duration) !== null && _f !== void 0 ? _f : 0,
|
|
9167
|
+
tempo: (_h = (_g = trackItems[item_1.ItemType.Tempo]) === null || _g === void 0 ? void 0 : _g.bpm) !== null && _h !== void 0 ? _h : 0,
|
|
9168
|
+
comment: (_k = (_j = trackItems[item_1.ItemType.Comment]) === null || _j === void 0 ? void 0 : _j.comment) !== null && _k !== void 0 ? _k : '',
|
|
9169
|
+
rating: (_m = (_l = trackItems[item_1.ItemType.Rating]) === null || _l === void 0 ? void 0 : _l.rating) !== null && _m !== void 0 ? _m : 0,
|
|
9170
|
+
year: (_o = trackItems === null || trackItems === void 0 ? void 0 : trackItems[item_1.ItemType.Year]) === null || _o === void 0 ? void 0 : _o.year,
|
|
9171
|
+
bitrate: (_p = trackItems === null || trackItems === void 0 ? void 0 : trackItems[item_1.ItemType.BitRate]) === null || _p === void 0 ? void 0 : _p.bitrate,
|
|
9172
|
+
artwork: { id: (_r = (_q = trackItems[item_1.ItemType.TrackTitle]) === null || _q === void 0 ? void 0 : _q.artworkId) !== null && _r !== void 0 ? _r : 0 },
|
|
9173
|
+
album: (_s = trackItems[item_1.ItemType.AlbumTitle]) !== null && _s !== void 0 ? _s : null,
|
|
9174
|
+
artist: (_t = trackItems[item_1.ItemType.Artist]) !== null && _t !== void 0 ? _t : null,
|
|
9175
|
+
genre: (_u = trackItems[item_1.ItemType.Genre]) !== null && _u !== void 0 ? _u : null,
|
|
9176
|
+
key: (_v = trackItems[item_1.ItemType.Key]) !== null && _v !== void 0 ? _v : null,
|
|
9177
|
+
color: (_w = (0, utils_1.findColor)(Object.values(trackItems))) !== null && _w !== void 0 ? _w : null,
|
|
9178
|
+
label: (_x = trackItems[item_1.ItemType.Label]) !== null && _x !== void 0 ? _x : null,
|
|
9179
|
+
remixer: (_y = trackItems === null || trackItems === void 0 ? void 0 : trackItems[item_1.ItemType.Remixer]) !== null && _y !== void 0 ? _y : null,
|
|
9180
|
+
originalArtist: (_z = trackItems === null || trackItems === void 0 ? void 0 : trackItems[item_1.ItemType.OriginalArtist]) !== null && _z !== void 0 ? _z : null,
|
|
9406
9181
|
composer: null,
|
|
9407
9182
|
fileName: '',
|
|
9408
9183
|
filePath: '',
|
|
@@ -10010,7 +9785,7 @@ function mediaSlotFromPacket(packet) {
|
|
|
10010
9785
|
return undefined;
|
|
10011
9786
|
}
|
|
10012
9787
|
const name = packet
|
|
10013
|
-
.slice(0x2c,
|
|
9788
|
+
.slice(0x2c, 0x2c + 40)
|
|
10014
9789
|
.toString()
|
|
10015
9790
|
.replace(/\0/g, '');
|
|
10016
9791
|
const createdDate = new Date(packet
|
|
@@ -10187,7 +9962,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
10187
9962
|
};
|
|
10188
9963
|
})();
|
|
10189
9964
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
10190
|
-
exports.MixstatusMode = exports.NetworkState = exports.
|
|
9965
|
+
exports.MixstatusMode = exports.NetworkState = exports.HotcueButton = exports.CueColor = exports.TrackType = exports.MediaSlot = exports.MediaColor = exports.DeviceType = exports.CDJStatus = void 0;
|
|
10191
9966
|
exports.CDJStatus = __importStar(__webpack_require__(/*! src/status/types */ "./src/status/types.ts"));
|
|
10192
9967
|
/**
|
|
10193
9968
|
* Known device types on the network
|
|
@@ -10220,6 +9995,11 @@ var MediaSlot;
|
|
|
10220
9995
|
MediaSlot[MediaSlot["SD"] = 2] = "SD";
|
|
10221
9996
|
MediaSlot[MediaSlot["USB"] = 3] = "USB";
|
|
10222
9997
|
MediaSlot[MediaSlot["RB"] = 4] = "RB";
|
|
9998
|
+
MediaSlot[MediaSlot["Unknown05"] = 5] = "Unknown05";
|
|
9999
|
+
MediaSlot[MediaSlot["StreamingDirectPlay"] = 6] = "StreamingDirectPlay";
|
|
10000
|
+
MediaSlot[MediaSlot["Unknown07"] = 7] = "Unknown07";
|
|
10001
|
+
MediaSlot[MediaSlot["Unknown08"] = 8] = "Unknown08";
|
|
10002
|
+
MediaSlot[MediaSlot["Beatport"] = 9] = "Beatport";
|
|
10223
10003
|
})(MediaSlot || (exports.MediaSlot = MediaSlot = {}));
|
|
10224
10004
|
/**
|
|
10225
10005
|
* Track type flags
|
|
@@ -10230,46 +10010,14 @@ var TrackType;
|
|
|
10230
10010
|
TrackType[TrackType["RB"] = 1] = "RB";
|
|
10231
10011
|
TrackType[TrackType["Unanalyzed"] = 2] = "Unanalyzed";
|
|
10232
10012
|
TrackType[TrackType["AudioCD"] = 5] = "AudioCD";
|
|
10013
|
+
TrackType[TrackType["Streaming"] = 6] = "Streaming";
|
|
10233
10014
|
})(TrackType || (exports.TrackType = TrackType = {}));
|
|
10234
10015
|
/**
|
|
10235
|
-
*
|
|
10236
|
-
*/
|
|
10237
|
-
var HotcueButton;
|
|
10238
|
-
(function (HotcueButton) {
|
|
10239
|
-
HotcueButton[HotcueButton["A"] = 1] = "A";
|
|
10240
|
-
HotcueButton[HotcueButton["B"] = 2] = "B";
|
|
10241
|
-
HotcueButton[HotcueButton["C"] = 3] = "C";
|
|
10242
|
-
HotcueButton[HotcueButton["D"] = 4] = "D";
|
|
10243
|
-
HotcueButton[HotcueButton["E"] = 5] = "E";
|
|
10244
|
-
HotcueButton[HotcueButton["F"] = 6] = "F";
|
|
10245
|
-
HotcueButton[HotcueButton["G"] = 7] = "G";
|
|
10246
|
-
HotcueButton[HotcueButton["H"] = 8] = "H";
|
|
10247
|
-
})(HotcueButton || (exports.HotcueButton = HotcueButton = {}));
|
|
10248
|
-
/**
|
|
10249
|
-
* When a custom color is not configured the cue point will be one of these
|
|
10250
|
-
* colors.
|
|
10016
|
+
* Re-export cue types from onelibrary-connect
|
|
10251
10017
|
*/
|
|
10252
|
-
var
|
|
10253
|
-
(function (
|
|
10254
|
-
|
|
10255
|
-
CueColor[CueColor["Blank"] = 21] = "Blank";
|
|
10256
|
-
CueColor[CueColor["Magenta"] = 49] = "Magenta";
|
|
10257
|
-
CueColor[CueColor["Violet"] = 56] = "Violet";
|
|
10258
|
-
CueColor[CueColor["Fuchsia"] = 60] = "Fuchsia";
|
|
10259
|
-
CueColor[CueColor["LightSlateBlue"] = 62] = "LightSlateBlue";
|
|
10260
|
-
CueColor[CueColor["Blue"] = 1] = "Blue";
|
|
10261
|
-
CueColor[CueColor["SteelBlue"] = 5] = "SteelBlue";
|
|
10262
|
-
CueColor[CueColor["Aqua"] = 9] = "Aqua";
|
|
10263
|
-
CueColor[CueColor["SeaGreen"] = 14] = "SeaGreen";
|
|
10264
|
-
CueColor[CueColor["Teal"] = 18] = "Teal";
|
|
10265
|
-
CueColor[CueColor["Green"] = 22] = "Green";
|
|
10266
|
-
CueColor[CueColor["Lime"] = 26] = "Lime";
|
|
10267
|
-
CueColor[CueColor["Olive"] = 30] = "Olive";
|
|
10268
|
-
CueColor[CueColor["Yellow"] = 32] = "Yellow";
|
|
10269
|
-
CueColor[CueColor["Orange"] = 38] = "Orange";
|
|
10270
|
-
CueColor[CueColor["Red"] = 42] = "Red";
|
|
10271
|
-
CueColor[CueColor["Pink"] = 45] = "Pink";
|
|
10272
|
-
})(CueColor || (exports.CueColor = CueColor = {}));
|
|
10018
|
+
var onelibrary_connect_1 = __webpack_require__(/*! onelibrary-connect */ "onelibrary-connect");
|
|
10019
|
+
Object.defineProperty(exports, "CueColor", ({ enumerable: true, get: function () { return onelibrary_connect_1.CueColor; } }));
|
|
10020
|
+
Object.defineProperty(exports, "HotcueButton", ({ enumerable: true, get: function () { return onelibrary_connect_1.HotcueButton; } }));
|
|
10273
10021
|
var NetworkState;
|
|
10274
10022
|
(function (NetworkState) {
|
|
10275
10023
|
/**
|
|
@@ -10427,6 +10175,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
10427
10175
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
10428
10176
|
exports.buildName = buildName;
|
|
10429
10177
|
exports.getMatchingInterface = getMatchingInterface;
|
|
10178
|
+
exports.getBroadcastAddress = getBroadcastAddress;
|
|
10430
10179
|
exports.bpmToSeconds = bpmToSeconds;
|
|
10431
10180
|
exports.getSlotName = getSlotName;
|
|
10432
10181
|
exports.getTrackTypeName = getTrackTypeName;
|
|
@@ -10464,6 +10213,18 @@ function getMatchingInterface(ipAddr) {
|
|
|
10464
10213
|
}
|
|
10465
10214
|
return matchedIface;
|
|
10466
10215
|
}
|
|
10216
|
+
/**
|
|
10217
|
+
* Computes the IPv4 subnet broadcast address for a network interface.
|
|
10218
|
+
*
|
|
10219
|
+
* Builds the address from the interface CIDR so the broadcast covers the whole
|
|
10220
|
+
* subnet (e.g. x.x.x.255 for a /24). Falls back to the bare interface address
|
|
10221
|
+
* when no CIDR is available, which only reaches the host itself but avoids
|
|
10222
|
+
* throwing.
|
|
10223
|
+
*/
|
|
10224
|
+
function getBroadcastAddress(iface) {
|
|
10225
|
+
var _a;
|
|
10226
|
+
return new ip.Address4((_a = iface.cidr) !== null && _a !== void 0 ? _a : iface.address).endAddress().address;
|
|
10227
|
+
}
|
|
10467
10228
|
/**
|
|
10468
10229
|
* Given a BPM and pitch value, compute how many seconds per beat
|
|
10469
10230
|
*/
|
|
@@ -10721,13 +10482,14 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
10721
10482
|
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");
|
|
10722
10483
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
10723
10484
|
};
|
|
10724
|
-
var _Announcer_instances, _Announcer_announceSocket, _Announcer_deviceManager, _Announcer_vcdj, _Announcer_intervalHandle, _Announcer_fullStartup, _Announcer_currentStage, _Announcer_stageCounter, _Announcer_conflictListener, _Announcer_iface, _Announcer_startFullStartup, _Announcer_handleConflict, _Announcer_sendStagePackets, _Announcer_advanceStage, _Announcer_startKeepAlive;
|
|
10485
|
+
var _Announcer_instances, _Announcer_announceSocket, _Announcer_deviceManager, _Announcer_vcdj, _Announcer_intervalHandle, _Announcer_fullStartup, _Announcer_announceToStagehand, _Announcer_currentStage, _Announcer_stageCounter, _Announcer_conflictListener, _Announcer_iface, _Announcer_logger, _Announcer_onStartupComplete, _Announcer_startFullStartup, _Announcer_handleConflict, _Announcer_sendPacket, _Announcer_sendStagePackets, _Announcer_advanceStage, _Announcer_startKeepAlive;
|
|
10725
10486
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
10726
10487
|
exports.Announcer = exports.getVirtualCDJ = void 0;
|
|
10727
10488
|
exports.makeStatusPacket = makeStatusPacket;
|
|
10728
10489
|
exports.makeAnnouncePacket = makeAnnouncePacket;
|
|
10729
10490
|
const ip = __importStar(__webpack_require__(/*! ip-address */ "ip-address"));
|
|
10730
10491
|
const constants_1 = __webpack_require__(/*! src/constants */ "./src/constants.ts");
|
|
10492
|
+
const logger_1 = __webpack_require__(/*! src/logger */ "./src/logger.ts");
|
|
10731
10493
|
const types_1 = __webpack_require__(/*! src/types */ "./src/types.ts");
|
|
10732
10494
|
const utils_1 = __webpack_require__(/*! src/utils */ "./src/utils/index.ts");
|
|
10733
10495
|
/**
|
|
@@ -10975,7 +10737,7 @@ function parseConflictPacket(packet) {
|
|
|
10975
10737
|
* as if it was a real CDJ.
|
|
10976
10738
|
*/
|
|
10977
10739
|
class Announcer {
|
|
10978
|
-
constructor(vcdj, announceSocket, deviceManager, iface, fullStartup = false) {
|
|
10740
|
+
constructor(vcdj, announceSocket, deviceManager, iface, fullStartup = false, announceToStagehand = false, logger = logger_1.noopLogger) {
|
|
10979
10741
|
_Announcer_instances.add(this);
|
|
10980
10742
|
/**
|
|
10981
10743
|
* The announce socket to use to make the announcements
|
|
@@ -10998,6 +10760,11 @@ class Announcer {
|
|
|
10998
10760
|
* Whether to use full startup protocol
|
|
10999
10761
|
*/
|
|
11000
10762
|
_Announcer_fullStartup.set(this, void 0);
|
|
10763
|
+
/**
|
|
10764
|
+
* Whether to send announcer packets to Pioneer Stagehand devices, which are
|
|
10765
|
+
* normally excluded because they crash on our packets
|
|
10766
|
+
*/
|
|
10767
|
+
_Announcer_announceToStagehand.set(this, void 0);
|
|
11001
10768
|
/**
|
|
11002
10769
|
* Current startup stage (only used when fullStartup is enabled)
|
|
11003
10770
|
*/
|
|
@@ -11014,11 +10781,36 @@ class Announcer {
|
|
|
11014
10781
|
* Network interface for the virtual CDJ
|
|
11015
10782
|
*/
|
|
11016
10783
|
_Announcer_iface.set(this, void 0);
|
|
10784
|
+
/**
|
|
10785
|
+
* Logger instance for diagnostic output
|
|
10786
|
+
*/
|
|
10787
|
+
_Announcer_logger.set(this, void 0);
|
|
10788
|
+
/**
|
|
10789
|
+
* Callback invoked when the full startup protocol completes
|
|
10790
|
+
*/
|
|
10791
|
+
_Announcer_onStartupComplete.set(this, void 0);
|
|
11017
10792
|
__classPrivateFieldSet(this, _Announcer_vcdj, vcdj, "f");
|
|
11018
10793
|
__classPrivateFieldSet(this, _Announcer_announceSocket, announceSocket, "f");
|
|
11019
10794
|
__classPrivateFieldSet(this, _Announcer_deviceManager, deviceManager, "f");
|
|
11020
10795
|
__classPrivateFieldSet(this, _Announcer_iface, iface, "f");
|
|
11021
10796
|
__classPrivateFieldSet(this, _Announcer_fullStartup, fullStartup, "f");
|
|
10797
|
+
__classPrivateFieldSet(this, _Announcer_announceToStagehand, announceToStagehand, "f");
|
|
10798
|
+
__classPrivateFieldSet(this, _Announcer_logger, logger, "f");
|
|
10799
|
+
}
|
|
10800
|
+
/**
|
|
10801
|
+
* Returns a promise that resolves when the startup protocol completes.
|
|
10802
|
+
* Resolves immediately if fullStartup is disabled.
|
|
10803
|
+
*/
|
|
10804
|
+
get ready() {
|
|
10805
|
+
if (!__classPrivateFieldGet(this, _Announcer_fullStartup, "f")) {
|
|
10806
|
+
return Promise.resolve();
|
|
10807
|
+
}
|
|
10808
|
+
if (__classPrivateFieldGet(this, _Announcer_currentStage, "f") === StartupStage.KeepAlive) {
|
|
10809
|
+
return Promise.resolve();
|
|
10810
|
+
}
|
|
10811
|
+
return new Promise(resolve => {
|
|
10812
|
+
__classPrivateFieldSet(this, _Announcer_onStartupComplete, resolve, "f");
|
|
10813
|
+
});
|
|
11022
10814
|
}
|
|
11023
10815
|
start() {
|
|
11024
10816
|
if (__classPrivateFieldGet(this, _Announcer_fullStartup, "f")) {
|
|
@@ -11042,14 +10834,14 @@ class Announcer {
|
|
|
11042
10834
|
}
|
|
11043
10835
|
}
|
|
11044
10836
|
exports.Announcer = Announcer;
|
|
11045
|
-
_Announcer_announceSocket = new WeakMap(), _Announcer_deviceManager = new WeakMap(), _Announcer_vcdj = new WeakMap(), _Announcer_intervalHandle = new WeakMap(), _Announcer_fullStartup = new WeakMap(), _Announcer_currentStage = new WeakMap(), _Announcer_stageCounter = new WeakMap(), _Announcer_conflictListener = new WeakMap(), _Announcer_iface = new WeakMap(), _Announcer_instances = new WeakSet(), _Announcer_startFullStartup = function _Announcer_startFullStartup() {
|
|
10837
|
+
_Announcer_announceSocket = new WeakMap(), _Announcer_deviceManager = new WeakMap(), _Announcer_vcdj = new WeakMap(), _Announcer_intervalHandle = new WeakMap(), _Announcer_fullStartup = new WeakMap(), _Announcer_announceToStagehand = new WeakMap(), _Announcer_currentStage = new WeakMap(), _Announcer_stageCounter = new WeakMap(), _Announcer_conflictListener = new WeakMap(), _Announcer_iface = new WeakMap(), _Announcer_logger = new WeakMap(), _Announcer_onStartupComplete = new WeakMap(), _Announcer_instances = new WeakSet(), _Announcer_startFullStartup = function _Announcer_startFullStartup() {
|
|
11046
10838
|
__classPrivateFieldSet(this, _Announcer_currentStage, StartupStage.InitialAnnounce, "f");
|
|
11047
10839
|
__classPrivateFieldSet(this, _Announcer_stageCounter, 0, "f");
|
|
11048
10840
|
// Set up conflict detection listener
|
|
11049
10841
|
__classPrivateFieldSet(this, _Announcer_conflictListener, (msg) => {
|
|
11050
10842
|
const conflictDeviceId = parseConflictPacket(msg);
|
|
11051
10843
|
if (conflictDeviceId === __classPrivateFieldGet(this, _Announcer_vcdj, "f").id) {
|
|
11052
|
-
|
|
10844
|
+
__classPrivateFieldGet(this, _Announcer_logger, "f").warn(`Device ID ${__classPrivateFieldGet(this, _Announcer_vcdj, "f").id} is already in use. Finding alternative...`);
|
|
11053
10845
|
__classPrivateFieldGet(this, _Announcer_instances, "m", _Announcer_handleConflict).call(this);
|
|
11054
10846
|
}
|
|
11055
10847
|
}, "f");
|
|
@@ -11080,20 +10872,29 @@ _Announcer_announceSocket = new WeakMap(), _Announcer_deviceManager = new WeakMa
|
|
|
11080
10872
|
}
|
|
11081
10873
|
}
|
|
11082
10874
|
if (newId === null) {
|
|
11083
|
-
|
|
10875
|
+
__classPrivateFieldGet(this, _Announcer_logger, "f").error('No available device IDs. All 32 slots are occupied.');
|
|
11084
10876
|
this.stop();
|
|
11085
10877
|
return;
|
|
11086
10878
|
}
|
|
11087
|
-
|
|
10879
|
+
__classPrivateFieldGet(this, _Announcer_logger, "f").info(`Switching to device ID ${newId}`);
|
|
11088
10880
|
// Update virtual CDJ with new ID
|
|
11089
10881
|
__classPrivateFieldSet(this, _Announcer_vcdj, (0, exports.getVirtualCDJ)(__classPrivateFieldGet(this, _Announcer_iface, "f"), newId), "f");
|
|
11090
10882
|
// Restart startup sequence
|
|
11091
10883
|
__classPrivateFieldSet(this, _Announcer_currentStage, StartupStage.InitialAnnounce, "f");
|
|
11092
10884
|
__classPrivateFieldSet(this, _Announcer_stageCounter, 0, "f");
|
|
11093
10885
|
__classPrivateFieldGet(this, _Announcer_instances, "m", _Announcer_sendStagePackets).call(this);
|
|
10886
|
+
}, _Announcer_sendPacket = function _Announcer_sendPacket(packet) {
|
|
10887
|
+
const devices = [...__classPrivateFieldGet(this, _Announcer_deviceManager, "f").devices.values()].filter(d => __classPrivateFieldGet(this, _Announcer_announceToStagehand, "f") || !d.name.toLowerCase().includes('stagehand'));
|
|
10888
|
+
devices.forEach(device => __classPrivateFieldGet(this, _Announcer_announceSocket, "f").send(packet, constants_1.ANNOUNCE_PORT, device.ip.address));
|
|
10889
|
+
// Cold-start discovery broadcast — gated; see the method doc above.
|
|
10890
|
+
if (devices.length === 0 && __classPrivateFieldGet(this, _Announcer_announceToStagehand, "f")) {
|
|
10891
|
+
const broadcastAddr = (0, utils_1.getBroadcastAddress)(__classPrivateFieldGet(this, _Announcer_iface, "f"));
|
|
10892
|
+
__classPrivateFieldGet(this, _Announcer_announceSocket, "f").send(packet, constants_1.ANNOUNCE_PORT, broadcastAddr);
|
|
10893
|
+
}
|
|
11094
10894
|
}, _Announcer_sendStagePackets = function _Announcer_sendStagePackets() {
|
|
11095
10895
|
var _a;
|
|
11096
|
-
|
|
10896
|
+
var _b;
|
|
10897
|
+
__classPrivateFieldSet(this, _Announcer_stageCounter, (_b = __classPrivateFieldGet(this, _Announcer_stageCounter, "f"), _b++, _b), "f");
|
|
11097
10898
|
// Build packet for current stage
|
|
11098
10899
|
let packet;
|
|
11099
10900
|
switch (__classPrivateFieldGet(this, _Announcer_currentStage, "f")) {
|
|
@@ -11112,16 +10913,11 @@ _Announcer_announceSocket = new WeakMap(), _Announcer_deviceManager = new WeakMa
|
|
|
11112
10913
|
case StartupStage.KeepAlive:
|
|
11113
10914
|
// Transition to keep-alive mode
|
|
11114
10915
|
__classPrivateFieldGet(this, _Announcer_instances, "m", _Announcer_startKeepAlive).call(this);
|
|
10916
|
+
(_a = __classPrivateFieldGet(this, _Announcer_onStartupComplete, "f")) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
10917
|
+
__classPrivateFieldSet(this, _Announcer_onStartupComplete, undefined, "f");
|
|
11115
10918
|
return;
|
|
11116
10919
|
}
|
|
11117
|
-
|
|
11118
|
-
const devices = [...__classPrivateFieldGet(this, _Announcer_deviceManager, "f").devices.values()];
|
|
11119
|
-
devices.forEach(device => __classPrivateFieldGet(this, _Announcer_announceSocket, "f").send(packet, constants_1.ANNOUNCE_PORT, device.ip.address));
|
|
11120
|
-
// Also broadcast to network if no devices yet
|
|
11121
|
-
if (devices.length === 0) {
|
|
11122
|
-
const broadcastAddr = __classPrivateFieldGet(this, _Announcer_vcdj, "f").ip.endAddress().address;
|
|
11123
|
-
__classPrivateFieldGet(this, _Announcer_announceSocket, "f").send(packet, constants_1.ANNOUNCE_PORT, broadcastAddr);
|
|
11124
|
-
}
|
|
10920
|
+
__classPrivateFieldGet(this, _Announcer_instances, "m", _Announcer_sendPacket).call(this, packet);
|
|
11125
10921
|
// Progress to next packet or stage
|
|
11126
10922
|
if (__classPrivateFieldGet(this, _Announcer_stageCounter, "f") >= 3) {
|
|
11127
10923
|
// Move to next stage
|
|
@@ -11160,13 +10956,7 @@ _Announcer_announceSocket = new WeakMap(), _Announcer_deviceManager = new WeakMa
|
|
|
11160
10956
|
const packet = __classPrivateFieldGet(this, _Announcer_fullStartup, "f")
|
|
11161
10957
|
? makeStage06Packet(__classPrivateFieldGet(this, _Announcer_vcdj, "f"), peerCount)
|
|
11162
10958
|
: makeAnnouncePacket(__classPrivateFieldGet(this, _Announcer_vcdj, "f"));
|
|
11163
|
-
|
|
11164
|
-
devices.forEach(device => __classPrivateFieldGet(this, _Announcer_announceSocket, "f").send(packet, constants_1.ANNOUNCE_PORT, device.ip.address));
|
|
11165
|
-
// Also broadcast if no devices
|
|
11166
|
-
if (devices.length === 0) {
|
|
11167
|
-
const broadcastAddr = __classPrivateFieldGet(this, _Announcer_vcdj, "f").ip.endAddress().address;
|
|
11168
|
-
__classPrivateFieldGet(this, _Announcer_announceSocket, "f").send(packet, constants_1.ANNOUNCE_PORT, broadcastAddr);
|
|
11169
|
-
}
|
|
10959
|
+
__classPrivateFieldGet(this, _Announcer_instances, "m", _Announcer_sendPacket).call(this, packet);
|
|
11170
10960
|
};
|
|
11171
10961
|
// Send first keep-alive immediately
|
|
11172
10962
|
sendKeepAlive();
|
|
@@ -11309,6 +11099,17 @@ module.exports = require("net");
|
|
|
11309
11099
|
|
|
11310
11100
|
/***/ },
|
|
11311
11101
|
|
|
11102
|
+
/***/ "onelibrary-connect"
|
|
11103
|
+
/*!*************************************!*\
|
|
11104
|
+
!*** external "onelibrary-connect" ***!
|
|
11105
|
+
\*************************************/
|
|
11106
|
+
(module) {
|
|
11107
|
+
|
|
11108
|
+
"use strict";
|
|
11109
|
+
module.exports = require("onelibrary-connect");
|
|
11110
|
+
|
|
11111
|
+
/***/ },
|
|
11112
|
+
|
|
11312
11113
|
/***/ "os"
|
|
11313
11114
|
/*!*********************!*\
|
|
11314
11115
|
!*** external "os" ***!
|
|
@@ -11373,17 +11174,6 @@ module.exports = require("promise-timeout");
|
|
|
11373
11174
|
"use strict";
|
|
11374
11175
|
module.exports = require("signale");
|
|
11375
11176
|
|
|
11376
|
-
/***/ },
|
|
11377
|
-
|
|
11378
|
-
/***/ "zlib"
|
|
11379
|
-
/*!***********************!*\
|
|
11380
|
-
!*** external "zlib" ***!
|
|
11381
|
-
\***********************/
|
|
11382
|
-
(module) {
|
|
11383
|
-
|
|
11384
|
-
"use strict";
|
|
11385
|
-
module.exports = require("zlib");
|
|
11386
|
-
|
|
11387
11177
|
/***/ }
|
|
11388
11178
|
|
|
11389
11179
|
/******/ });
|