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_USBEngine 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.
| 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 |
pip install rb2engineRequires 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# Check your setup and what's on a stick — always safe to run first
rb2engine doctor /Volumes/MY_USB
# Look at what rekordbox recorded — reads only, writes nothing
rb2engine inspect /Volumes/MY_USB
# Convert
rb2engine convert /Volumes/MY_USB
# Confirm the conversion is faithful, track by track
rb2engine verify /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-artworkA conversion of a full stick takes minutes, so convert reports each phase as
it goes:
reading tracks ▰▰▰▰▰▰▰▰▰▰▰▰▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱ 30%
Progress goes to stderr and switches itself off when stderr is redirected or
--log-json is set, so piping stays clean.
| Command | Writes? | What it does |
|---|---|---|
convert |
yes, only inside Engine Library/ |
Builds the Engine library from the rekordbox export |
inspect |
no | Dumps what rekordbox recorded — tracks, playlists, cues, grids. --json for machine output |
verify |
no | Decodes the library you just wrote and diffs it against the source |
doctor |
no | Versions, supported schemas, and a read of the drive |
Exit codes (all commands): 0 all good · 1 finished with something worth
your attention — tracks skipped on convert, discrepancies on verify · 2
fatal, nothing usable written.
Every convert writes Engine Library/rb2engine-report.json listing what
converted, what was skipped and why, and any dropped cues or loops.
verify is the answer to "did that actually work?". Instead of opening Engine
and spot-checking a few tracks, it decodes the m.db that was written and
compares it against a fresh parse of the source — at sample granularity:
- beatgrid marker positions
- hot-cue pad number, position, ARGB colour and label
- loop in/out points
- track metadata and that each
pathresolves to a real file - playlist order, reconstructed from Engine's linked list
- artwork counts
rb2engine verify /Volumes/MY_USB # whole library
rb2engine verify /Volumes/MY_USB --sample 50 # first 50 tracks (much faster over USB)Exit 0 if everything matches, 1 if it finds discrepancies (each one listed
with track, field, expected and actual), 2 if it could not verify at all.
doctor first. It is read-only and tells you what rb2engine sees:
rb2engine doctor # versions and supported schemas
rb2engine doctor /Volumes/MY_USB # + what's actually on the drive
rb2engine doctor --engine-db path/to/m.db # is this schema supported?It reports the tool and dependency versions, which Engine schemas are bundled, the drive layout, and — if the drive already has a library — its schema and UUID. An unsupported schema is named explicitly along with how to add it, rather than failing with a bare error. See docs/TROUBLESHOOTING.md.
| 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 (see Which schema gets written below) |
--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 |
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.
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.
- Engine DJ: schema
3.0.1and3.0.2(Engine DJ 4.x). - rekordbox: exports from rekordbox 5, 6 and 7.
Never guessed from the app version — the same Engine build was observed running
3.0.1 on the desktop library and 3.0.2 on a USB stick. In precedence order:
--target-schema, if you pass it- the schema already on the drive — a
3.0.2stick stays3.0.2 - your own Engine desktop library, if one is readable on this machine
3.0.1as a conservative fallback
The fallback is the oldest supported version on purpose. Engine migrates an
older schema upward — a 3.0.1 stick was migrated in place to 3.0.2 by
Engine DJ 4.3.0 — but there is no downgrade path, so writing the newest version
to a fresh stick could hand an older Engine something it cannot open.
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.
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.
uv sync
uv run pytest # ~680 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_stickThe 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.
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.