🎧 djkr8 - Playlist Optimizer for rekordbox
Optimize rekordbox playlists for harmonic mixing using Google OR-Tools constraint programming.
Features
- ✨ Longest Path Optimization: Finds the maximum number of tracks that can be mixed together
- ⚡ Energy Flow Management: Enforces non-decreasing energy progression with max +1 increase per transition (1-5 range)
- 🎵 Harmonic Mixing: Uses the Camelot Wheel system for key compatibility
- 🚀 Energy Boost Transitions: Strategic +2 hour jumps (5A→7A) for crowd excitement, used sparingly (max 3 per set)
- 📊 Transition Quality Scoring: Intelligently weights transitions by harmonic quality (1.0 = perfect, 0.6 = energy boost)
- 🎧 Rekordbox Integration: Read playlists directly from your local Rekordbox 6/7 database (tested with v7.2.8)
- 🔊 BPM Matching: Supports direct, halftime, and doubletime BPM compatibility
- ⚙️ Configurable Strictness: STRICT, MODERATE, or RELAXED harmonic compatibility levels
- 📤 Rekordbox Export: Export results to Rekordbox XML or write directly to the Rekordbox database
- 🚀 Fast: Powered by Google OR-Tools CP-SAT solver (award-winning constraint solver)
- 📦 SDK + CLI: Use as a Python library or command-line tool
Installation
uv add djkr8
Or with pip:
pip install djkr8
Quick Start
SDK Usage
from djkr8 import PlaylistOptimizer, Track, HarmonicLevel
tracks = [
Track(id="track_001", key="8A", bpm=128),
Track(id="track_002", key="8B", bpm=130),
Track(id="track_003", key="9A", bpm=125),
]
optimizer = PlaylistOptimizer(
bpm_tolerance=10,
allow_halftime_bpm=True,
max_violation_pct=0.10,
harmonic_level=HarmonicLevel.STRICT,
)
result = optimizer.optimize(tracks)
for i, track in enumerate(result.playlist, 1):
print(f"{i}. {track.id} ({track.key}, {track.bpm} BPM)")
CLI Usage
# Basic usage
djkr8 tracks.json
# With custom settings
djkr8 tracks.json --bpm-tolerance 8 --harmonic-level moderate
# Energy flow management
djkr8 tracks.json --energy-weight 5.0 # Prioritize higher energy tracks
djkr8 tracks.json --allow-energy-drops # Disable strict non-decreasing energy constraint
# Save results to JSON
djkr8 tracks.json --output result.json
# Use with Rekordbox (v6/v7)
krate --rekordbox # List playlists
krate --rekordbox --playlist "Techno" # Optimize specific playlist
krate --rekordbox --playlist "Techno" --output r.xml # Export to Rekordbox XML
krate --rekordbox --playlist "Techno" --write-to-db # Write directly to Rekordbox DB
# Enable verbose logging
djkr8 tracks.json -v # INFO level
djkr8 tracks.json -vv # DEBUG level
Rekordbox Integration
The tool provides two ways to save your optimized playlists back to Rekordbox:
1. XML Export (Recommended)
Export the results to an XML file that can be imported into Rekordbox:
krate --rekordbox --playlist "My Playlist" --output optimized.xml
In Rekordbox:
- Go to File > Import > Import Playlist
- Select
optimized.xml - The playlist will appear in the
ROOTfolder (e.g.,My Playlist_20260115_120000)
2. Direct Database Write (Advanced)
Write the optimized playlist directly to your Rekordbox 6 database:
krate --rekordbox --playlist "My Playlist" --write-to-db
⚠️ WARNING:
- Close Rekordbox before running this command.
- This modifies your
master.dbfile directly. - Backup your database before using this feature.
Input Format
JSON file with tracks containing id, key (Camelot notation), and bpm:
{
"tracks": [
{"id": "track_001", "key": "8A", "bpm": 128},
{"id": "track_002", "key": "8B", "bpm": 130},
{"id": "track_003", "key": "9A", "bpm": 125}
]
}
How It Works
1. BPM Compatibility
Adjacent tracks must have compatible BPMs within tolerance:
| Track A | Track B | Tolerance | Match? | Reason |
|---|---|---|---|---|
| 128 BPM | 130 BPM | ±10 | ✅ | Direct (diff = 2) |
| 128 BPM | 64 BPM | ±10 | ✅ | Half-time (128 = 64×2) |
| 75 BPM | 150 BPM | ±10 | ✅ | Double-time (75×2 = 150) |
| 128 BPM | 100 BPM | ±10 | ❌ | Too far |
2. Harmonic Mixing (Camelot Wheel)
Harmonic compatibility levels:
STRICT (default):
- Same key (8A → 8A)
- ±1 hour same letter (8A → 7A, 9A)
- Same hour different letter (8A → 8B)
MODERATE:
- Above + ±1 hour different letter (8A → 9B, 7B)
RELAXED:
- Above + ±3 hours (8A → 5A, 11A)
3. Optimization Goal
Maximize playlist length while optimizing transition quality and respecting harmonic constraints.
Multi-Objective Optimization:
- Primary: Maximize playlist length (more tracks = better)
- Secondary: Maximize transition quality scores (smoother mixes = better)
- Constraints: Energy flow, harmonic violations, energy boosts
Configuration Options
| Parameter | Default | Description |
|---|---|---|
bpm_tolerance |
10.0 | Maximum BPM difference for direct match |
allow_halftime_bpm |
True | Enable half/double-time matching |
max_violation_pct |
0.10 | Max percentage of non-harmonic transitions |
harmonic_level |
STRICT | Harmonic compatibility strictness |
enforce_energy_flow |
True | Enforce non-decreasing energy with max +1 increase (next >= current and next - current <= 1) |
max_energy_boosts |
3 | Maximum number of energy boost transitions (+2 hours on Camelot wheel) per playlist |
transition_quality_weight |
10.0 | Weight for transition quality in objective function (higher = prefer quality over length) |
time_limit_seconds |
60.0 | Solver time limit |
Advanced Features
Energy Boost Mixing
Based on Mixed In Key's Energy Boost technique, you can use strategic harmonic jumps to add excitement:
+2 Hours (Energy Boost): Jump forward 2 hours on the Camelot wheel
# Example: 5A → 7A
# Gives a quick burst of energy to wake up the dancefloor
optimizer = PlaylistOptimizer(max_energy_boosts=3) # Allow up to 3 boosts
-5 Hours (Armin Variation): Jump backward 5 hours
# Example: 12A → 7A
# Used by Armin Van Buuren and Skrillex for dramatic energy spikes
Best Practices (from professional DJ analysis):
- Use energy boosts sparingly (every 20-30 minutes, not constantly)
- Top DJs average 2-3 energy boosts per set
- Combine with energy level increases for maximum impact
- Default limit:
max_energy_boosts=3per playlist
Transition Quality Scoring
The optimizer now scores each transition by harmonic quality:
| Transition Type | Quality Score | Example |
|---|---|---|
| Perfect match (same key) | 1.0 | 8A → 8A |
| ±1 hour, same letter | 0.95 | 8A → 9A |
| Relative major/minor | 0.9 | 8A → 8B |
| ±1 hour, different letter | 0.8 | 8A → 9B |
| Energy Boost (+2) | 0.6 | 5A → 7A |
| Armin Variation (-5) | 0.5 | 12A → 7A |
| Mood shift (±3 hours) | 0.4 | 8A → 5A |
| Clash | 0.0 | 8A → 2B |
Quality scores are weighted in the objective function, so the solver prefers smoother transitions when possible.
Examples
See examples/ directory:
example_tracks.json- Sample input datasdk_usage.py- SDK usage demonstrationlogging_example.py- Logging configuration example
Development
# Clone repository
git clone https://github.com/yourusername/djkr8
cd krate
# Install with dev dependencies
uv sync --dev
# Install pre-commit hooks
uv run pre-commit install
uv run pre-commit install --hook-type commit-msg
# Run tests
uv run pytest
# Lint and format
uv run ruff check # Check for issues
uv run ruff check --fix # Auto-fix issues
uv run ruff format # Format code
# Run pre-commit on all files
uv run pre-commit run --all-files
# Run example
uv run python examples/sdk_usage.py
How the Solver Works
The optimizer uses Google OR-Tools CP-SAT solver with:
- Binary Variables:
included[i]= track i is in playlist - Edge Variables:
edge[i,j]= track j follows track i - Circuit Constraint:
AddCircuitensures valid track ordering - BPM Constraints: Only create edges between BPM-compatible tracks
- Harmonic Soft Constraints: Penalize non-harmonic transitions
- Objective: Maximize
sum(included)
License
MIT
Credits
Built with:
- Google OR-Tools - Constraint programming solver
- pyrekordbox - Rekordbox database access
- Camelot Wheel system by Mark Davis (Mixed In Key)
Metadata
Release files for djkr8 1.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| djkr8-1.5.0.tar.gz | 20.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| djkr8-1.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 45.0 kB
Release files / djkr8-1.5.0.tar.gz
| Download URL | djkr8-1.5.0.tar.gz |
|---|---|
| Size | 20.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
69fc258c0552c914a76aa8783e24e151f7b3be29efef4b24c87779a4bebd23a3
|
|
BLAKE2b-256 checksum How to use checksums |
310393a03f852492266443dff455ba903b4a6da83dcd477c5486974a7abadc6d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jan 17, 2026.
Transparency logRelease files / djkr8-1.5.0-py3-none-any.whl
| Download URL | djkr8-1.5.0-py3-none-any.whl |
|---|---|
| Size | 24.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
660747662b65364afa547282cf6e005956ded834d6e9a5e48b03888b07da39cc
|
|
BLAKE2b-256 checksum How to use checksums |
30e498fa0d76f1294d9e1b091b6aeb6bc9e2f803f3bd0ad4630cc0e0dda2f09f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jan 17, 2026.
Transparency log