rekordbox-connect 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # rekordbox-connect
2
+
3
+ Library for reading the Rekordbox SQLCipher-encrypted database and emitting events when the library or play history changes. Built to decouple Rekordbox integration out of NowPlaying into a standalone, reusable package.
4
+
5
+ ## Features
6
+
7
+ - Polls the Rekordbox database file for changes (mtime-based)
8
+ - Typed events: `ready`, `db-changed`, `tracks`, `history`, `error`
9
+ - Incremental history emission (only new plays after start)
10
+ - Uses `better-sqlite3-multiple-ciphers` for SQLCipher support
11
+ - Automatically reads database path and password from Rekordbox `options.json`
12
+ - Decrypts database password using Blowfish encryption
13
+ - Configurable DB path, password, polling interval, track max rows, and history max rows
14
+
15
+ ## Quick Start
16
+
17
+ ```ts
18
+ import { RekordboxConnect } from 'rekordbox-connect';
19
+
20
+ const rb = new RekordboxConnect({ pollIntervalMs: 2000, maxRows: 5000, historyMaxRows: 100 });
21
+
22
+ rb.on('ready', (info) => {
23
+ console.log('Rekordbox ready:', info);
24
+ });
25
+
26
+ rb.on('db-changed', (change) => {
27
+ console.log('Database changed:', change);
28
+ });
29
+
30
+ rb.on('tracks', (payload) => {
31
+ console.log('Loaded tracks:', payload.count);
32
+ });
33
+
34
+ rb.on('history', (payload) => {
35
+ console.log('New plays:', payload.count);
36
+ });
37
+
38
+ rb.start();
39
+
40
+ // later
41
+ // rb.stop();
42
+ ```
43
+
44
+ ## API
45
+
46
+ ### `new RekordboxConnect(options?)`
47
+
48
+ Options:
49
+
50
+ - `dbPath?: string` — Absolute path to the Rekordbox DB. If omitted, read from Rekordbox `options.json`.
51
+ - `dbPassword?: string` — Database password for SQLCipher decryption. If omitted, read and decrypted from Rekordbox `options.json`.
52
+ - `pollIntervalMs?: number` — Milliseconds between file mtime polls. Default `2000`.
53
+ - `maxRows?: number` — Limit for `tracks` emission. Default `5000`.
54
+ - `historyMaxRows?: number` — Limit for `history` emission per poll. Default `100`.
55
+
56
+ Methods:
57
+
58
+ - `start()` — Locate/open DB, emit initial `ready`, load tracks/history, and begin polling.
59
+ - `stop()` — Stop polling and close DB handle.
60
+
61
+ Events:
62
+
63
+ - `ready` — `{ dbPath, modifiedTime }` when the DB is opened.
64
+ - `db-changed` — `{ dbPath, previousModifiedTime, modifiedTime }` when file mtime increases.
65
+ - `tracks` — `{ dbPath, count, rows }` with a slice of tracks (schema best-effort per Rekordbox version).
66
+ - `history` — `{ dbPath, count, rows, lastRowId }` with new plays since the previous poll.
67
+ - `error` — `(error)` for any recoverable errors.
68
+
69
+ ## Notes
70
+
71
+ - Rekordbox stores its library as a SQLCipher-encrypted SQLite database. By default, the library reads both the database path and encrypted password from Rekordbox's `options.json` file (located at `~/Library/Application Support/Pioneer/rekordboxAgent/storage/options.json` on macOS).
72
+ - The password is decrypted using Blowfish encryption before opening the database.
73
+ - If `dbPath` and `dbPassword` are not provided, the library automatically locates and reads them from `options.json`.
74
+ - Track schema varies across Rekordbox versions. When unknown, `tracks` emits raw rows from the detected song table (e.g., `djmdSong`). Map to your internal types as needed.
75
+ - History polling is rowid-based: on startup the cursor seeds to the current max rowid, so `history` only emits new plays after the library starts.
76
+
77
+ ## License
78
+
79
+ See repository license.
package/dist/db.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ import type { RekordboxHistoryPayload, RekordboxTracksPayload } from './types';
2
+ export declare class RekordboxDb {
3
+ private readonly dbPath;
4
+ private readonly password;
5
+ private db?;
6
+ constructor(dbPath: string, password: string);
7
+ open(): void;
8
+ close(): void;
9
+ loadTracks(maxRows?: number): RekordboxTracksPayload | undefined;
10
+ seedHistoryCursor(): number | undefined;
11
+ loadNewHistory(sinceRowId: number | undefined, maxRows?: number): RekordboxHistoryPayload | undefined;
12
+ }
package/dist/db.js ADDED
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.RekordboxDb = void 0;
7
+ const better_sqlite3_multiple_ciphers_1 = __importDefault(require("better-sqlite3-multiple-ciphers"));
8
+ const node_fs_1 = __importDefault(require("node:fs"));
9
+ const DEFAULT_MAX_ROWS = 5000;
10
+ const DEFAULT_HISTORY_ROWS = 100;
11
+ // Known Rekordbox 6 table names
12
+ const HISTORY_TABLE = 'djmdSongHistory';
13
+ const CONTENT_TABLE = 'djmdContent';
14
+ class RekordboxDb {
15
+ constructor(dbPath, password) {
16
+ this.dbPath = dbPath;
17
+ this.password = password;
18
+ }
19
+ open() {
20
+ this.close();
21
+ if (!node_fs_1.default.existsSync(this.dbPath)) {
22
+ throw new Error(`Rekordbox database not found at: ${this.dbPath}`);
23
+ }
24
+ this.db = new better_sqlite3_multiple_ciphers_1.default(this.dbPath, { readonly: true });
25
+ // Configure SQLCipher
26
+ this.db.pragma(`cipher='sqlcipher'`);
27
+ this.db.pragma(`legacy=4`);
28
+ this.db.pragma(`key='${this.password}'`);
29
+ // Enable reading uncommitted data from WAL (see writes from Rekordbox)
30
+ this.db.pragma(`read_uncommitted=true`);
31
+ // Test the connection
32
+ try {
33
+ this.db.pragma('cipher_version');
34
+ }
35
+ catch (error) {
36
+ this.db.close();
37
+ throw new Error('Failed to decrypt Rekordbox database. Invalid password?');
38
+ }
39
+ }
40
+ close() {
41
+ if (!this.db)
42
+ return;
43
+ try {
44
+ this.db.close();
45
+ }
46
+ catch {
47
+ // ignore close errors
48
+ }
49
+ this.db = undefined;
50
+ }
51
+ loadTracks(maxRows) {
52
+ if (!this.db)
53
+ return;
54
+ const limit = maxRows ?? DEFAULT_MAX_ROWS;
55
+ // Query djmdContent with joined metadata (same structure as history query)
56
+ const query = `
57
+ SELECT
58
+ c.ID AS id,
59
+ c.FolderPath AS filePath,
60
+ c.Title AS title,
61
+ c.Subtitle AS subTitle,
62
+ a.Name AS artist,
63
+ c.ImagePath AS imagePath,
64
+ c.BPM AS bpm,
65
+ c.Rating AS rating,
66
+ c.ReleaseDate AS releaseDate,
67
+ c.Length AS length,
68
+ c.ColorID AS colorId,
69
+ c.Commnt AS comment,
70
+ al.Name AS album,
71
+ la.Name AS label,
72
+ ge.Name AS genre,
73
+ k.ScaleName AS key,
74
+ rmx.Name AS remixer
75
+ FROM ${CONTENT_TABLE} AS c
76
+ LEFT JOIN djmdArtist AS a ON c.ArtistID = a.ID
77
+ LEFT JOIN djmdArtist AS rmx ON c.RemixerID = rmx.ID
78
+ LEFT JOIN djmdAlbum AS al ON c.AlbumID = al.ID
79
+ LEFT JOIN djmdLabel AS la ON c.LabelID = la.ID
80
+ LEFT JOIN djmdGenre AS ge ON c.GenreID = ge.ID
81
+ LEFT JOIN djmdKey AS k ON c.KeyID = k.ID
82
+ LIMIT @limit
83
+ `;
84
+ try {
85
+ const rows = this.db.prepare(query).all({ limit });
86
+ return { dbPath: this.dbPath, count: rows.length, rows };
87
+ }
88
+ catch {
89
+ return { dbPath: this.dbPath, count: 0, rows: [] };
90
+ }
91
+ }
92
+ seedHistoryCursor() {
93
+ if (!this.db)
94
+ return undefined;
95
+ try {
96
+ const row = this.db.prepare(`SELECT MAX(rowid) as maxRowId FROM ${HISTORY_TABLE}`).get();
97
+ return row?.maxRowId;
98
+ }
99
+ catch {
100
+ return undefined;
101
+ }
102
+ }
103
+ loadNewHistory(sinceRowId, maxRows) {
104
+ if (!this.db)
105
+ return undefined;
106
+ const limit = maxRows ?? DEFAULT_HISTORY_ROWS;
107
+ // Query history with joined content and metadata tables
108
+ const query = `
109
+ SELECT
110
+ h.rowid AS rowid,
111
+ h.ID AS id,
112
+ h.created_at,
113
+ c.FolderPath AS filePath,
114
+ c.Title AS title,
115
+ c.Subtitle AS subTitle,
116
+ a.Name AS artist,
117
+ c.ImagePath AS imagePath,
118
+ c.BPM AS bpm,
119
+ c.Rating AS rating,
120
+ c.ReleaseDate AS releaseDate,
121
+ c.Length AS length,
122
+ c.ColorID AS colorId,
123
+ c.Commnt AS comment,
124
+ al.Name AS album,
125
+ la.Name AS label,
126
+ ge.Name AS genre,
127
+ k.ScaleName AS key,
128
+ rmx.Name AS remixer
129
+ FROM ${HISTORY_TABLE} AS h
130
+ JOIN ${CONTENT_TABLE} AS c ON h.ContentID = c.ID
131
+ LEFT JOIN djmdArtist AS a ON c.ArtistID = a.ID
132
+ LEFT JOIN djmdArtist AS rmx ON c.RemixerID = rmx.ID
133
+ LEFT JOIN djmdAlbum AS al ON c.AlbumID = al.ID
134
+ LEFT JOIN djmdLabel AS la ON c.LabelID = la.ID
135
+ LEFT JOIN djmdGenre AS ge ON c.GenreID = ge.ID
136
+ LEFT JOIN djmdKey AS k ON c.KeyID = k.ID
137
+ WHERE h.rowid > @since
138
+ ORDER BY h.rowid ASC
139
+ LIMIT @limit
140
+ `;
141
+ try {
142
+ const rows = this.db
143
+ .prepare(query)
144
+ .all({ since: sinceRowId ?? 0, limit });
145
+ const lastRowId = rows.length > 0 ? rows[rows.length - 1].rowid : sinceRowId;
146
+ return { dbPath: this.dbPath, count: rows.length, rows, lastRowId };
147
+ }
148
+ catch {
149
+ return { dbPath: this.dbPath, count: 0, rows: [], lastRowId: sinceRowId };
150
+ }
151
+ }
152
+ }
153
+ exports.RekordboxDb = RekordboxDb;
154
+ //# sourceMappingURL=db.js.map
package/dist/db.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { RekordboxOptions } from './types';
2
+ export declare function detectRekordboxDbPath(explicit?: string): string | undefined;
3
+ /**
4
+ * Get the default options.json path for the current platform
5
+ */
6
+ export declare function getOptionsPath(): string;
7
+ /**
8
+ * Read and parse Rekordbox options.json
9
+ */
10
+ export declare function readRekordboxOptions(): RekordboxOptions;
11
+ /**
12
+ * Get database path and decrypted password from options.json
13
+ */
14
+ export declare function getRekordboxConfig(explicitDbPath?: string, explicitPassword?: string): {
15
+ dbPath: string;
16
+ password: string;
17
+ };
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.detectRekordboxDbPath = detectRekordboxDbPath;
7
+ exports.getOptionsPath = getOptionsPath;
8
+ exports.readRekordboxOptions = readRekordboxOptions;
9
+ exports.getRekordboxConfig = getRekordboxConfig;
10
+ const egoroof_blowfish_1 = __importDefault(require("egoroof-blowfish"));
11
+ const node_fs_1 = __importDefault(require("node:fs"));
12
+ const node_os_1 = __importDefault(require("node:os"));
13
+ const node_path_1 = __importDefault(require("node:path"));
14
+ const types_1 = require("./types");
15
+ const SQLITE_EXTENSIONS = ['.db', '.sqlite'];
16
+ function detectRekordboxDbPath(explicit) {
17
+ if (explicit)
18
+ return explicit;
19
+ const candidates = [];
20
+ const home = node_os_1.default.homedir();
21
+ if (process.platform === 'darwin') {
22
+ candidates.push(node_path_1.default.join(home, 'Library', 'Application Support', 'Pioneer', 'rekordbox'), node_path_1.default.join(home, 'Library', 'Pioneer', 'rekordbox'));
23
+ }
24
+ else if (process.platform === 'win32') {
25
+ const appData = process.env.APPDATA || node_path_1.default.join(home, 'AppData', 'Roaming');
26
+ const localAppData = process.env.LOCALAPPDATA || node_path_1.default.join(home, 'AppData', 'Local');
27
+ candidates.push(node_path_1.default.join(appData, 'Pioneer', 'rekordbox'), node_path_1.default.join(localAppData, 'Pioneer', 'rekordbox'));
28
+ }
29
+ else {
30
+ // Best-effort guess for Linux/Wine environments
31
+ candidates.push(node_path_1.default.join(home, '.Pioneer', 'rekordbox'), node_path_1.default.join(home, '.wine', 'drive_c', 'Pioneer', 'rekordbox'));
32
+ }
33
+ const dbFiles = discoverDbFiles(candidates);
34
+ if (dbFiles.length === 0)
35
+ return undefined;
36
+ const newest = dbFiles
37
+ .map((f) => {
38
+ try {
39
+ return { f, mtime: node_fs_1.default.statSync(f).mtimeMs };
40
+ }
41
+ catch {
42
+ return { f, mtime: 0 };
43
+ }
44
+ })
45
+ .sort((a, b) => b.mtime - a.mtime)[0];
46
+ return newest?.f;
47
+ }
48
+ function discoverDbFiles(dirs) {
49
+ const results = [];
50
+ for (const dir of dirs) {
51
+ try {
52
+ const entries = node_fs_1.default.readdirSync(dir);
53
+ for (const name of entries) {
54
+ const ext = node_path_1.default.extname(name).toLowerCase();
55
+ if (SQLITE_EXTENSIONS.includes(ext) || name.toLowerCase().includes('rekordbox') && ext) {
56
+ results.push(node_path_1.default.join(dir, name));
57
+ }
58
+ }
59
+ }
60
+ catch {
61
+ // directory may not exist or be unreadable
62
+ }
63
+ }
64
+ return results;
65
+ }
66
+ /**
67
+ * Get the default options.json path for the current platform
68
+ */
69
+ function getOptionsPath() {
70
+ const home = node_os_1.default.homedir();
71
+ if (process.platform === 'darwin') {
72
+ return node_path_1.default.join(home, 'Library', 'Application Support', 'Pioneer', 'rekordboxAgent', 'storage', 'options.json');
73
+ }
74
+ else if (process.platform === 'win32') {
75
+ const appData = process.env.APPDATA || node_path_1.default.join(home, 'AppData', 'Roaming');
76
+ return node_path_1.default.join(appData, 'Pioneer', 'rekordboxAgent', 'storage', 'options.json');
77
+ }
78
+ else {
79
+ // Best-effort guess for Linux/Wine
80
+ return node_path_1.default.join(home, '.Pioneer', 'rekordboxAgent', 'storage', 'options.json');
81
+ }
82
+ }
83
+ /**
84
+ * Read and parse Rekordbox options.json
85
+ */
86
+ function readRekordboxOptions() {
87
+ const optionsPath = getOptionsPath();
88
+ if (!node_fs_1.default.existsSync(optionsPath)) {
89
+ throw new Error(`Rekordbox options.json not found at: ${optionsPath}`);
90
+ }
91
+ const data = node_fs_1.default.readFileSync(optionsPath, 'utf8');
92
+ return JSON.parse(data);
93
+ }
94
+ /**
95
+ * Get database path and decrypted password from options.json
96
+ */
97
+ function getRekordboxConfig(explicitDbPath, explicitPassword) {
98
+ // Use custom path/password if provided
99
+ if (explicitDbPath && explicitPassword) {
100
+ return {
101
+ dbPath: explicitDbPath,
102
+ password: explicitPassword,
103
+ };
104
+ }
105
+ const options = readRekordboxOptions();
106
+ const dbPathOption = options.options.find((opt) => opt[0] === 'db-path');
107
+ const passwordOption = options.options.find((opt) => opt[0] === 'dp');
108
+ if (!dbPathOption || !passwordOption) {
109
+ throw new Error('Could not find database path or password in Rekordbox options');
110
+ }
111
+ // Decrypt password using Blowfish
112
+ const bf = new egoroof_blowfish_1.default(types_1.REKORDBOX_MAGIC, egoroof_blowfish_1.default.MODE.ECB, egoroof_blowfish_1.default.PADDING.PKCS5);
113
+ const encryptedPassword = Buffer.from(passwordOption[1], 'base64');
114
+ const decryptedPassword = bf
115
+ .decode(encryptedPassword, egoroof_blowfish_1.default.TYPE.STRING)
116
+ .trim();
117
+ return {
118
+ dbPath: explicitDbPath || dbPathOption[1],
119
+ password: explicitPassword || decryptedPassword,
120
+ };
121
+ }
122
+ //# sourceMappingURL=detectDb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detectDb.js","sourceRoot":"","sources":["../src/detectDb.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { RekordboxConnect } from './rekordboxConnect';
2
+ export type { RekordboxConnectEvents, RekordboxConnectOptions, RekordboxHistoryPayload, RekordboxOptions, RekordboxReadyInfo, RekordboxTracksPayload } from './types';
3
+ export { REKORDBOX_MAGIC } from './types';
4
+ export { detectRekordboxDbPath, getRekordboxConfig, getOptionsPath, readRekordboxOptions } from './detectDb';
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readRekordboxOptions = exports.getOptionsPath = exports.getRekordboxConfig = exports.detectRekordboxDbPath = exports.REKORDBOX_MAGIC = exports.RekordboxConnect = void 0;
4
+ var rekordboxConnect_1 = require("./rekordboxConnect");
5
+ Object.defineProperty(exports, "RekordboxConnect", { enumerable: true, get: function () { return rekordboxConnect_1.RekordboxConnect; } });
6
+ var types_1 = require("./types");
7
+ Object.defineProperty(exports, "REKORDBOX_MAGIC", { enumerable: true, get: function () { return types_1.REKORDBOX_MAGIC; } });
8
+ var detectDb_1 = require("./detectDb");
9
+ Object.defineProperty(exports, "detectRekordboxDbPath", { enumerable: true, get: function () { return detectDb_1.detectRekordboxDbPath; } });
10
+ Object.defineProperty(exports, "getRekordboxConfig", { enumerable: true, get: function () { return detectDb_1.getRekordboxConfig; } });
11
+ Object.defineProperty(exports, "getOptionsPath", { enumerable: true, get: function () { return detectDb_1.getOptionsPath; } });
12
+ Object.defineProperty(exports, "readRekordboxOptions", { enumerable: true, get: function () { return detectDb_1.readRekordboxOptions; } });
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,uDAAsD;AAA7C,oHAAA,gBAAgB,OAAA;AASzB,iCAA0C;AAAjC,wGAAA,eAAe,OAAA;AACxB,uCAKoB;AAJhB,iHAAA,qBAAqB,OAAA;AACrB,8GAAA,kBAAkB,OAAA;AAClB,0GAAA,cAAc,OAAA;AACd,gHAAA,oBAAoB,OAAA"}
@@ -0,0 +1,20 @@
1
+ import type { RekordboxConnectEvents, RekordboxConnectOptions, TypedEmitter } from './types';
2
+ declare const RekordboxConnect_base: {
3
+ new (): TypedEmitter;
4
+ };
5
+ export declare class RekordboxConnect extends RekordboxConnect_base {
6
+ private readonly pollIntervalMs;
7
+ private readonly historyMaxRows?;
8
+ private readonly explicitDbPath?;
9
+ private readonly explicitDbPassword?;
10
+ private dbPath?;
11
+ private timer?;
12
+ private db?;
13
+ private lastHistoryRowId?;
14
+ constructor(opts?: RekordboxConnectOptions);
15
+ start(): void;
16
+ stop(): void;
17
+ private pollOnce;
18
+ private loadHistorySafe;
19
+ }
20
+ export type { RekordboxConnectEvents, RekordboxConnectOptions };
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.RekordboxConnect = void 0;
7
+ const node_events_1 = __importDefault(require("node:events"));
8
+ const db_1 = require("./db");
9
+ const detectDb_1 = require("./detectDb");
10
+ class RekordboxConnect extends node_events_1.default {
11
+ constructor(opts = {}) {
12
+ super();
13
+ this.pollIntervalMs = opts.pollIntervalMs ?? 2000;
14
+ this.explicitDbPath = opts.dbPath;
15
+ this.explicitDbPassword = opts.dbPassword;
16
+ this.historyMaxRows = opts.historyMaxRows;
17
+ }
18
+ start() {
19
+ try {
20
+ // Get database path and password from options.json or explicit config
21
+ const { dbPath, password } = (0, detectDb_1.getRekordboxConfig)(this.explicitDbPath, this.explicitDbPassword);
22
+ this.dbPath = dbPath;
23
+ this.db = new db_1.RekordboxDb(dbPath, password);
24
+ this.db.open();
25
+ this.lastHistoryRowId = this.db.seedHistoryCursor();
26
+ this.emit('ready', { dbPath: this.dbPath });
27
+ this.timer = setInterval(() => this.pollOnce(), this.pollIntervalMs);
28
+ }
29
+ catch (err) {
30
+ this.emit('error', err instanceof Error ? err : new Error(String(err)));
31
+ }
32
+ }
33
+ stop() {
34
+ if (this.timer) {
35
+ clearInterval(this.timer);
36
+ this.timer = undefined;
37
+ }
38
+ if (this.db) {
39
+ this.db.close();
40
+ this.db = undefined;
41
+ }
42
+ }
43
+ pollOnce() {
44
+ if (!this.dbPath || !this.db)
45
+ return;
46
+ try {
47
+ this.emit('poll');
48
+ // Always poll history directly - mtime checks are unreliable with SQLite WAL mode
49
+ this.loadHistorySafe();
50
+ }
51
+ catch (err) {
52
+ this.emit('error', err instanceof Error ? err : new Error(String(err)));
53
+ }
54
+ }
55
+ loadHistorySafe() {
56
+ if (!this.db)
57
+ return;
58
+ try {
59
+ const payload = this.db.loadNewHistory(this.lastHistoryRowId, this.historyMaxRows);
60
+ if (payload && payload.count > 0) {
61
+ this.lastHistoryRowId = payload.lastRowId ?? this.lastHistoryRowId;
62
+ this.emit('history', payload);
63
+ }
64
+ }
65
+ catch (err) {
66
+ this.emit('error', err instanceof Error ? err : new Error(String(err)));
67
+ }
68
+ }
69
+ }
70
+ exports.RekordboxConnect = RekordboxConnect;
71
+ //# sourceMappingURL=rekordboxConnect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rekordboxConnect.js","sourceRoot":"","sources":["../src/rekordboxConnect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"root":["../src/db.ts","../src/detectdb.ts","../src/index.ts","../src/rekordboxconnect.ts","../src/types.ts"],"version":"5.9.3"}
@@ -0,0 +1,46 @@
1
+ import EventEmitter from 'node:events';
2
+ import type { StrictEventEmitter } from 'strict-event-emitter-types';
3
+ export type RekordboxConnectOptions = {
4
+ /** Optional absolute path to the Rekordbox SQLite database. If omitted, read from options.json. */
5
+ dbPath?: string;
6
+ /** Optional database password for SQLCipher decryption. If omitted, read from options.json. */
7
+ dbPassword?: string;
8
+ /** How frequently to poll the DB file modified time (ms). */
9
+ pollIntervalMs?: number;
10
+ /** Maximum number of tracks to fetch per emission. */
11
+ maxRows?: number;
12
+ /** Maximum number of new history rows to emit per poll. */
13
+ historyMaxRows?: number;
14
+ };
15
+ /**
16
+ * Rekordbox options.json structure (partial)
17
+ */
18
+ export interface RekordboxOptions {
19
+ options: Array<[string, string]>;
20
+ }
21
+ /**
22
+ * Magic string for Blowfish password decryption
23
+ */
24
+ export declare const REKORDBOX_MAGIC = "ZOwUlUZYqe9Rdm6j";
25
+ export type RekordboxReadyInfo = {
26
+ dbPath: string;
27
+ };
28
+ export type RekordboxTracksPayload = {
29
+ dbPath: string;
30
+ count: number;
31
+ rows: Record<string, unknown>[];
32
+ };
33
+ export type RekordboxHistoryPayload = {
34
+ dbPath: string;
35
+ count: number;
36
+ rows: Record<string, unknown>[];
37
+ lastRowId?: number;
38
+ };
39
+ export interface RekordboxConnectEvents {
40
+ ready: (info: RekordboxReadyInfo) => void;
41
+ poll: () => void;
42
+ tracks: (payload: RekordboxTracksPayload) => void;
43
+ history: (payload: RekordboxHistoryPayload) => void;
44
+ error: (err: Error) => void;
45
+ }
46
+ export type TypedEmitter = StrictEventEmitter<EventEmitter, RekordboxConnectEvents>;
package/dist/types.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.REKORDBOX_MAGIC = void 0;
4
+ /**
5
+ * Magic string for Blowfish password decryption
6
+ */
7
+ exports.REKORDBOX_MAGIC = 'ZOwUlUZYqe9Rdm6j';
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;;AAuBA;;GAEG;AACU,QAAA,eAAe,GAAG,kBAAkB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "rekordbox-connect",
3
+ "version": "1.0.0",
4
+ "description": "Library to read the Rekordbox database and emit change events",
5
+ "private": false,
6
+ "type": "commonjs",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.js",
14
+ "default": "./dist/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "scripts": {
21
+ "test": "vitest run",
22
+ "test:integration": "vitest run --config vitest.integration.config.mts",
23
+ "test:watch": "vitest",
24
+ "test:coverage": "vitest run --coverage",
25
+ "build": "tsc --build tsconfig.json",
26
+ "watch": "tsc --build tsconfig.json -w",
27
+ "clean": "rm -rf dist",
28
+ "prepublishOnly": "npm run clean && npm run build && npm test",
29
+ "release": "node .github/release.js",
30
+ "release:patch": "node .github/release.js --patch",
31
+ "release:major": "node .github/release.js --major"
32
+ },
33
+ "engines": {
34
+ "node": ">=22.0.0"
35
+ },
36
+ "dependencies": {
37
+ "better-sqlite3-multiple-ciphers": "^11.7.1",
38
+ "egoroof-blowfish": "^2.2.2",
39
+ "strict-event-emitter-types": "^2.0.0"
40
+ },
41
+ "devDependencies": {
42
+ "@types/better-sqlite3": "^7.6.13",
43
+ "@types/node": "^22.15.29",
44
+ "@vitest/coverage-v8": "^1.6.0",
45
+ "prettier": "^3.5.3",
46
+ "typescript": "^5.9.3",
47
+ "vitest": "^1.3.0"
48
+ }
49
+ }