Wer regelmäßig DJ-Bemusterungen bekommt, kennt das Spiel: Mail öffnen, Download-Link anklicken, eventuell Feedback abgeben, ZIP herunterladen, entpacken, WAV-Dateien konvertieren, ID3-Tags korrigieren, Cover einbetten und die fertigen Titel anschließend irgendwo ablegen.
Bei einigen wenigen Bemusterungen ist das kein Problem. Kommen regelmäßig Promos von verschiedenen Plattformen, wird daraus aber schnell eine ziemlich stumpfe Beschäftigung. Deshalb habe ich den Vorgang auf einem Linux-Server automatisiert. Das System verarbeitet derzeit Promoly und Megamix Promotion.
Das Ziel
Fox Party DJ Team/
└── Promo Titel/
├── 2026-KW32/
│ ├── Artist - Titel.mp3
│ └── Artist - Titel (Extended Mix).mp3
└── 2026-KW34/
├── Die_JunX_Rhythmus_Der_Nacht_.mp3
├── Vanessa_Mai__Ich_Fall_In_Love_.mp3
└── RMC ONE - Be My Love (Original Mix).mp3
Keine ZIP-Dateien, keine WAV-Dateien, keine JPG-Cover, keine JSON-Dateien und keine zusätzlichen Unterordner. Das Cover steckt direkt im MP3.
Was die Automatisierung erledigt
E-Mail
↓
Bemusterung erkennen
↓
Download-Link auslesen
↓
Download
↓
ZIP entpacken
↓
WAV → MP3 320 kbit/s CBR
↓
ID3-Tags korrigieren
↓
Cover einbetten
↓
Dropbox / Jahr-Kalenderwoche
↓
Upload überprüfen
↓
lokale Dateien löschen
↓
Mail gelesen + Papierkorb
↓
Benachrichtigungsmail
Voraussetzungen
sudo apt update
sudo apt install -y \
python3 \
python3-venv \
python3-pip \
ffmpeg \
unzip \
rclone
Projekt anlegen
mkdir -p ~/promoly-auto
cd ~/promoly-auto
python3 -m venv .venv
source .venv/bin/activate
pip install playwright requests beautifulsoup4 mutagen
playwright install chromium
Die Projektstruktur sieht ungefähr so aus:
promoly-auto/
├── .venv/
├── promoly_worker.py
├── megamix_worker.py
├── promoly_tags.py
├── promoly_run.py
└── flatten_dropbox.py
Konfiguration
Zugangsdaten gehören nicht direkt in die Python-Skripte. Die Konfiguration liegt beispielsweise unter ~/.config/promoly-auto/config.json.
{
"imap_host": "imap.example.de",
"imap_port": 993,
"imap_user": "BENUTZERNAME",
"imap_password": "PASSWORT",
"imap_folder": "INBOX",
"smtp_host": "smtp.example.de",
"smtp_port": 465,
"notify_from": "dj@example.de",
"notify_to": "bemusterung@example.de",
"trash_folder": "Trash",
"rclone_target": "dropbox:Fox Party DJ Team/Promo Titel",
"max_per_run": 2,
"max_megamix_per_run": 2
}
mkdir -p ~/.config/promoly-auto
chmod 700 ~/.config/promoly-auto
chmod 600 ~/.config/promoly-auto/config.json
Bei 1blu sind beispielsweise IMAP über imap.1blu.de:993 und SMTP über smtp.1blu.de:465 mit SSL nutzbar.
Dropbox mit rclone anbinden
rclone config
rclone lsd "dropbox:Fox Party DJ Team"
Das Ziel lautet dropbox:Fox Party DJ Team/Promo Titel.
Promoly automatisieren
Promoly ist der komplexere Teil, weil ein einfacher HTTP-Download nicht ausreicht. Die E-Mails kommen beispielsweise von content@promolymail.com. Beim Abrufen über IMAP sollte BODY.PEEK[] verwendet werden, damit die Nachricht nicht schon durch das Lesen als gesehen markiert wird.
Feedback automatisch abgeben
Bei Bedarf wird über Playwright ein neutrales Feedback abgegeben: 3 Sterne und beispielsweise Thanks for the promo. Downloaded for review.. Vorher wird geprüft, ob bereits Your Feedback angezeigt wird.
MP3 bevorzugen, WAV als Fallback
Der Worker sucht bevorzugt Download Zip - MP3. Ist das nicht verfügbar, wird Download Zip - WAV verwendet. Weil Promoly die Download-Menüs teilweise dynamisch erzeugt, sollte nach klickbaren Elementen wie button, a, [role='menuitem'], [role='option'] gesucht werden.
WAV automatisch nach MP3 konvertieren
ffmpeg \
-i "Titel.wav" \
-codec:a libmp3lame \
-b:a 320k \
"Titel.mp3"
ID3-Tags
Mit mutagen werden Artist, Title, Year, Genre, Label, Version beziehungsweise Kommentar und das Cover gepflegt. Versionen wie Original Mix, Extended Mix, Radio Edit, Instrumental oder DJ Version können aus dem Dateinamen erkannt werden. Das Cover wird als APIC direkt in das MP3 geschrieben, gespeichert wird als ID3v2.3.
Megamix Promotion automatisieren
Megamix ist einfacher, weil die Mail direkte Download-Links enthält. Die Bemusterungen kommen beispielsweise von promotion@megamix-promotion.de. Die Mail enthält Interpret, Titel, Label, Cover und einen Link mit /protool/load.php?ID=. Dafür reicht requests.
Abgelaufene Links erkennen
Ältere Megamix-Links können trotz HTTP 200 eine HTML-Fehlerseite liefern. Deshalb zusätzlich den Content-Type prüfen:
ctype = response.headers.get(
"Content-Type", ""
).split(";")[0].lower()
if ctype == "text/html":
# Fehlerseite statt Audiodatei
Megamix-Tags erhalten
Megamix liefert teilweise bereits brauchbar getaggte MP3s. Deshalb werden vorhandene Tags nicht pauschal gelöscht. Artist, Title, Label, Year und Cover werden sicher aktualisiert; Genre und andere vorhandene Informationen bleiben erhalten, wenn die Mail dafür keinen besseren Wert liefert. Bereits gelieferte MP3-Dateien werden nicht erneut encodiert.
Nur MP3 direkt im KW-Ordner
2026-KW34/
├── RMC ONE - Be My Love (Original Mix).mp3
├── RMC ONE - Darkest Night (Original Mix).mp3
├── RMC ONE - I Dont Wanna Go (Original Mix).mp3
├── RMC ONE - No Regrets (Original Mix).mp3
├── Die_JunX_Rhythmus_Der_Nacht_.mp3
└── Vanessa_Mai__Ich_Fall_In_Love_.mp3
Doppeluploads vermeiden
Nicht den kompletten Kampagnenordner mit rclone hochladen. In einer frühen Version lag die Originaldatei im Kampagnenordner und die fertige MP3 zusätzlich unter files/. Dadurch konnte derselbe Titel zweimal in Dropbox landen.
Die fertige Version überträgt deshalb ausschließlich die bearbeiteten MP3-Dateien einzeln mit rclone copyto direkt in den jeweiligen Kalenderwochen-Ordner:
rclone copyto \
"/tmp/fertiger-titel.mp3" \
"dropbox:Fox Party DJ Team/Promo Titel/2026-KW34/fertiger-titel.mp3"
Upload einer einzelnen MP3 korrekt überprüfen
Nach der Umstellung auf die flache Struktur YYYY-KWxx/Titel.mp3 ist eine wichtige Besonderheit aufgefallen: rclone check eignet sich hier nicht für die Prüfung einer einzelnen Remote-Datei, weil es Verzeichnisse erwartet. Bei einem direkten Dateipfad kann dann ein Fehler wie is a file not a directory entstehen.
Deshalb wird jede hochgeladene MP3 mit rclone lsjson --stat geprüft. Anschließend wird die Dateigröße der Remote-Datei mit der lokalen MP3 verglichen.
rclone lsjson \
"dropbox:Fox Party DJ Team/Promo Titel/2026-KW34/Titel.mp3" \
--stat
Die Python-Logik sieht sinngemäß so aus:
r = subprocess.run(
[
"rclone",
"lsjson",
remote,
"--stat"
],
capture_output=True,
text=True
)
if r.returncode != 0:
raise RuntimeError(
f"Dropbox-Datei nicht gefunden: {mp3.name}"
)
remote_info = json.loads(r.stdout)
remote_size = int(remote_info.get("Size", -1))
local_size = mp3.stat().st_size
if remote_size != local_size:
raise RuntimeError(
f"Dropbox-Größe stimmt nicht: {mp3.name}"
)
Erst wenn jede einzelne MP3 einer Bemusterung erfolgreich hochgeladen und geprüft wurde, gilt die Mail als vollständig verarbeitet.
Mail wirklich erst nach dem letzten Track verschieben
Bei Mehrtitel-Releases ist die Reihenfolge besonders wichtig. Der Worker arbeitet die MP3-Dateien in einer Schleife ab. Erst nachdem der letzte Titel erfolgreich hochgeladen und mit lsjson --stat geprüft wurde, werden die lokalen Promo-Dateien gelöscht und die Quellmail gelesen markiert und nach Trash verschoben.
Track 1
→ Upload
→ Remote-Datei prüfen
→ OK
Track 2
→ Upload
→ Remote-Datei prüfen
→ OK
...
alle Tracks OK
→ lokale Arbeitsdateien löschen
→ Mail als gelesen markieren
→ Mail nach Trash verschieben
Diese Reihenfolge verhindert, dass eine Mail bereits verschwindet, obwohl nur ein Teil eines Releases erfolgreich verarbeitet wurde.
Zentrale Steuerung
Dropbox-Bestand vorher erfassen
↓
Promoly-Worker starten
↓
alle Tracks der Bemusterung hochladen
↓
jede einzelne Remote-MP3 prüfen
↓
erst danach Mail nach Trash
↓
Megamix-Worker starten
↓
auch dort alle Tracks einzeln prüfen
↓
Dropbox-Bestand nachher erfassen
↓
neue MP3s bestimmen
↓
Benachrichtigungsmail versenden
Eine Mehrtitel-Promo wird also erst dann abgeschlossen, wenn wirklich alle enthaltenen MP3-Dateien erfolgreich verarbeitet wurden. Schlägt die Prüfung bei einem Titel fehl, bleibt die Mail im Posteingang und wird beim nächsten Lauf erneut versucht.
Die Sammelmail basiert auf dem Unterschied zwischen dem Dropbox-Bestand vor und nach dem Lauf. Dadurch werden nur tatsächlich neu hinzugekommene MP3-Dateien gemeldet.
Bearbeitungsmenge begrenzen
"max_per_run": 2,
"max_megamix_per_run": 2
Für einen einmaligen Altbestand kann ./promoly_run.py --all verwendet werden.
Automatischer Start mit systemd
Service:
[Unit]
Description=Promoly DJ Promo Automation
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=USER
WorkingDirectory=/home/USER/promoly-auto
ExecStart=/home/USER/promoly-auto/.venv/bin/python /home/USER/promoly-auto/promoly_run.py --once
Timer:
[Unit]
Description=Check DJ promos
[Timer]
OnBootSec=2min
OnUnitActiveSec=10min
RandomizedDelaySec=30
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now promoly-auto.timer
systemctl status promoly-auto.timer --no-pager
Logs ansehen
journalctl -u promoly-auto.service -n 150 --no-pager
journalctl -u promoly-auto.service -f
Bestehenden Bestand aufräumen
Ein separates flatten_dropbox.py kann alte Kampagnen-Unterordner auflösen. Wichtig: zuerst Trockenlauf, dann erst ausführen.
./flatten_dropbox.py
./flatten_dropbox.py --execute
Dateien außerhalb der YYYY-KWxx-Struktur sollten dabei grundsätzlich nicht automatisch angefasst werden.
Sicherheit
chmod 700 ~/.config/promoly-auto
chmod 600 ~/.config/promoly-auto/config.json
Persönliche Promo-Links sollten nicht öffentlich protokolliert werden.
Das Ergebnis
Im Normalfall muss eine neue Promo-Mail nicht mehr manuell angefasst werden. Download, gegebenenfalls WAV-Konvertierung, ID3-Tags, Cover, der direkte Upload in YYYY-KWxx, die Einzeldatei-Prüfung per rclone lsjson --stat, das Aufräumen, das Verschieben der Mail nach Trash und die Benachrichtigung laufen automatisch.
Mail rein – fertig getaggte Musik raus.