mixarchive 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mixarchive-0.2.0/LICENSE +21 -0
- mixarchive-0.2.0/PKG-INFO +487 -0
- mixarchive-0.2.0/README.md +460 -0
- mixarchive-0.2.0/mixarchive/__init__.py +2 -0
- mixarchive-0.2.0/mixarchive/__main__.py +3 -0
- mixarchive-0.2.0/mixarchive/art.py +159 -0
- mixarchive-0.2.0/mixarchive/cli.py +1214 -0
- mixarchive-0.2.0/mixarchive/collection.py +162 -0
- mixarchive-0.2.0/mixarchive/gui.html +739 -0
- mixarchive-0.2.0/mixarchive/gui.py +339 -0
- mixarchive-0.2.0/mixarchive/gui_hybrid.html +262 -0
- mixarchive-0.2.0/mixarchive/gui_music.html +481 -0
- mixarchive-0.2.0/mixarchive/gui_panel.html +248 -0
- mixarchive-0.2.0/mixarchive/gui_progress.js +81 -0
- mixarchive-0.2.0/mixarchive/gui_radio.html +250 -0
- mixarchive-0.2.0/mixarchive/gui_skin.js +313 -0
- mixarchive-0.2.0/mixarchive/modland.py +166 -0
- mixarchive-0.2.0/mixarchive/rbdb.py +413 -0
- mixarchive-0.2.0/mixarchive/rbfiles.py +1006 -0
- mixarchive-0.2.0/mixarchive/rbmerge.py +389 -0
- mixarchive-0.2.0/mixarchive/rbxml.py +116 -0
- mixarchive-0.2.0/mixarchive/singles.py +232 -0
- mixarchive-0.2.0/mixarchive/split.py +262 -0
- mixarchive-0.2.0/mixarchive/tracklist.py +231 -0
- mixarchive-0.2.0/mixarchive/util.py +241 -0
- mixarchive-0.2.0/mixarchive/yt.py +233 -0
- mixarchive-0.2.0/mixarchive.egg-info/PKG-INFO +487 -0
- mixarchive-0.2.0/mixarchive.egg-info/SOURCES.txt +32 -0
- mixarchive-0.2.0/mixarchive.egg-info/dependency_links.txt +1 -0
- mixarchive-0.2.0/mixarchive.egg-info/entry_points.txt +5 -0
- mixarchive-0.2.0/mixarchive.egg-info/requires.txt +10 -0
- mixarchive-0.2.0/mixarchive.egg-info/top_level.txt +1 -0
- mixarchive-0.2.0/pyproject.toml +42 -0
- mixarchive-0.2.0/setup.cfg +4 -0
mixarchive-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 wolfybird
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mixarchive
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Archive YouTube channels and long mixes as tagged tracks, and sync them into rekordbox for CDJ/XDJ
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/wolfybird/mixarchive
|
|
7
|
+
Project-URL: Issues, https://github.com/wolfybird/mixarchive/issues
|
|
8
|
+
Keywords: youtube,rekordbox,dj,mixes,tracklists,yt-dlp,cdj,xdj
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
11
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
12
|
+
Classifier: Operating System :: MacOS
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: yt-dlp[default]
|
|
19
|
+
Requires-Dist: deno
|
|
20
|
+
Requires-Dist: mutagen
|
|
21
|
+
Requires-Dist: pillow
|
|
22
|
+
Provides-Extra: app
|
|
23
|
+
Requires-Dist: pywebview>=6; extra == "app"
|
|
24
|
+
Provides-Extra: rekordbox
|
|
25
|
+
Requires-Dist: pyrekordbox==0.5.0; extra == "rekordbox"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# mixarchive
|
|
29
|
+
|
|
30
|
+
Archive YouTube channels, playlists and long mixes (DJ sets, compilations, mixed CDs) as
|
|
31
|
+
tagged, level-matched, cover-art-equipped tracks, and put them straight into rekordbox as
|
|
32
|
+
playlists, ready to analyze and export to a USB stick for Pioneer DJ players.
|
|
33
|
+
|
|
34
|
+
New here? Start with [`docs/guide.html`](docs/guide.html): how the pieces fit together
|
|
35
|
+
from YouTube to the decks, setup, the day-to-day steps with screenshots, where the backups
|
|
36
|
+
are and how to undo things. This README is the reference.
|
|
37
|
+
|
|
38
|
+
> mixarchive is an independent project. It is not made, endorsed or supported by
|
|
39
|
+
> AlphaTheta or Pioneer DJ; rekordbox, CDJ and XDJ are trademarks of their owners. It
|
|
40
|
+
> writes to rekordbox's database only while rekordbox is closed, after saving a copy, but
|
|
41
|
+
> keep your own backups. Download and play only what you have the right to: YouTube's
|
|
42
|
+
> terms limit downloading, and playing music in public needs the licences that apply where
|
|
43
|
+
> you play.
|
|
44
|
+
|
|
45
|
+
Built and tested on Windows 10 with rekordbox 7; runs on macOS too.
|
|
46
|
+
|
|
47
|
+
## Install
|
|
48
|
+
|
|
49
|
+
You need Python 3.11 or newer ([python.org](https://www.python.org/downloads/), or
|
|
50
|
+
`winget install Python.Python.3.14` on Windows) and ffmpeg (`winget install ffmpeg` on
|
|
51
|
+
Windows, `brew install ffmpeg` on macOS). Then, in a terminal:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
python -m pip install --user pipx
|
|
55
|
+
python -m pipx ensurepath
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
open a new terminal (so the commands are on PATH), and:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
pipx install "mixarchive[app,rekordbox]"
|
|
62
|
+
mixarchive-app
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`mixarchive-app` opens mixarchive in a window of its own (see [GUI](#gui)); `mixarchive`
|
|
66
|
+
is the command line. pipx gives mixarchive an environment of its own; `pip install` into
|
|
67
|
+
any virtual environment works too. The extras: `app` is the window (pywebview), and
|
|
68
|
+
`rekordbox` is what writes to rekordbox's database: `rbsync`, `files` and `merge`
|
|
69
|
+
(pyrekordbox). Without them you still get the command line and `mixarchive gui` in the
|
|
70
|
+
browser.
|
|
71
|
+
|
|
72
|
+
Until the first release is on PyPI, install from GitHub instead:
|
|
73
|
+
`pipx install "mixarchive[app,rekordbox] @ git+https://github.com/wolfybird/mixarchive"`.
|
|
74
|
+
|
|
75
|
+
Your collections go in `Music\mixarchive` in your home folder unless you pick another
|
|
76
|
+
**music folder** before adding the first one (on the Add screen, or `library_root` in
|
|
77
|
+
`settings.json`). mixarchive's own files - settings, the list of collections, caches - are in
|
|
78
|
+
`%APPDATA%\mixarchive` on Windows, `~/Library/Application Support/mixarchive` on macOS and
|
|
79
|
+
`~/.local/share/mixarchive` on Linux; set `MIXARCHIVE_HOME` to use another folder.
|
|
80
|
+
|
|
81
|
+
**Updating:** `pipx upgrade mixarchive`. When YouTube changes something and downloads
|
|
82
|
+
start failing, a newer yt-dlp is usually the fix, before a new mixarchive is out:
|
|
83
|
+
`pipx runpip mixarchive install -U yt-dlp`.
|
|
84
|
+
|
|
85
|
+
## Day to day
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
mixarchive add https://www.youtube.com/@somelabel --label "Some Label" --genre House
|
|
89
|
+
mixarchive add https://youtu.be/VIDEOID https://youtu.be/OTHERID # -> YouTube picks
|
|
90
|
+
mixarchive update # every collection: new uploads -> download -> split -> rbsync
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
then in rekordbox: *mixarchive › Not analyzed yet*, Ctrl+A, right-click › Analyze Track;
|
|
94
|
+
then export the playlists you want to the USB stick. `update` leaves rekordbox alone while
|
|
95
|
+
it's open (it says so) - close it and run `mixarchive rbsync`.
|
|
96
|
+
|
|
97
|
+
`mixarchive` here is the command pip installs; in a clone (below) it's `mixarchive.bat`
|
|
98
|
+
(or `venv\Scripts\python -I -m mixarchive`), on macOS `./mixarchive.sh`.
|
|
99
|
+
|
|
100
|
+
## GUI
|
|
101
|
+
|
|
102
|
+
`mixarchive gui` opens the workflow in the browser (served on 127.0.0.1 only; the address
|
|
103
|
+
carries a random token, so other pages can't drive it - keep its window open while you use
|
|
104
|
+
it). `mixarchive app` (or `mixarchive-app`) opens it in a window of its own instead
|
|
105
|
+
(pywebview, from the `app` extra: Edge WebView2 on Windows, WebKit on macOS). The window
|
|
106
|
+
won't close while a command is running - press Stop first.
|
|
107
|
+
|
|
108
|
+
Both open on **hybrid**. The **Design** menu at the top switches between the designs, and
|
|
109
|
+
`--page NAME` opens on another one:
|
|
110
|
+
|
|
111
|
+
| design | |
|
|
112
|
+
|---|---|
|
|
113
|
+
| `hybrid` (default) | the radio header over the panel body |
|
|
114
|
+
| `radio` | black, thin rules, condensed caps; NOW and NEXT rows under the menu |
|
|
115
|
+
| `panel` | light grey, lowercase; the five steps as hardware-style keys beside a status screen |
|
|
116
|
+
| `music` | dark sidebar, album-style collection cards, the running command in a player bar |
|
|
117
|
+
| `classic` | the original tabbed page, with skins (below) |
|
|
118
|
+
|
|
119
|
+
Hybrid, radio and panel are built around the pipeline: **1 Add, 2 Update, 3 Sync,
|
|
120
|
+
4 Analyze, 5 USB**, each with a live count, and *Next* points at the first step with work
|
|
121
|
+
waiting ("Update · 5 mixes to fetch", "Sync · close rekordbox first"). The classic page's
|
|
122
|
+
tabs are **Collections**, **Add sources**, **rekordbox** (including Merge playlists) and
|
|
123
|
+
**Library files** (the `files` tool below); merging and the files tool are only there for
|
|
124
|
+
now.
|
|
125
|
+
|
|
126
|
+
Every design runs the CLI's own commands one at a time, with their output alongside;
|
|
127
|
+
anything that writes to rekordbox is disabled while rekordbox is open. While a command runs,
|
|
128
|
+
a progress bar shows the collection, the step and its count, with an estimate of the time
|
|
129
|
+
left once the step reports a count.
|
|
130
|
+
|
|
131
|
+
The classic page picks a **skin** in its header, kept between runs: **Bare** (the default:
|
|
132
|
+
black and white with one acid accent, light or dark with the system), **Burrow** (wood
|
|
133
|
+
cheeks, candy keys, a small OLED readout), **Field** (grey panel, numbered keys, a green
|
|
134
|
+
LCD) and **Classic** (the original look). `?skin=NAME` in the address overrides it for one
|
|
135
|
+
visit.
|
|
136
|
+
|
|
137
|
+
To start the window with a double-click:
|
|
138
|
+
- **Windows:** `mixarchive-app.exe` starts it without a console window. pipx puts it in
|
|
139
|
+
`%USERPROFILE%\.local\bin`, a clone in `venv\Scripts`; make a desktop or Start menu
|
|
140
|
+
shortcut to it.
|
|
141
|
+
- **macOS, from a clone:** `./make_mac_app.sh` builds `~/Applications/mixarchive.app`,
|
|
142
|
+
which runs the clone's venv, so code changes need no rebuild (rebuild it if you move the
|
|
143
|
+
folder). It opens on hybrid; `./make_mac_app.sh radio` picks another design.
|
|
144
|
+
|
|
145
|
+
## Setup from a clone
|
|
146
|
+
|
|
147
|
+
For working on mixarchive itself, or running it from a folder of its own:
|
|
148
|
+
|
|
149
|
+
1. Python 3.11+:
|
|
150
|
+
`py -3 -m venv venv` then `venv\Scripts\python -m pip install -e ".[app,rekordbox]"`.
|
|
151
|
+
On macOS: `python3 -m venv venv` then `venv/bin/python -m pip install -e ".[app,rekordbox]"`;
|
|
152
|
+
paths below that say `venv\Scripts\` are `venv/bin/` there.
|
|
153
|
+
2. ffmpeg, as in [Install](#install).
|
|
154
|
+
3. Run from a clone, mixarchive keeps its own files (`settings.json`, `library.json`,
|
|
155
|
+
`cache`, `tools`) in the clone rather than the app-data folder. For settings, copy
|
|
156
|
+
`settings.example.json` to `settings.json` and adjust it. Every key is optional:
|
|
157
|
+
|
|
158
|
+
| key | default | |
|
|
159
|
+
|---|---|---|
|
|
160
|
+
| `ffmpeg`, `ffprobe` | on PATH, or where winget / Homebrew put them | full paths if they're elsewhere (a path that doesn't exist falls back to the search) |
|
|
161
|
+
| `ytdlp` | the yt-dlp installed with mixarchive | |
|
|
162
|
+
| `rbpython` | `rbvenv\Scripts\python.exe` if there is one, else mixarchive's own Python | the Python with pyrekordbox |
|
|
163
|
+
| `library_root` | `Music\mixarchive` in your home folder | where a new library's collections go |
|
|
164
|
+
| `usb_mirror` | none | a backup copy of your USB stick, if you keep one: `files` flags tracks rekordbox plays from inside it |
|
|
165
|
+
| `openmpt123` | `tools\openmpt\openmpt123.exe` | only for MOD renders |
|
|
166
|
+
| `library` | `library.json` | the list of collections (written by `add` / `init`) |
|
|
167
|
+
|
|
168
|
+
An `rbvenv` (a second environment with pyrekordbox, from when it needed a newer Python
|
|
169
|
+
than the rest) is still used if it's there; a new setup doesn't need one.
|
|
170
|
+
4. The first collection decides where the library lives: by default `library_root`, or give
|
|
171
|
+
it `--root`, e.g.
|
|
172
|
+
`mixarchive add https://www.youtube.com/@somelabel --label "Some Label" --root "D:\DJ\Library\mixarchive\Some Label"`.
|
|
173
|
+
Later collections go next to it (label collections in a `labels\` folder beside it).
|
|
174
|
+
|
|
175
|
+
yt-dlp needs deno for YouTube's JavaScript (installed with the package). Some videos
|
|
176
|
+
answer 403 unless the request carries a PO token; see yt-dlp's
|
|
177
|
+
[PO Token Guide](https://github.com/yt-dlp/yt-dlp/wiki/PO-Token-Guide). If a bgutil
|
|
178
|
+
provider is installed in `tools\bgutil-ytdlp-pot-provider` in mixarchive's own folder (see that project's own
|
|
179
|
+
instructions), mixarchive starts it on 127.0.0.1 when a download needs one. Videos YouTube
|
|
180
|
+
age-restricts need a signed-in account; mixarchive never signs in, so they're left out.
|
|
181
|
+
|
|
182
|
+
## Where things live
|
|
183
|
+
|
|
184
|
+
All collections live under one library root, e.g.
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
D:\DJ\Library\mixarchive\ <- one folder per collection; labels\<Label>\ for labels
|
|
188
|
+
D:\DJ\Library\_rekordbox backups\ <- copies of rekordbox's database before each write
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
`library.json` maps collection names to their folders; `move NAME NEWROOT` relocates one
|
|
192
|
+
(copy, size check, re-register; `--delete-old`). Keep the library off the USB stick and out
|
|
193
|
+
of any backup copy of the stick: rekordbox's USB export re-files tracks as
|
|
194
|
+
`Contents\<Artist>\<Album>\`, and the next copy of the stick would bring them back as
|
|
195
|
+
second copies.
|
|
196
|
+
|
|
197
|
+
## Conventions
|
|
198
|
+
|
|
199
|
+
Every collection is one folder:
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
<collection>\
|
|
203
|
+
originals\<title> [<id>].opus best YouTube audio, never re-encoded
|
|
204
|
+
_mixarchive\ state (small, safe to sync)
|
|
205
|
+
collection.json settings (below)
|
|
206
|
+
sources.json which YouTube upload each mix maps to (+ alternates)
|
|
207
|
+
info\<id>.info.json yt-dlp metadata incl. top comments
|
|
208
|
+
tracklists\<id>.txt EDITABLE tracklist per mix - the source of truth for splits
|
|
209
|
+
tracks.csv EDITABLE singles list (label channels)
|
|
210
|
+
art\ square 600x600 covers (<id>_main.jpg, <id>_<NN>.jpg)
|
|
211
|
+
modules.csv, modules\, renders\ (opt-in MOD renders)
|
|
212
|
+
split\
|
|
213
|
+
MP3 320\<Mix>\NN - Artist - Title.mp3 tracks
|
|
214
|
+
MP3 320\_full mixes\<Mix>.mp3 whole mixes (tracklist in comments)
|
|
215
|
+
MP3 320\tracklist.csv manifest incl. loudness + gain applied
|
|
216
|
+
MOD renders\<Mix>\NN - Artist - Title (MOD render).mp3
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
- **Audio source**: YouTube Opus 251 (full band to ~20 kHz); AAC 140 (16 kHz low-pass) only
|
|
220
|
+
when no Opus exists. YouTube serves nothing lossless.
|
|
221
|
+
- **Output**: MP3 320 CBR (plays on every CDJ/XDJ). Add `flac` to `formats` for 16-bit FLAC.
|
|
222
|
+
- **Normalization** (default on): each track gets one linear gain to **-14 LUFS**, capped so the
|
|
223
|
+
true peak stays <= **-1 dBTP** - no limiter, dynamics untouched. Full mixes get one gain
|
|
224
|
+
for the whole mix. YouTube decodes often peak above 0 dBFS, so this also stops clipping.
|
|
225
|
+
Very dynamic tracks can land a few dB under -14 (peak-capped) rather than be squashed;
|
|
226
|
+
rekordbox Auto Gain evens those out. `tracklist.csv` records measured LUFS / peak / gain.
|
|
227
|
+
- **Tags**: artist, title, album (= mix), album artist (DJ / channel), track n/total, genre,
|
|
228
|
+
year, label (TPUB = rekordbox "Label"), comment with a YouTube link at the track's start time,
|
|
229
|
+
square cover art.
|
|
230
|
+
|
|
231
|
+
## Label channels and playlists (singles + mixes)
|
|
232
|
+
|
|
233
|
+
`init NAME --root DIR --playlist URL [--playlist URL2] --label "Label" --genre G` takes
|
|
234
|
+
playlists and channel pages (`https://www.youtube.com/@channel/videos`). `resolve` sorts
|
|
235
|
+
every video:
|
|
236
|
+
|
|
237
|
+
- **single track** (shorter than `mix_min_sec`, 15 min) -> a row in `_mixarchive\tracks.csv`
|
|
238
|
+
- **mix** (longer) -> the usual tracklist file and split
|
|
239
|
+
- **skip**: unavailable, under 60 s, over 12 h (livestreams), a title matching `skip_titles`
|
|
240
|
+
(trailer, teaser, preview, announcement, interview, ...), or a video another collection
|
|
241
|
+
already archives (`skip_known`)
|
|
242
|
+
|
|
243
|
+
`tracks.csv` is the singles' source of truth (UTF-8 CSV, opens in Excel). Edit freely,
|
|
244
|
+
then re-run `split`: renamed rows are re-exported and the old file removed.
|
|
245
|
+
|
|
246
|
+
| column | |
|
|
247
|
+
|---|---|
|
|
248
|
+
| status | `ok`, `check` (no artist found), `dup` (same artist + title as a better upload: official audio > plain > video, then longest), `skip`; set `manual` to protect a hand-edited row from `resolve --force` |
|
|
249
|
+
| artist, title, album | parsed from the title: `Artist - Title`, `Artist 'Title'`, catalog numbers (`ABC039`, `- XYZ030`, `[LB007]`), `(Label, 1993)`, `[Official Audio]`, `\| Series` |
|
|
250
|
+
| year, year_src | `title` / `youtube` (music metadata) / `description` (the (P) line) / `upload` (fallback; off with `--no-upload-year` for archive labels whose uploads are decades after release) |
|
|
251
|
+
| part, start, end | a short video with real chapters becomes one row per track |
|
|
252
|
+
|
|
253
|
+
Singles export whole (one gain to -14 LUFS / -1 dBTP) to `split\MP3 320\<Label>\Artist - Title.mp3`
|
|
254
|
+
with album = label (or the series), rekordbox Label = label, and the YouTube title, link,
|
|
255
|
+
catalog number and upload date in the comment. Covers: when the thumbnail is a sleeve on a
|
|
256
|
+
flat backdrop or in black bars, the sleeve is cropped out; otherwise the frame is letterboxed.
|
|
257
|
+
|
|
258
|
+
`--kind track` / `--kind mix` limits any step to singles or mixes (e.g. fetch only singles).
|
|
259
|
+
|
|
260
|
+
## Adding sources
|
|
261
|
+
|
|
262
|
+
`add LINK...` takes channel pages (`youtube.com/@name`, `/channel/UC...`; any tab becomes
|
|
263
|
+
`/videos`), playlists (`playlist?list=`) and single videos (`youtu.be/ID`, `watch?v=`,
|
|
264
|
+
`shorts/`, or a bare id; a watch link inside a playlist counts as the video):
|
|
265
|
+
|
|
266
|
+
- a channel / playlist becomes a new collection named after it, in the library root.
|
|
267
|
+
`--label L` makes it a label collection in `labels\L` (rekordbox Label = L); without one,
|
|
268
|
+
a channel is a DJ's (album artist = the channel) and the rekordbox playlist is named
|
|
269
|
+
after the channel / playlist. `--name`, `--genre`, `--album-artist`, `--root`,
|
|
270
|
+
`--no-upload-year` as for `init`
|
|
271
|
+
- single videos go to the **youtube-picks** collection (`YouTube picks` in the library root,
|
|
272
|
+
rekordbox playlist "YouTube picks"; created on first use). Short ones become singles
|
|
273
|
+
(album artist = their own artist), long ones mixes with tracklists, as anywhere else.
|
|
274
|
+
`--name X` with only videos makes / extends a picks collection called X instead
|
|
275
|
+
- `--to NAME` puts everything into an existing collection instead
|
|
276
|
+
- `--run` downloads + splits straight away; otherwise `update` does it later
|
|
277
|
+
|
|
278
|
+
Videos already archived in another collection are skipped as usual (`skip_known`).
|
|
279
|
+
Single videos live in `collection.json` as `source.videos`.
|
|
280
|
+
|
|
281
|
+
## Tracklist files
|
|
282
|
+
|
|
283
|
+
`_mixarchive\tracklists\<video id>.txt` - edit freely, then re-run `split`:
|
|
284
|
+
|
|
285
|
+
```
|
|
286
|
+
@album: Some DJ - Studio Mix (1996)
|
|
287
|
+
@album_artist: Some DJ
|
|
288
|
+
@year: 1996
|
|
289
|
+
00:00:00 First Artist - First Track {1995} {label: First Label}
|
|
290
|
+
00:07:51 Second Artist - Second Track (Some Remix) {1996} {label: Second Label}
|
|
291
|
+
--:--:-- Some Artist - Untimed Track <- blocks splitting until given a time
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Mixes with any `--:--:--` line get only a full-mix file until every line has a time.
|
|
295
|
+
`tracklists --force` refreshes the track lines but keeps your `@album/@album_artist/@year/@genre`.
|
|
296
|
+
|
|
297
|
+
Channels that write titles in styled Unicode letters (`𝗟𝗢𝗦𝗧 𝗢𝗖𝗘𝗔𝗡 沈殿`) show up blank or
|
|
298
|
+
as boxes on many DJ players. `"plain_titles": true` in collection.json makes new tracklists,
|
|
299
|
+
files and tags use plain letters instead (`LOST OCEAN 沈殿`); accents and kanji are kept.
|
|
300
|
+
Set it before a collection's first `split`: on one already split, the next `split` writes
|
|
301
|
+
plain-named copies next to the old files.
|
|
302
|
+
|
|
303
|
+
Where tracklists come from, best first: YouTube chapters -> timestamps in the description ->
|
|
304
|
+
per-track **lengths** whose total matches the mix (CD listings, also borrowed from sibling
|
|
305
|
+
uploads in the same collection, +-15 s and >=50% track overlap) -> timestamped top comments ->
|
|
306
|
+
silent gaps (unmixed CDs: clustered silences within 30 s merge, longest wins; used only when
|
|
307
|
+
there are exactly N-1 gaps) -> untimed lists.
|
|
308
|
+
|
|
309
|
+
Downloads retry YouTube's intermittent 403s; a mix that still fails is skipped and listed so
|
|
310
|
+
the rest of the batch finishes - just re-run `fetch`. As a last resort a download falls back
|
|
311
|
+
to the HLS copy of AAC 128k (format 234: same audio as 140, 16 kHz low-pass).
|
|
312
|
+
|
|
313
|
+
## Commands
|
|
314
|
+
|
|
315
|
+
| command | does |
|
|
316
|
+
|---|---|
|
|
317
|
+
| `init NAME --root DIR (--playlist URL... \| --local DIR [--exclude RE]) [--genre G] [--album-artist A] [--label L] [--no-upload-year] [--modrender] [--frames-art]` | create a collection (`--playlist` repeats; channel `/videos` pages work) |
|
|
318
|
+
| `resolve NAME [--pick "substring=VIDEOID"]` | find each mix's upload (search + duration match for local files), pick the one with the best tracklist |
|
|
319
|
+
| `tracklists NAME [--years CSV] [--force]` | write the editable tracklists |
|
|
320
|
+
| `fetch NAME` | download originals; move old local files to `_old 128k` |
|
|
321
|
+
| `art NAME` | square covers (thumbnail letterboxed; `frames` mode crops each track's on-screen info box) |
|
|
322
|
+
| `split NAME [--only X] [--formats mp3,flac] [--jobs N]` | cut + normalize + tag |
|
|
323
|
+
| `modmatch NAME` / `modrender NAME` | opt-in tracker music: Modland match, libopenmpt render |
|
|
324
|
+
| `status NAME` | per-mix progress |
|
|
325
|
+
| `verify NAME [--sample N]` | every split decodes, has the right length, tags + cover; re-measures a sample's loudness |
|
|
326
|
+
| `run NAME` | everything above in order |
|
|
327
|
+
| `add LINK... [--to NAME] [--label L] [--genre G] [--name N] [--root DIR] [--run]` | add channels / playlists (new collections) or videos (youtube-picks) |
|
|
328
|
+
| `update [NAME...] [--no-rbsync]` | `run` for every collection (or those named), then `rbsync` if rekordbox is closed |
|
|
329
|
+
| `files status \| audit \| verify \| relocate \| move \| flac \| undo \| flaccopy \| silence ...` | the files behind rekordbox's whole collection (below) |
|
|
330
|
+
| `gui [--port N] [--page NAME]` | the browser GUI (design: hybrid, radio, panel, music or classic) |
|
|
331
|
+
| `app [--port N] [--page NAME]` | the same GUI in a window of its own (needs the `app` extra: pywebview) |
|
|
332
|
+
| `list` | known collections |
|
|
333
|
+
| `move NAME NEWROOT [--delete-old]` | relocate a collection (copy, check sizes, re-register) |
|
|
334
|
+
| `rbsync [NAME...] [--dry-run]` | put tracks + playlists straight into rekordbox's database (rekordbox closed); default: all collections |
|
|
335
|
+
| `rbxml [NAME...] [--out PATH]` | the same playlists as a rekordbox XML file, for a manual import instead |
|
|
336
|
+
| `merge SOURCE --into TARGET [--apply] [--keep-source]` | merge one rekordbox playlist into another (below; rekordbox closed) |
|
|
337
|
+
|
|
338
|
+
All steps skip finished work; `--force` redoes it, `--only a,b` limits to matching mixes.
|
|
339
|
+
|
|
340
|
+
## Into rekordbox and onto the USB
|
|
341
|
+
|
|
342
|
+
Close rekordbox, then `mixarchive rbsync`. It writes into rekordbox's own database
|
|
343
|
+
(`%APPDATA%\Pioneer\rekordbox\master.db`, via pyrekordbox):
|
|
344
|
+
|
|
345
|
+
- a `mixarchive` playlist folder with one playlist per collection. Each holds the
|
|
346
|
+
collection's splits (mix tracks + singles); a mix without a timed tracklist contributes
|
|
347
|
+
its full-mix file instead, so a USB export never carries a mix alongside its own tracks.
|
|
348
|
+
MOD renders get their own playlist. Two collection.json settings change that:
|
|
349
|
+
`"full_mixes": true` lists every mix by its full mix instead (for when its splits live in
|
|
350
|
+
playlists of your own), and `"mix_playlists": {"<mix>": "<playlist>"}` moves a mix to a
|
|
351
|
+
playlist of its own, its splits then its full mix. Playlists are rewritten to match on every run, except
|
|
352
|
+
that tracks you dragged into one yourself stay (after the collection's own, in your order;
|
|
353
|
+
rbsync remembers what it put there in `cache\rbsync_written.json` and only ever removes
|
|
354
|
+
those). Other playlists, including your own inside that folder, are never touched
|
|
355
|
+
- new files are added to the collection with their tags, label and cover
|
|
356
|
+
- files a re-split changed (size differs) get tags, cover and size refreshed, like Reload
|
|
357
|
+
Tag; cues, beat grids and analysis stay
|
|
358
|
+
- files a tracklist / tracks.csv edit renamed are followed: the existing rekordbox entry
|
|
359
|
+
(matched by the YouTube link + start time in its comment) is pointed at the new name,
|
|
360
|
+
so its cues aren't lost to a fresh copy
|
|
361
|
+
- `mixarchive / Not analyzed yet` lists every synced track rekordbox hasn't analyzed. Open
|
|
362
|
+
it, Ctrl+A, right-click > Analyze Track. It disappears once it's empty
|
|
363
|
+
|
|
364
|
+
`master.db` and `masterPlaylists6.xml` are copied to `_rekordbox backups\<date> rbsync\`
|
|
365
|
+
(next to the library root) before each write; the last 10 are kept. To undo a sync, close
|
|
366
|
+
rekordbox and copy them back. A run with nothing to do writes nothing. `--dry-run` shows
|
|
367
|
+
what would change. Entries under the library whose file mixarchive removed (a single
|
|
368
|
+
that became a dup or skip) are reported, not deleted; they show as missing in rekordbox.
|
|
369
|
+
|
|
370
|
+
`rbxml` remains for a manual route: it writes the same playlists as `rekordbox.xml` in the
|
|
371
|
+
library root for rekordbox xml > Import Playlist. Use one route or the other; importing the
|
|
372
|
+
XML after `rbsync` adds a second set of playlists.
|
|
373
|
+
|
|
374
|
+
Both print each playlist's size, for planning what fits on the USB stick.
|
|
375
|
+
|
|
376
|
+
rekordbox has no way to start an analysis from outside, so analyzing stays a click in
|
|
377
|
+
rekordbox. Closing rekordbox stops an analysis midway; the next `rbsync` shrinks *Not
|
|
378
|
+
analyzed yet* to what's left. When a re-split changes the audio behind a file rekordbox
|
|
379
|
+
already analyzed (same name, or a followed rename, but a different length), `rbsync` adds
|
|
380
|
+
it to *mixarchive › Re-analyze (audio changed)*: analyze those again, then delete that
|
|
381
|
+
playlist. Old split files that no longer exist show as missing in rekordbox - remove them
|
|
382
|
+
with *File › Display All Missing Files*.
|
|
383
|
+
|
|
384
|
+
mixarchive never writes to the USB stick; rekordbox's export does.
|
|
385
|
+
|
|
386
|
+
## Merging playlists (`merge`)
|
|
387
|
+
|
|
388
|
+
`mixarchive merge "Some Label (old)" --into "mixarchive / Some Label"` (or the rekordbox tab
|
|
389
|
+
of the GUI) merges one rekordbox playlist into another (rbmerge.py, with pyrekordbox like rbsync).
|
|
390
|
+
Name a playlist by its name, or by its path with ` / ` when the name is used twice.
|
|
391
|
+
|
|
392
|
+
- the target keeps its tracks in its order; the source's follow in theirs; a track listed
|
|
393
|
+
twice is listed once
|
|
394
|
+
- copies of the same recording (same words in the name, similar length, then confirmed
|
|
395
|
+
by ear: the first 4 minutes correlated) keep only the best copy: WAV / AIFF, then FLAC,
|
|
396
|
+
then the highest bitrate. Tracks rbsync put in a mixarchive target always stay; a copy
|
|
397
|
+
of yours stays next to them only if it's lossless or at least 320 kbps (a bought file),
|
|
398
|
+
so lower-bitrate rips of what mixarchive has go
|
|
399
|
+
- the copies that go leave the playlist, not the collection (play histories keep them);
|
|
400
|
+
their cues and hot cues are copied to the copy that stays, shifted by the measured
|
|
401
|
+
offset (not where it already has one there, or the hot cue pad is taken)
|
|
402
|
+
- the source playlist is deleted (`--keep-source` keeps it). One of mixarchive's own
|
|
403
|
+
playlists can't be the source: Sync would bring it back. Tracks merged into a mixarchive
|
|
404
|
+
playlist stay there on later Syncs (after mixarchive's own)
|
|
405
|
+
|
|
406
|
+
Without `--apply` it only reports, with every copy it found and where its cues go. With
|
|
407
|
+
it, master.db and masterPlaylists6.xml are copied to `_rekordbox backups\<date> merge\` first
|
|
408
|
+
with `plan.csv` (every track's fate); to undo, close rekordbox and copy them back. The USB
|
|
409
|
+
stick isn't touched: export the merged playlist again and delete the old one from the stick.
|
|
410
|
+
|
|
411
|
+
## rekordbox's files (`files`)
|
|
412
|
+
|
|
413
|
+
`mixarchive files COMMAND` works on rekordbox's whole collection, not only mixarchive's
|
|
414
|
+
(rbfiles.py, with pyrekordbox like rbsync). Filters for every command: `--ext wav,aif,aiff`,
|
|
415
|
+
`--under DIR`, `--playlist NAME` (a folder includes what's below it), `--missing`.
|
|
416
|
+
|
|
417
|
+
| command | does |
|
|
418
|
+
|---|---|
|
|
419
|
+
| `status` | rekordbox open?, tracks, not analyzed, missing; mixarchive playlists; tracks / GB per location and format |
|
|
420
|
+
| `audit [--list] [--csv PATH]` | the same per location for the filtered tracks; `--list` prints each file + its playlists |
|
|
421
|
+
| `verify [--decode]` | each file exists with the size rekordbox recorded; `--decode` decodes them all (ffmpeg) |
|
|
422
|
+
| `relocate --search DIR...` | missing files: same name (+ size) found under DIR -> rekordbox points there |
|
|
423
|
+
| `move --to DIR [--layout keep\|flat\|playlist\|artist] [--copy]` | move (or copy) the files and repoint rekordbox; `playlist` files each under the smallest playlist holding it, `artist` under artist / album, `keep` keeps the folders below `--under` |
|
|
424
|
+
| `flac --to DIR [--layout ...]` | WAV / AIFF -> FLAC under DIR, rekordbox repointed (type FLAC, bitrate 0, new size, as rekordbox records FLACs). Each FLAC is decoded and compared sample for sample with its original first; tags + cover art come along, with rekordbox's title / artist / album / genre on top. Originals stay; 32-bit float files (FLAC can't hold them) are copied as they are. Older Pioneer players don't play FLAC: if you use them, keep WAV for rekordbox and use `flaccopy` for the backup instead |
|
|
425
|
+
| `undo JOURNAL.csv [--keep]` | reverse a relocate / move / flac; `--keep` points rekordbox back at the originals but leaves the copies / FLACs |
|
|
426
|
+
| `flaccopy --from DIR --to DIR` | keep `--to` a FLAC copy of the WAV / AIFF under `--from` (checked the same way; float files copied as they are; copies whose source is gone are deleted). Files only, rekordbox untouched - `backup_library.bat` runs it for `<library>\WAV` |
|
|
427
|
+
| `silence [--min-gap S] [--list] [--csv PATH] [--jobs N] [--cues --apply]` | silent gaps inside tracks, such as dropouts in a rip (below) |
|
|
428
|
+
|
|
429
|
+
`files silence` decodes each track (mono 8 kHz, ffmpeg, 8 at a time; about 4,000x realtime
|
|
430
|
+
on a desktop) and measures its level every 0.1 s over 0.4 s. A gap is a stretch of
|
|
431
|
+
`--min-gap` seconds (default 2) at least 30 dB under the track's typical level and under
|
|
432
|
+
-35 dBFS, between where the music first gets within 10 dB of typical and where it last is,
|
|
433
|
+
so quiet intros, fade-ins and endings don't count. Short blips inside a gap don't end it.
|
|
434
|
+
A gap is **dropout-like** when the level falls 20 dB or more into it, or half of it is digital
|
|
435
|
+
silence; otherwise it's a **quiet passage** (breakdowns, dub drops). Files over 15 minutes
|
|
436
|
+
(mixes, tapes) and acapellas are reported apart. Results are cached in
|
|
437
|
+
`cache\silence_scan.json`, so a re-run scans only new or changed files. With `--cues --apply`
|
|
438
|
+
(rekordbox closed, master.db backed up first) it puts a memory cue named "gap N s" at each
|
|
439
|
+
dropout-like gap at least 20 s from either end, so you see it on the deck; a gap that
|
|
440
|
+
already has a memory cue within 1 s is skipped, so a re-run adds nothing twice.
|
|
441
|
+
|
|
442
|
+
relocate / move / flac / undo only report until `--apply`, and write only with rekordbox
|
|
443
|
+
closed. Cues, beat grids and analysis stay (pyrekordbox's `update_content_path` also rewrites
|
|
444
|
+
the path inside the analysis files, and rbfiles updates the size + md5 rekordbox keeps of
|
|
445
|
+
each analysis file in `contentFile`). Before writing, master.db, masterPlaylists6.xml and the
|
|
446
|
+
touched tracks' analysis files go to `_rekordbox backups\<date> files-<command>\` with a
|
|
447
|
+
`journal.csv` of every change - the input for `undo`. If anything fails midway the files are
|
|
448
|
+
put back, the analysis files restored and the database left as it was. Moves on one drive
|
|
449
|
+
are instant renames; to another drive the files are copied, checked, and the originals
|
|
450
|
+
deleted only after the database is committed.
|
|
451
|
+
|
|
452
|
+
`status` flags tracks that play from inside a backup copy of the USB stick (`usb_mirror`
|
|
453
|
+
in settings): that folder is a copy of the stick, not a library.
|
|
454
|
+
|
|
455
|
+
## Backup
|
|
456
|
+
|
|
457
|
+
`backup_library.bat LIBRARY BACKUP`, e.g. `backup_library.bat "D:\DJ\Library" "B:\DJ Backup"`,
|
|
458
|
+
mirrors the library to `BACKUP\Library` (robocopy /MIR, so files you deleted from the
|
|
459
|
+
library go from the backup too) and, while rekordbox is closed, rekordbox's own database
|
|
460
|
+
and analysis files (`%APPDATA%\Pioneer\rekordbox`) to `BACKUP\rekordbox`. Log:
|
|
461
|
+
`cache\backup_library.log`.
|
|
462
|
+
|
|
463
|
+
If the library has a `WAV` folder (WAV / AIFF for rekordbox), the backup leaves it out and
|
|
464
|
+
carries `WAV - FLAC backup` instead: the same audio as FLAC at about half the size,
|
|
465
|
+
refreshed by `files flaccopy` at the start of every backup. To restore a WAV from it:
|
|
466
|
+
`ffmpeg -i "x.flac" -c:a pcm_s16le "x.wav"` - `pcm_s24le` for 24-bit files (ffprobe shows
|
|
467
|
+
`bits_per_raw_sample`); left to its default ffmpeg would write 24-bit audio as 16-bit.
|
|
468
|
+
|
|
469
|
+
## MOD renders (tracker collections, opt-in)
|
|
470
|
+
|
|
471
|
+
`modmatch` fuzzy-matches artist/title against the Modland index (cached monthly), with scene
|
|
472
|
+
aliases in `aliases.json` (e.g. `{"xsm": ["xtra spicy mike"]}`). `modules.csv` statuses:
|
|
473
|
+
`auto` (title + artist match), `check` (needs a look), `none`, and the hand-set
|
|
474
|
+
`manual` / `skip` (never overwritten). `modrender` renders with libopenmpt (A1200 filter
|
|
475
|
+
emulation, 50% stereo, 48 kHz; needs `openmpt123`) and **verifies each render's length
|
|
476
|
+
against the mix's track** (+-12% / 20 s), trying alternates and marking `mismatch` instead
|
|
477
|
+
of exporting a same-named different song.
|
|
478
|
+
|
|
479
|
+
## Licence
|
|
480
|
+
|
|
481
|
+
mixarchive's own code is under the MIT licence (see [LICENSE](LICENSE)). It installs and
|
|
482
|
+
uses other projects under their own licences, notably yt-dlp (Unlicense), pyrekordbox (MIT),
|
|
483
|
+
mutagen (GPL-2.0-or-later), Pillow (MIT-CMU), SQLAlchemy (MIT), sqlcipher3 (zlib) and, for
|
|
484
|
+
`mixarchive app`, pywebview (BSD-3-Clause); none of them is bundled here. The GUI's fonts
|
|
485
|
+
(Inter, Inter Tight, JetBrains Mono, Space Mono, Fredoka, VT323; SIL Open Font Licence) load
|
|
486
|
+
from Google Fonts, with system fonts as the fallback offline. Thanks to their authors, and to pyrekordbox in particular for making
|
|
487
|
+
rekordbox's database reachable.
|