DJ playlist optimizer using Google OR-Tools for harmonic mixing and BPM matching
Project description
🎧 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 (1-5 range)
- 🎵 Harmonic Mixing: Uses the Camelot Wheel system for key compatibility
- 🎧 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 keeping non-harmonic transitions below the threshold (default: 10%).
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 (next >= current) |
time_limit_seconds |
60.0 | Solver time limit |
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)
Project details
Release history Release notifications | RSS feed
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 djkr8-1.4.0.tar.gz.
File metadata
- Download URL: djkr8-1.4.0.tar.gz
- Upload date:
- Size: 18.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b67519688e5b0e33c60e6d6c5bc83130c107bfc6e12960c1f390d08aa5935a15
|
|
| MD5 |
f0798cb0126340cdc9ae6895e27f3e18
|
|
| BLAKE2b-256 |
9b2faf9a169566009ee74f78e481ed540eb3ca947fcff89b43f9f1cc8888679d
|
Provenance
The following attestation bundles were made for djkr8-1.4.0.tar.gz:
Publisher:
release.yml on schoi80/djkr8
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
djkr8-1.4.0.tar.gz -
Subject digest:
b67519688e5b0e33c60e6d6c5bc83130c107bfc6e12960c1f390d08aa5935a15 - Sigstore transparency entry: 830791656
- Sigstore integration time:
-
Permalink:
schoi80/djkr8@1788fcd6c94114eb497222b85d8d4d5b22f55d76 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/schoi80
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1788fcd6c94114eb497222b85d8d4d5b22f55d76 -
Trigger Event:
push
-
Statement type:
File details
Details for the file djkr8-1.4.0-py3-none-any.whl.
File metadata
- Download URL: djkr8-1.4.0-py3-none-any.whl
- Upload date:
- Size: 22.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
428b6b4d9ac05602f1a45ff7d1124c5a5063efab009d9264b4f7145aaece8934
|
|
| MD5 |
7808c0ed7a4eb4888a99391cb5dabb73
|
|
| BLAKE2b-256 |
8cef8fee79311714465ad351d5c48c10c59258dba34c74a89e07576ea5bac05b
|
Provenance
The following attestation bundles were made for djkr8-1.4.0-py3-none-any.whl:
Publisher:
release.yml on schoi80/djkr8
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
djkr8-1.4.0-py3-none-any.whl -
Subject digest:
428b6b4d9ac05602f1a45ff7d1124c5a5063efab009d9264b4f7145aaece8934 - Sigstore transparency entry: 830791662
- Sigstore integration time:
-
Permalink:
schoi80/djkr8@1788fcd6c94114eb497222b85d8d4d5b22f55d76 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/schoi80
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1788fcd6c94114eb497222b85d8d4d5b22f55d76 -
Trigger Event:
push
-
Statement type: