djay Pro adapter¶
Status: v0.2 (Phase 15, Apr 2026)
Implements: OPEN-02 (djay side)
Code: apps/open_dj/adapters/djay.py
Licence: Apache-2.0.
Scope¶
Clean-room adapter between Algoriddim djay Pro's MediaLibrary.db (TSAF
blobs + SQLite) and the open-dj v0.2 document shape. Export is read-only
in v0.2; the TSAF reader surface is still evolving in Phase 4, so cue
coverage is opportunistic.
The hot path (build_library) takes plain DjayTrackInput /
DjayPlaylistInput dataclass iterables. Live-DB export opens the
MediaLibrary.db via apps.shared.djay_db.iter_tracks /
iter_playlists, which connects in true read-only mode
(mode=ro&immutable=1) so there is no risk of writing through the reader.
Write path is deferred. Mutations land through Phase 4's cautious
cue / beatgrid writers plus apps.sync.apply_ratings for ratings; Phase
16 wires adapter-driven imports on top of those foundations.
Capabilities (open-dj fields)¶
| open-dj field | support | notes |
|---|---|---|
track_id |
lossless | Computed by compute_track_id_with_tier over isrc / fingerprint / duration+size / absolute_path+mtime. |
file_path |
lossless | DjayTrack.file_path; empty string when the track is streaming-only. |
title |
lossless | |
artists[] |
lossy | djay stores a single string; the adapter splits on , and strips. |
isrc |
lossless | Normalised via normalise_isrc. |
duration_ms |
lossless | Derived from duration_s * 1000. |
size_bytes |
lossless | When surfaced by the reader. |
bpm |
lossless | Wrapped with source = "djay" provenance. |
key |
lossless | Wrapped when present on the input. |
rating |
lossless | Wrapped with provenance; 0 values are dropped (djay uses 0 for unrated). |
vendor_ids.djay |
lossless | Original uuid preserved verbatim. |
content_hash |
lossless / synthetic | Uses content_hash_hex when supplied; otherwise synthesises a deterministic sha256 from (size_bytes, mtime, duration_ms, file_path) and flags the track with x_content_hash_mode = "inferred". |
x_djay_streaming |
lossless | Emitted as true for tracks where is_local == False (SoundCloud / Tidal / Beatport Link items). |
cue_points[] |
read-only | Emitted when the caller provides them; tagged with source = "djay" in provenance. |
playlists[] |
lossless | Parent / child edges preserved as djay_pl_<uuid>. |
Authoritative source:
apps/open_dj/adapters/djay.py.
Export entry points¶
build_library(tracks, playlists, *, include_cues=True)-- pure, dataclass-driven, returns anExportResult.export_library(source_path=..., out_path=..., include_cues=True)-- live-DB wrapper. Iterates the djay DB viaapps.shared.djay_db(which enforcesmode=ro&immutable=1) and writes canonical JSON viaopen_dj.canon.to_canonical_byteswhenout_pathis provided.
Safety¶
The adapter itself is export-only; no live djay DB is ever mutated by code in this module. Any future write path will go through the same cautious-writer stack that guards ratings + cues today:
- pgrep gate on the djay Pro process.
- Backup of
MediaLibrary.dbbefore mutation. - Reversal log under
.planning/adapters/djay/reversal-log/(gitignored). - Dry-run-first diff preview.
- Schema-version check against the pinned TSAF range.
- Per-row verify after write.
Known gaps¶
- Write path is deferred (v0.3 / Phase 16). Ratings import reuses
apps.sync.apply_ratings; cue / beatgrid writes go through the Phase 4 cautious writers when wired. - Cue coverage depends on the TSAF reader surface; some binary variants still decode to opaque extension blobs.
- Streaming-only tracks (SoundCloud, Tidal, Beatport Link) carry
x_djay_streaming = trueand an emptyfile_path; most round-trips into Rekordbox / Serato / Traktor drop them with a warning (D6). - Real-world sign-off on a production
MediaLibrary.dbis Phase 16 work.