alphatheta-connect 0.23.1 → 0.23.2

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.
@@ -25,5 +25,27 @@ export interface Options {
25
25
  */
26
26
  span?: Span;
27
27
  }
28
+ /**
29
+ * Why a local database lookup produced no track.
30
+ *
31
+ * `viaLocal` used to answer both cases with a bare `null`, so a metadata
32
+ * failure in the field was indistinguishable from a slot that never hydrated
33
+ * — the reason NP3-361 could not be diagnosed from the logs a user sent in.
34
+ */
35
+ export type LocalMiss =
36
+ /** No rekordbox database is loaded for that device slot */
37
+ 'no-database'
38
+ /** The database is loaded, but holds no track with that id */
39
+ | 'track-absent';
40
+ /**
41
+ * The outcome of a local database metadata lookup.
42
+ */
43
+ export type LocalResult = {
44
+ track: Track;
45
+ miss: null;
46
+ } | {
47
+ track: null;
48
+ miss: LocalMiss;
49
+ };
28
50
  export declare function viaRemote(remote: RemoteDatabase, opts: Required<Options>): Promise<Track<import("../entities").EntityFK.WithRelations> | null>;
29
- export declare function viaLocal(local: LocalDatabase, device: Device, opts: Required<Options>): Promise<Track | null>;
51
+ export declare function viaLocal(local: LocalDatabase, device: Device, opts: Required<Options>): Promise<LocalResult>;
package/lib/db/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import DeviceManager from "../devices";
2
2
  import { Track } from "../entities";
3
3
  import LocalDatabase from "../localdb";
4
4
  import { DatabaseType } from "../localdb/database-adapter";
5
+ import { type Logger } from "../logger";
5
6
  import RemoteDatabase from "../remotedb";
6
7
  import { MediaSlot, PlaylistContents, Waveforms } from "../types";
7
8
  import * as GetArtworkFromFile from './getArtworkFromFile';
@@ -15,7 +16,7 @@ import * as GetTrackAnalysis from './getTrackAnalysis';
15
16
  */
16
17
  declare class Database {
17
18
  #private;
18
- constructor(local: LocalDatabase, remote: RemoteDatabase, deviceManager: DeviceManager);
19
+ constructor(local: LocalDatabase, remote: RemoteDatabase, deviceManager: DeviceManager, logger?: Logger);
19
20
  /**
20
21
  * Get the database type (oneLibrary or pdb) for a loaded device slot.
21
22
  * Returns null if the slot uses remote database or no database is loaded.
package/lib/index.js CHANGED
@@ -1272,11 +1272,11 @@ async function viaLocal(local, device, opts) {
1272
1272
  }
1273
1273
  const adapter = await local.get(deviceId, trackSlot);
1274
1274
  if (adapter === null) {
1275
- return null;
1275
+ return { track: null, miss: 'no-database' };
1276
1276
  }
1277
1277
  const dbTrack = adapter.findTrack(trackId);
1278
1278
  if (dbTrack === null) {
1279
- return null;
1279
+ return { track: null, miss: 'track-absent' };
1280
1280
  }
1281
1281
  const anlz = await (0, rekordbox_1.loadAnlz)(dbTrack, 'DAT', (0, utils_1.anlzLoader)({ device, slot: trackSlot }));
1282
1282
  const track = {
@@ -1284,7 +1284,7 @@ async function viaLocal(local, device, opts) {
1284
1284
  beatGrid: anlz.beatGrid,
1285
1285
  waveformHd: null,
1286
1286
  };
1287
- return track;
1287
+ return { track, miss: null };
1288
1288
  }
1289
1289
 
1290
1290
 
@@ -1534,8 +1534,10 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
1534
1534
  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");
1535
1535
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
1536
1536
  };
1537
- var _Database_deviceManager, _Database_localDatabase, _Database_remoteDatabase, _Database_getTrackLookupStrategy, _Database_getMediaLookupStrategy;
1537
+ var _Database_instances, _Database_deviceManager, _Database_localDatabase, _Database_remoteDatabase, _Database_logger, _Database_getTrackLookupStrategy, _Database_getMediaLookupStrategy, _Database_metadataViaRemoteFallback;
1538
1538
  Object.defineProperty(exports, "__esModule", ({ value: true }));
1539
+ const constants_1 = __webpack_require__(/*! src/constants */ "./src/constants.ts");
1540
+ const logger_1 = __webpack_require__(/*! src/logger */ "./src/logger.ts");
1539
1541
  const types_1 = __webpack_require__(/*! src/types */ "./src/types.ts");
1540
1542
  const utils_1 = __webpack_require__(/*! src/utils */ "./src/utils/index.ts");
1541
1543
  const Telemetry = __importStar(__webpack_require__(/*! src/utils/telemetry */ "./src/utils/telemetry.ts"));
@@ -1558,7 +1560,8 @@ var LookupStrategy;
1558
1560
  * network for information from their databases.
1559
1561
  */
1560
1562
  class Database {
1561
- constructor(local, remote, deviceManager) {
1563
+ constructor(local, remote, deviceManager, logger = logger_1.noopLogger) {
1564
+ _Database_instances.add(this);
1562
1565
  _Database_deviceManager.set(this, void 0);
1563
1566
  /**
1564
1567
  * The local database service, used when querying media devices connected
@@ -1570,6 +1573,7 @@ class Database {
1570
1573
  * CDJ with an unanalyzed media device connected (when possible).
1571
1574
  */
1572
1575
  _Database_remoteDatabase.set(this, void 0);
1576
+ _Database_logger.set(this, void 0);
1573
1577
  _Database_getTrackLookupStrategy.set(this, (device, type) => {
1574
1578
  const isUnanalyzed = type === types_1.TrackType.AudioCD || type === types_1.TrackType.Unanalyzed;
1575
1579
  const isStreaming = type === types_1.TrackType.Streaming;
@@ -1592,6 +1596,7 @@ class Database {
1592
1596
  __classPrivateFieldSet(this, _Database_localDatabase, local, "f");
1593
1597
  __classPrivateFieldSet(this, _Database_remoteDatabase, remote, "f");
1594
1598
  __classPrivateFieldSet(this, _Database_deviceManager, deviceManager, "f");
1599
+ __classPrivateFieldSet(this, _Database_logger, logger, "f");
1595
1600
  }
1596
1601
  /**
1597
1602
  * Get the database type (oneLibrary or pdb) for a loaded device slot.
@@ -1622,7 +1627,18 @@ class Database {
1622
1627
  track = await GetMetadata.viaRemote(__classPrivateFieldGet(this, _Database_remoteDatabase, "f"), callOpts);
1623
1628
  }
1624
1629
  if (strategy === LookupStrategy.Local) {
1625
- track = await GetMetadata.viaLocal(__classPrivateFieldGet(this, _Database_localDatabase, "f"), device, callOpts);
1630
+ const local = await GetMetadata.viaLocal(__classPrivateFieldGet(this, _Database_localDatabase, "f"), device, callOpts);
1631
+ track = local.track;
1632
+ // A local miss used to end the lookup, so the DJ's track silently never
1633
+ // reached the overlay for the rest of the set (NP3-361). Say what was
1634
+ // missed, then give the CDJ's own database a chance to answer.
1635
+ if (local.miss !== null) {
1636
+ __classPrivateFieldGet(this, _Database_logger, "f").warn(`Local metadata lookup missed track ${opts.trackId} on device ${deviceId} ` +
1637
+ `${(0, utils_1.getSlotName)(trackSlot)}: ${local.miss === 'no-database'
1638
+ ? 'no rekordbox database is loaded for that slot'
1639
+ : 'the loaded database holds no track with that id'}`);
1640
+ track = await __classPrivateFieldGet(this, _Database_instances, "m", _Database_metadataViaRemoteFallback).call(this, callOpts);
1641
+ }
1626
1642
  }
1627
1643
  if (strategy === LookupStrategy.NoneAvailable) {
1628
1644
  tx.setStatus(telemetry_1.SpanStatus.Unavailable);
@@ -1816,7 +1832,40 @@ class Database {
1816
1832
  return contents;
1817
1833
  }
1818
1834
  }
1819
- _Database_deviceManager = new WeakMap(), _Database_localDatabase = new WeakMap(), _Database_remoteDatabase = new WeakMap(), _Database_getTrackLookupStrategy = new WeakMap(), _Database_getMediaLookupStrategy = new WeakMap();
1835
+ _Database_deviceManager = new WeakMap(), _Database_localDatabase = new WeakMap(), _Database_remoteDatabase = new WeakMap(), _Database_logger = new WeakMap(), _Database_getTrackLookupStrategy = new WeakMap(), _Database_getMediaLookupStrategy = new WeakMap(), _Database_instances = new WeakSet(), _Database_metadataViaRemoteFallback =
1836
+ /**
1837
+ * Second chance for a track the local database could not answer for.
1838
+ *
1839
+ * CDJs only answer remote database queries from a host announcing a device
1840
+ * ID in the 1-6 range, so this is unavailable whenever the virtual CDJ sits
1841
+ * outside it (the package default is 7). That is a real configuration, not
1842
+ * an error — log why the fallback was skipped rather than throwing, so the
1843
+ * next report says which of the two happened.
1844
+ */
1845
+ async function _Database_metadataViaRemoteFallback(opts) {
1846
+ const hostId = __classPrivateFieldGet(this, _Database_remoteDatabase, "f").hostDevice.id;
1847
+ if (hostId < constants_1.MIN_CDJ_DEVICE_ID || hostId > constants_1.MAX_CDJ_DEVICE_ID) {
1848
+ __classPrivateFieldGet(this, _Database_logger, "f").warn(`No remote fallback for track ${opts.trackId}: this player is device ` +
1849
+ `${hostId}, and CDJs only answer remote database queries from ` +
1850
+ `devices ${constants_1.MIN_CDJ_DEVICE_ID}-${constants_1.MAX_CDJ_DEVICE_ID}`);
1851
+ return null;
1852
+ }
1853
+ try {
1854
+ const track = await GetMetadata.viaRemote(__classPrivateFieldGet(this, _Database_remoteDatabase, "f"), opts);
1855
+ if (track === null) {
1856
+ __classPrivateFieldGet(this, _Database_logger, "f").warn(`Remote fallback found no track ${opts.trackId} on device ${opts.deviceId}`);
1857
+ return null;
1858
+ }
1859
+ __classPrivateFieldGet(this, _Database_logger, "f").info(`Remote fallback recovered track ${opts.trackId} from device ${opts.deviceId}`);
1860
+ return track;
1861
+ }
1862
+ catch (e) {
1863
+ const message = e instanceof Error ? e.message : String(e);
1864
+ __classPrivateFieldGet(this, _Database_logger, "f").warn(`Remote fallback failed for track ${opts.trackId} on device ` +
1865
+ `${opts.deviceId}: ${message}`);
1866
+ return null;
1867
+ }
1868
+ };
1820
1869
  exports["default"] = Database;
1821
1870
 
1822
1871
 
@@ -7050,7 +7099,7 @@ class ProlinkNetwork {
7050
7099
  const remotedb = new remotedb_1.default(__classPrivateFieldGet(this, _ProlinkNetwork_deviceManager, "f"), vcdj);
7051
7100
  const localdb = new localdb_1.default(vcdj, __classPrivateFieldGet(this, _ProlinkNetwork_deviceManager, "f"), __classPrivateFieldGet(this, _ProlinkNetwork_statusEmitter, "f"), (_f = __classPrivateFieldGet(this, _ProlinkNetwork_config, "f").databasePreference) !== null && _f !== void 0 ? _f : 'auto');
7052
7101
  // Create unified database
7053
- const database = new db_1.default(localdb, remotedb, __classPrivateFieldGet(this, _ProlinkNetwork_deviceManager, "f"));
7102
+ const database = new db_1.default(localdb, remotedb, __classPrivateFieldGet(this, _ProlinkNetwork_deviceManager, "f"), __classPrivateFieldGet(this, _ProlinkNetwork_logger, "f"));
7054
7103
  // Create controller service
7055
7104
  const control = new control_1.default(__classPrivateFieldGet(this, _ProlinkNetwork_beatSocket, "f"), vcdj);
7056
7105
  __classPrivateFieldSet(this, _ProlinkNetwork_state, types_1.NetworkState.Connected, "f");
@@ -10801,6 +10850,15 @@ class RemoteDatabase {
10801
10850
  __classPrivateFieldSet(this, _RemoteDatabase_deviceManager, deviceManager, "f");
10802
10851
  __classPrivateFieldSet(this, _RemoteDatabase_hostDevice, hostDevice, "f");
10803
10852
  }
10853
+ /**
10854
+ * The device we introduce ourselves as when opening a remote database
10855
+ * connection. CDJs only answer queries from a host announcing an ID in the
10856
+ * 1-6 range, so callers need to read this before deciding a remote lookup is
10857
+ * even possible.
10858
+ */
10859
+ get hostDevice() {
10860
+ return __classPrivateFieldGet(this, _RemoteDatabase_hostDevice, "f");
10861
+ }
10804
10862
  /**
10805
10863
  * Gets the remote database query interface for the given device.
10806
10864
  *