Convert a rekordbox USB export into an Engine DJ library, in place, without duplicating your music
Project description
rb2engine
Convert a rekordbox USB export into an Engine DJ library — on the same stick, in place, without duplicating a single audio file.
Plug the result into Denon/Engine OS gear and your tracks, playlists, beatgrids, hot cues, loops and artwork are there. The Pioneer side keeps working exactly as before, because nothing on it is touched.
rb2engine convert /Volumes/MY_USB
Why this exists
Engine DJ can already import a rekordbox stick — but its export copies your audio into Engine Library/Music/. On a full stick that means a second copy of your whole library. A 44 GB library needs 44 GB free, and usually there isn't.
rb2engine writes only a database. Your music stays exactly where rekordbox put it, and both systems index the same files:
USB STICK
├── Contents/ ← your audio, untouched, referenced by BOTH
├── PIONEER/ ← rekordbox's export, never modified
└── Engine Library/
└── Database2/m.db ← the only thing rb2engine writes
Verified on a real 3,665-track library: 3,620 tracks, 47 playlists, ~500 MB of database against a 44 GB library — instead of a second 44 GB.
What transfers
| Tracks | title, artist, album, genre, label, comment, composer, remixer, year, track/disc number, BPM, key, rating, bitrate, length |
| Playlists | full folder hierarchy and track order |
| Beatgrids | including manually adjusted grids and tempo changes |
| Hot cues | same pad numbers (A–H), with custom colours and names |
| Memory cues | fill remaining pads in chronological order |
| Saved loops | in/out points, into Engine's separate loop slots |
| Album art | extracted from your files' embedded tags, deduplicated |
Install
pip install rb2engine
Requires Python 3.11+. Works on macOS, Windows and Linux.
From source:
git clone https://github.com/jrgutier/rb2engine
cd rb2engine
uv sync
uv run rb2engine --help
Usage
# Look at what's on a stick — reads only, writes nothing
rb2engine inspect /Volumes/MY_USB
# Convert
rb2engine convert /Volumes/MY_USB
# See what would happen without writing
rb2engine convert /Volumes/MY_USB --dry-run
# Skip artwork (much faster — artwork reads every audio file)
rb2engine convert /Volumes/MY_USB --no-artwork
Exit codes: 0 clean · 1 converted, but some tracks were skipped · 2 fatal, nothing usable written.
Every run writes Engine Library/rb2engine-report.json listing what converted, what was skipped and why, and any dropped cues or loops.
Options
| Flag | Purpose |
|---|---|
--dry-run |
Parse and map, write nothing |
--no-artwork |
Skip cover-art extraction |
--target-schema 3.0.2 |
Force an Engine schema version (default: read from the existing database) |
--database-uuid UUID |
Override the library UUID (default: reuse the existing one) |
--report PATH |
Write the JSON report elsewhere |
-v / -vv |
More logging · --log-json for machine-readable logs |
Safety
rb2engine is built to be non-destructive, and the guarantee is tested rather than asserted:
- Only
Engine Library/is ever written. A test walks the entire drive before and after a conversion and fails if anything else changed. On the real 3,665-track stick, all 24,247 files outsideEngine Library/were byte-identical afterwards, with both.pdbchecksums unchanged. - Your audio is never opened for writing. Artwork extraction is read-only, and a test hashes source files before and after to prove it.
- The database swap is atomic against process failure: the library is built elsewhere and moved into place with
os.replace, so a crash leaves your previousm.dbintact rather than a half-written one. - Engine's own files survive.
hm.db(your play history),sm.db,stm.db,Music/,Artwork/andOverviewData/are preserved. The library UUID is carried forward so Engine's history stays linked.
Still: it's a DJ library. Back it up before you point a new tool at it.
Things worth knowing
Re-running rebuilds from scratch. The Engine library is derived data. Edits made in Engine to the converted library are not preserved — re-export from rekordbox, re-run, and you get a clean library.
Two rekordbox habits Engine can't represent. Both are handled rather than fatal, and both appear in the report:
- Duplicate playlist names in one folder. rekordbox allows it; Engine's schema doesn't. Duplicates get a numeric suffix (
Setlist (2)), assigned deterministically so re-runs are stable. - The same track twice in one playlist. rekordbox allows it; Engine doesn't. The first occurrence is kept.
More than 8 cues on a track. Engine has 8 pads. Hot cues keep their original pad; memory cues fill what's left chronologically; anything beyond 8 is dropped and itemised per-track in the report. Loops go to Engine's separate 8 loop slots, so a looped hot cue frees its pad.
Waveforms aren't generated. Engine builds those itself when it analyses a track. Beatgrids, cues and loops are what has to survive the conversion, and they do.
Supported versions
- Engine DJ: schema
3.0.1and3.0.2(Engine DJ 4.x). The target version is read from your existing database rather than guessed — the same Engine build can run different schema versions on the desktop and on a stick. - rekordbox: exports from rekordbox 5, 6 and 7.
If your stick uses a schema rb2engine doesn't know, it refuses to write and tells you, rather than producing a database Engine might silently misread. Adding a version is a schema capture plus one line — see src/rb2engine/writer/ddl/README.md.
How it works
export.pdb ────┐
├─→ Source IR ─→ mapper ─→ Engine IR ─→ Engine Library/Database2/m.db
ANLZ .DAT/.EXT ┘
reader/parsesexport.pdb(DeviceSQL, viaconstruct) and the ANLZ analysis files (beatgrids, cues, loops)ir.py/ir_engine.pydecouple the two sides — no rekordbox type reaches the writer, no Engine type reaches the readermapper/applies the semantics: key ordinals, cue-to-pad policy, beatgrid compressionwriter/builds the SQLite database from schema DDL captured from Engine's own output
Positions are integer sample counts throughout; the millisecond conversion happens exactly once, at the reader boundary.
Development
uv sync
uv run pytest # ~430 tests
uv run ruff check src/ tests/
uv run mypy src/
Tests marked real_stick need an actual rekordbox USB mounted and are skipped otherwise:
uv run pytest -m real_stick
The blob codecs are validated by byte-identity against Engine's own output: encode(decode(blob)) == blob for beatData, quickCues, loops and trackData, using a database Engine itself wrote. That check is what makes the encoders trustworthy.
License
MIT — see LICENSE.
All runtime dependencies are permissively licensed (MIT / BSD). See NOTICE for attribution to crate-digger and libdjinterop, whose format documentation made this possible — neither is included or linked, and the Engine schema is captured from Engine's own databases.
rb2engine is an independent interoperability tool, unaffiliated with AlphaTheta/Pioneer DJ or inMusic/Denon DJ.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rb2engine-0.1.0.tar.gz.
File metadata
- Download URL: rb2engine-0.1.0.tar.gz
- Upload date:
- Size: 74.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8603760f7c74d48cdc8e459b616a6af498dfc9a4d883506b813e8b885e4a82e6
|
|
| MD5 |
009b1802b49612d0a79ae90399a28517
|
|
| BLAKE2b-256 |
10324d138a75a97e0e8162d63640a59145d6d18e4ef651b3d8fd8d9e8ffece28
|
Provenance
The following attestation bundles were made for rb2engine-0.1.0.tar.gz:
Publisher:
release.yml on jrgutier/rb2engine
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rb2engine-0.1.0.tar.gz -
Subject digest:
8603760f7c74d48cdc8e459b616a6af498dfc9a4d883506b813e8b885e4a82e6 - Sigstore transparency entry: 2249612134
- Sigstore integration time:
-
Permalink:
jrgutier/rb2engine@bb0527c947ed4f7c43b3238ca7c42f8786a771ad -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/jrgutier
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@bb0527c947ed4f7c43b3238ca7c42f8786a771ad -
Trigger Event:
push
-
Statement type:
File details
Details for the file rb2engine-0.1.0-py3-none-any.whl.
File metadata
- Download URL: rb2engine-0.1.0-py3-none-any.whl
- Upload date:
- Size: 93.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3dc90b004433003962deb62fafb70e6dd98c87df8863e21739d6886e9c186aaa
|
|
| MD5 |
30fc417d376b5d12c5828ef9fa9122f0
|
|
| BLAKE2b-256 |
c3dfd93a26b2b7de5df290f430e6b27373022d5db6bc8501ccc5fd1af314be00
|
Provenance
The following attestation bundles were made for rb2engine-0.1.0-py3-none-any.whl:
Publisher:
release.yml on jrgutier/rb2engine
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rb2engine-0.1.0-py3-none-any.whl -
Subject digest:
3dc90b004433003962deb62fafb70e6dd98c87df8863e21739d6886e9c186aaa - Sigstore transparency entry: 2249612194
- Sigstore integration time:
-
Permalink:
jrgutier/rb2engine@bb0527c947ed4f7c43b3238ca7c42f8786a771ad -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/jrgutier
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@bb0527c947ed4f7c43b3238ca7c42f8786a771ad -
Trigger Event:
push
-
Statement type: