Skip to content

Commit 2885759

Browse files
Stop assuming everyone owns Mixed In Key and Platinum Notes
Sortero only needs key and BPM tags to exist; it has never cared what wrote them. The UI said otherwise at every turn, naming two paid tools as if they were requirements. rekordbox, Serato, Traktor and Mixxx all analyse for free, and most people already have one. Prompts now say "your analysis tool", with those named as examples rather than prerequisites, and the README lists the free options alongside the paid one. The Platinum Notes ordering reminder is now conditional on actually using it, and says plainly that it is optional: it improves audio quality and writes nothing Sortero reads. The warning still explains why order matters, because getting it wrong fails silently - mastering after analysing leaves the tags describing a file that no longer exists, with no error to notice. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 8606f10 commit 2885759

8 files changed

Lines changed: 68 additions & 53 deletions

File tree

README.md

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ DJ Collection/
3232
Mixes/ recordings over 20 minutes
3333
_Playlists/*.m3u8 every folder you had, as a playlist
3434
_Quarantine/ duplicates and rejects, never deleted
35-
To Be Processed/ your Platinum Notes / Mixed In Key stage
35+
To Be Processed/ tracks waiting to be analysed
3636
Processed/ analysed and ready to file
3737
```
3838

@@ -47,25 +47,37 @@ DJ Collection/
4747
| **Tags** | Strips download-site spam from Genre/Comment, infers missing Artist/Title from filenames, normalises Genre, and promotes Mixed In Key energy into the sortable Grouping field. |
4848
| **Duplicates** | Exact (identical audio) and Likely (same artist/title/version, same length). Different remixes are never grouped. Extras move to `_Quarantine`. |
4949
| **Import** | Add files or folders. Analysed tracks go straight to `Tracks/<Genre>`; anything missing key/BPM lands in `To Be Processed`. Tracks already in the library are flagged, not copied. **"Sort the 'Processed' folder"** files everything you've already run through PN and MIK. |
50-
| **Needs Work** | Everything Sortero can't fix by itself, filtered by what's missing (key/BPM, energy, genre, artist, low bitrate). Select tracks and stage them in `To Be Processed` for Platinum Notes (first) then Mixed In Key (second). Your own set recordings are excluded. |
50+
| **Needs Work** | Everything Sortero can't fix by itself, filtered by what's missing (key/BPM, energy, genre, artist, low bitrate). Select tracks and stage them in `To Be Processed` for whichever analysis tool you use. Your own set recordings are excluded. |
5151
| **Playlists** | Rebuild a Spotify or TIDAL playlist against your local files, or rebuild the folder playlists. |
5252
| **History** | Every operation, with one-click undo. |
5353

5454
## The analysis loop
5555

56-
Sortero can compute genre, artist and titles, but not key, BPM or energy —
57-
those need Mixed In Key and Platinum Notes. The **Needs Work** tab closes that
56+
Sortero works out genre, artist and title on its own, but not key, BPM or
57+
energy — those come from an analysis tool. The **Needs Work** tab closes that
5858
loop:
5959

6060
1. Filter by *Missing key or BPM* and select what you want.
6161
2. **Stage selected in 'To Be Processed'.**
62-
3. Run that folder through **Platinum Notes first**, then **Mixed In Key second**,
63-
saving the results into `Processed`.
62+
3. Run that folder through your analysis tool, saving the results into `Processed`.
6463
4. **Import → "Sort the 'Processed' folder"** files them by genre automatically.
6564

66-
> **Order matters.** Platinum Notes re-encodes the audio. Run it *after* Mixed In
67-
> Key and you throw that analysis away — the key and energy tags won't match the
68-
> file you end up with. Sortero repeats this reminder when you stage tracks.
65+
Sortero doesn't care which tool wrote the tags — only that key and BPM are there.
66+
Any of these work:
67+
68+
| Tool | Notes |
69+
|---|---|
70+
| **rekordbox / Serato / Traktor** | Already analyse on import. Free, and you probably have one. |
71+
| **[Mixxx](https://mixxx.org)** | Free and open source on every platform. Built-in key and BPM detection. |
72+
| **[Mixed In Key](https://mixedinkey.com)** | Paid. Widely considered the most accurate for key, and the source of the 1–10 energy rating. |
73+
| **KeyFinder** | Open source, key detection only. |
74+
75+
> **If you use Platinum Notes, run it *before* analysing.** It re-encodes the
76+
> audio, so mastering afterwards leaves key and energy tags describing a file
77+
> that no longer exists — silently, with no error. Platinum Notes is entirely
78+
> optional; it improves audio quality and writes no tags Sortero needs. For the
79+
> loudness part alone, [MP3Gain](https://alternativeto.net/software/mp3gain/) or
80+
> your DJ software's auto-gain is a free substitute.
6981
7082
Staging a track out of a set or vibe folder does **not** cost you that
7183
curation. Before anything moves, Sortero writes the folder out as a playlist

sortero/app.py

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ def render(self, h):
476476

477477
self.issues.delete(0, "end")
478478
for label, items, hint in [
479-
("missing key/BPM", h["needs_analysis"], "Platinum Notes 1st, Mixed In Key 2nd"),
479+
("missing key/BPM", h["needs_analysis"], "run through your analysis tool"),
480480
("missing genre", h["no_genre"], "Tags tab can normalise what exists"),
481481
("missing artist", h["no_artist"], "Tags tab can infer from filename"),
482482
("spam in genre", h["spam_genre"], "Tags tab clears these"),
@@ -764,16 +764,16 @@ class ImportTab(BaseTab):
764764
def build(self):
765765
ttk.Label(self, foreground="#666", wraplength=980, justify="left",
766766
text="Add new music. Tracks with key and BPM go straight to Tracks/<Genre>. "
767-
"Anything missing analysis lands in 'To Be Processed' for Platinum Notes "
768-
"and Mixed In Key. Tracks already in the library are flagged, not copied."
767+
"Anything missing analysis lands in 'To Be Processed' for your "
768+
"analysis tool. Tracks already in the library are flagged, not copied."
769769
).pack(anchor="w", pady=(0, 8))
770770
pf = ttk.Frame(self)
771771
pf.pack(fill="x", pady=(0, 8))
772772
ttk.Button(pf, text="Sort the 'Processed' folder",
773773
command=self.intake_processed).pack(side="left")
774774
ttk.Label(pf, foreground="#666",
775775
text=" — file everything you have already run through "
776-
"Platinum Notes then Mixed In Key").pack(side="left")
776+
"your analysis tool").pack(side="left")
777777

778778
btns = ttk.Frame(self)
779779
btns.pack(fill="x", pady=(0, 8))
@@ -905,7 +905,7 @@ class NeedsWorkTab(BaseTab):
905905
"""Find and act on tracks whose metadata still needs outside help."""
906906

907907
FILTERS = [
908-
("Missing key or BPM — needs Platinum Notes + Mixed In Key",
908+
("Missing key or BPM — needs analysing",
909909
lambda r: not r.analyzed),
910910
("Missing key only", lambda r: not r.key),
911911
("Missing BPM only", lambda r: not r.bpm),
@@ -917,18 +917,21 @@ class NeedsWorkTab(BaseTab):
917917

918918
def build(self):
919919
ttk.Label(self, foreground="#666", wraplength=980, justify="left",
920-
text="Everything here needs something Sortero can't compute itself. "
921-
"Pick a filter, select the tracks you want, then stage them in "
922-
"'To Be Processed', then run that folder through Platinum Notes "
923-
"FIRST and Mixed In Key SECOND. When they land in 'Processed' "
924-
"the Import tab files them automatically."
920+
text="Everything here needs something Sortero can't work out itself. "
921+
"Pick a filter, select the tracks you want, and stage them in "
922+
"'To Be Processed'. Run that folder through your analysis tool — "
923+
"Mixed In Key, rekordbox, Mixxx, or whatever you use — and when "
924+
"the results land in 'Processed', the Import tab files them "
925+
"automatically."
925926
).pack(anchor="w", pady=(0, 4))
926927
ttk.Label(self, foreground="#8a5a00", font=("Helvetica", 12, "bold"),
927-
text="Order matters: 1. Platinum Notes → 2. Mixed In Key"
928+
text="If you use Platinum Notes: run it BEFORE your key/BPM analysis"
928929
).pack(anchor="w", pady=(0, 2))
929930
ttk.Label(self, foreground="#666", wraplength=980, justify="left",
930-
text="Platinum Notes re-encodes the audio, so running it after "
931-
"Mixed In Key throws the analysis away."
931+
text="Platinum Notes re-encodes the audio, so analysing first and "
932+
"mastering second leaves the tags describing a file that no "
933+
"longer exists. Skip this if you don't use it — Sortero only "
934+
"needs the key and BPM tags, whatever wrote them."
932935
).pack(anchor="w", pady=(0, 8))
933936

934937
row = ttk.Frame(self)
@@ -1034,11 +1037,10 @@ def stage(self):
10341037
return
10351038
if not messagebox.askyesno(
10361039
APP, f"Move {len(recs)} tracks into 'To Be Processed'?\n\n"
1037-
"Then, in this order:\n"
1038-
" 1. Platinum Notes (re-encodes the audio)\n"
1039-
" 2. Mixed In Key (reads the final audio)\n\n"
1040-
"Save the results into 'Processed', then use Import → "
1041-
"\"Sort the 'Processed' folder\".\n\n"
1040+
"Run them through your analysis tool, save the results into "
1041+
"'Processed', then use Import → \"Sort the 'Processed' folder\".\n\n"
1042+
"If you use Platinum Notes, run it BEFORE analysing — it "
1043+
"re-encodes the audio.\n\n"
10421044
"Reversible from History."):
10431045
return
10441046
root = self.app.root_dir.get()
@@ -1055,12 +1057,10 @@ def done(res):
10551057
path, n = res
10561058
messagebox.showinfo(
10571059
APP, f"Staged {n} tracks in 'To Be Processed'.\n\n"
1058-
"Remember the order:\n"
1059-
" 1. Platinum Notes FIRST\n"
1060-
" 2. Mixed In Key SECOND\n\n"
1061-
"Platinum Notes re-encodes the audio, so running it second "
1062-
"would discard the Mixed In Key analysis.\n\n"
1063-
"Save the results into 'Processed'.")
1060+
"Run that folder through your analysis tool — Mixed In Key, "
1061+
"rekordbox, Mixxx — and save the results into 'Processed'.\n\n"
1062+
"If you use Platinum Notes, run it FIRST. It re-encodes the "
1063+
"audio, so mastering after analysing discards the analysis.")
10641064
self.app.tab_history.refresh()
10651065
self.app.scan()
10661066

sortero/importer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
Rules, in order:
44
1. Already in the library (same content signature) -> flagged as duplicate.
5-
2. No key/BPM -> 'To Be Processed' (your Platinum Notes / MIK stage).
5+
2. No key/BPM -> 'To Be Processed' (your analysis staging lane).
66
3. Otherwise -> Tracks/<Genre>/Artist - Title.ext
77
Spam tags are stripped on the way in so junk never enters the library.
88
"""
@@ -108,7 +108,7 @@ def plan(root, sources, library_recs, progress=None):
108108
elif not (r.key and r.bpm):
109109
dest = os.path.join(root, TO_PROCESS, target_filename(r))
110110
results.append({"rec": r, "dest": dest, "action": "to-process",
111-
"reason": "missing key/BPM - run Platinum Notes + Mixed In Key"})
111+
"reason": "missing key/BPM - needs analysing"})
112112
else:
113113
g = resolve_genre(r)
114114
dest = os.path.join(root, TRACKS_DIR, safe(g, 60), target_filename(r))

sortero/library.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
from .common import AUDIO_EXTS, is_spam, clean_stem, split_artist_title, to_camelot
55
from .tagio import Track
66

7-
# Folders Sortero never touches. The user's Platinum Notes / Mixed In Key staging
8-
# areas, plus Sortero's own quarantine.
7+
# Folders Sortero never touches: the user's analysis staging lanes, plus
8+
# Sortero's own quarantine.
99
PROTECTED = {"To Be Processed", "Processed", "_Quarantine", "_Playlists"}
1010

1111
# Folders holding your own recordings rather than tracks to play. Nothing in
12-
# here ever needs Mixed In Key / Platinum Notes analysis.
12+
# here ever needs key/BPM analysis.
1313
RECORDING_DIRS = {"Recorded Mixes", "Mixes", "Recordings"}
1414

1515

sortero/membership.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Remember which playlists a track belonged to while it is away being analysed.
22
3-
Staging a track for Platinum Notes / Mixed In Key moves it out of the folder it
3+
Staging a track for analysis moves it out of the folder it
44
was curated in, and it comes back renamed, in a different place. Without a
55
record of where it came from, that track would quietly fall out of the set or
66
vibe playlist it belonged to.

sortero/organize.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ def folder_playlist_name(rec):
305305

306306

307307
def stage_for_analysis(root, recs, all_recs=None, log=print, progress=None):
308-
"""Move chosen tracks into 'To Be Processed' for Platinum Notes / Mixed In Key.
308+
"""Move chosen tracks into 'To Be Processed' for key/BPM analysis.
309309
310310
Their current folder membership is written to playlists first and recorded
311311
as pending, so filing them back out of 'Processed' restores them to the

sortero/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.3.4"
1+
__version__ = "0.3.5"

sortero/wizard.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,8 @@ def _folder_note(self):
246246
msg = f"Found: {d}"
247247
if have:
248248
msg += ("\n\nSortero can see your " + " and ".join(f"'{h}'" for h in have) +
249-
" folder. Those are your Platinum Notes / Mixed In Key staging "
250-
"area and are never reorganised.")
249+
" folder. Those are your analysis staging area and are never "
250+
"reorganised.")
251251
else:
252252
msg += ("\n\nSortero will create 'To Be Processed' and 'Processed' folders "
253253
"for the analysis workflow when it first needs them.")
@@ -284,7 +284,7 @@ def _render_scan(self):
284284
f"{h['pct_analyzed']:.0f}% have key and BPM\n"
285285
f"{h['pct_genre']:.0f}% have a usable genre\n"
286286
f"{h['pct_energy']:.0f}% have an energy rating\n\n"
287-
f"{len(h['needs_analysis'])} tracks need Platinum Notes / Mixed In Key\n"
287+
f"{len(h['needs_analysis'])} tracks need analysing for key and BPM\n"
288288
f"{len(h['no_genre'])} need a genre — most can be inferred")
289289
ttk.Label(self.scan_box, text=text, font=("Menlo", 11),
290290
justify="left").pack(anchor="w")
@@ -457,14 +457,17 @@ def done(_):
457457
def _analysis(self):
458458
self._heading("Tracks that need Mixed In Key",
459459
"Sortero works out genre, artist and title itself, but key, BPM "
460-
"and energy need Mixed In Key and Platinum Notes. It can gather "
461-
"those tracks into 'To Be Processed' ready for you to run them.")
460+
"and energy come from an analysis tool — Mixed In Key, rekordbox, "
461+
"Mixxx, whichever you use. Sortero can gather the tracks that need "
462+
"it into 'To Be Processed' ready to run.")
462463
ttk.Label(self.body, foreground="#8a5a00", font=("Helvetica", 13, "bold"),
463-
text="Order matters: 1. Platinum Notes → 2. Mixed In Key"
464+
text="If you use Platinum Notes: run it BEFORE analysing"
464465
).pack(anchor="w", pady=(0, 2))
465466
ttk.Label(self.body, foreground="#666", wraplength=690, justify="left",
466-
text="Platinum Notes re-encodes the audio, so running it after Mixed "
467-
"In Key throws that analysis away."
467+
text="It re-encodes the audio, so mastering after analysing leaves the "
468+
"tags describing a file that no longer exists. Ignore this if you "
469+
"don't use it — Sortero only needs the key and BPM tags, whatever "
470+
"wrote them."
468471
).pack(anchor="w", pady=(0, 10))
469472
if not self.recs:
470473
ttk.Label(self.body, text="Nothing scanned yet.").pack(anchor="w")
@@ -480,10 +483,10 @@ def _analysis(self):
480483
if not pending:
481484
self.an_btn.configure(state="disabled")
482485
ttk.Label(self.body, foreground="#666", wraplength=690, justify="left",
483-
text="\nAfterwards: Platinum Notes first, then Mixed In Key, saving "
484-
"into 'Processed'. Then use Import → \"Sort the 'Processed' "
485-
"folder\". Tracks staged out of a set rejoin its playlist "
486-
"automatically when they come back."
486+
text="\nAfterwards: analyse them, save the results into 'Processed', "
487+
"then use Import → \"Sort the 'Processed' folder\". Tracks staged "
488+
"out of a set rejoin its playlist automatically when they come "
489+
"back."
487490
).pack(anchor="w", pady=(10, 0))
488491

489492
def _apply_stage(self):

0 commit comments

Comments
 (0)