No description
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
jonas 7ac67bd892 SheetMusicDirect Downloader: Skripte, GUI und OMR-Tagging
Enthält download_smd.py (Capture/Crop/OCR + Parallel-Worker), die Tkinter-GUI,
tag_scores.py (Besetzung/Tonumfang via Audiveris+music21) und Hilfsskripte.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 01:11:46 +02:00
.gitignore SheetMusicDirect Downloader: Skripte, GUI und OMR-Tagging 2026-06-03 01:11:46 +02:00
download_smd.py SheetMusicDirect Downloader: Skripte, GUI und OMR-Tagging 2026-06-03 01:11:46 +02:00
download_smd_gui.py SheetMusicDirect Downloader: Skripte, GUI und OMR-Tagging 2026-06-03 01:11:46 +02:00
inspect_noteflight.py SheetMusicDirect Downloader: Skripte, GUI und OMR-Tagging 2026-06-03 01:11:46 +02:00
README.md SheetMusicDirect Downloader: Skripte, GUI und OMR-Tagging 2026-06-03 01:11:46 +02:00
requirements.txt SheetMusicDirect Downloader: Skripte, GUI und OMR-Tagging 2026-06-03 01:11:46 +02:00
sheetmusicdirect-downloader.desktop SheetMusicDirect Downloader: Skripte, GUI und OMR-Tagging 2026-06-03 01:11:46 +02:00
tag_scores.py SheetMusicDirect Downloader: Skripte, GUI und OMR-Tagging 2026-06-03 01:11:46 +02:00

SheetMusicDirect Downloader

Lädt Noten von sheetmusicdirect.com per Selenium-Screenshots, baut daraus ein PDF und optional einen Tesseract-Textlayer sowie MusicXML (Audiveris).

Voraussetzung: Du hast die Rechte am Material (z.B. Abo, gekauftes Stück, eigene Veröffentlichungen). Sheet-Music-Direct verbietet den Download außerhalb der eigenen App — dieses Tool ist für die persönliche, autorisierte Nutzung gedacht.


Komponenten

Datei Zweck
download_smd.py CLI-Hauptscript (Selenium + PDF + OCR/OMR)
download_smd_gui.py Tkinter-GUI als Wrapper um das CLI-Script
sheetmusicdirect-downloader.desktop KDE-Menüeintrag
requirements.txt Python-Dependencies
.venv/ Python venv (lokal angelegt)
.chrome-profile/ Persistentes Chromium-Profil (Cookies/Login)
downloads/ Ausgabe-Verzeichnis pro Stück

Installation

# Python-Dependencies
python -m venv .venv
.venv/bin/pip install -r requirements.txt

# System-Tools (Arch / CachyOS)
sudo pacman -S tk tesseract tesseract-data-deu tesseract-data-eng \
                chromium libnotify
paru -S audiveris        # optional, nur für MusicXML

# KDE-Menüeintrag
cp sheetmusicdirect-downloader.desktop ~/.local/share/applications/

tk ist für Tkinter (GUI). libnotify für Desktop-Notifications. audiveris ist optional — wenn nicht installiert, wird MusicXML-Export einfach übersprungen.


Verwendung

GUI

.venv/bin/python download_smd_gui.py

Oder über KDE-Menü („Multimedia → SheetMusicDirect Downloader").

Felder ausfüllen → Start → im Browser einloggen → „Login fertig → Weiter" klicken (Button wird aktiv, sobald das Script soweit ist). Live-Log unten zeigt Fortschritt. KDE-Notification kommt bei Cloudflare-Check und am Ende des Laufs.

Im Abschnitt Verhalten steuert „Bereits vorhandene Downloads überspringen" (default an), ob Stücke mit bereits existierender PDF erneut gezogen werden — praktisch zum Wiederaufsetzen abgebrochener Batch-Läufe.

CLI

Einzelnes Stück:

.venv/bin/python download_smd.py --no-omr \
  --url "https://www.sheetmusicdirect.com/de-DE/se/ID_No/1255185/Product.aspx"

Ganze Kategorie/Suchergebnis (Batch):

.venv/bin/python download_smd.py --no-omr --list-url \
  "https://www.sheetmusicdirect.com/de-DE/Search.aspx?query=Film+Choir&arr=108&page=1"

Ordnername wird automatisch aus <h1> / <title> der Produktseite abgeleitet (alles vor „VON/BY/BEI…" abgeschnitten, all-caps → Title-Case).


Workflow

Single-Mode

  1. Browser geht auf die Produktseite
  2. Du loggst dich ein und öffnest den Viewer (Vorschau / Anzeigen)
  3. Enter / „Weiter"
  4. Script navigiert direkt zur iframe-src (Top-Frame, mehr Auflösung)
  5. Bei Cloudflare-Wand → Pause + Desktop-Notification; nach 2 Min Auto-Resume, sobald die Challenge passiv durchgegangen ist
  6. Screenshot-Loop bis letzte Seite (button.pager.page-right disabled oder zwei identische Bilder)
  7. Pro Seite: Trim Footer → Auto-Crop → Margin
  8. PDF zusammenbauen → Tesseract-Layer → ggf. Audiveris-MusicXML

Batch-Mode (--list-url)

  1. Browser geht auf Listen-URL
  2. Login + Enter
  3. URL-Cache prüfen: liegt unter downloads/.url-cache/<hash>.json (Hash der list-url). Wenn vorhanden → Schritt 4 überspringen. Mit --refresh-list erzwingt man das Neusammeln
  4. collect_product_urls() sammelt alle Product.aspx-Links inkl. Pagination (URL ?page=N erhöhen, fallback auf next-Button / infinite scroll), Ergebnis wird in den Cache geschrieben
  5. Für jede URL: download_one() wie Single-Mode
  6. Schon vorhandene PDFs werden übersprungen (idempotent, default; via --no-skip-existing deaktivierbar) → Abgebrochene Läufe sind so wiederaufnehmbar: Cache liefert die URL-Liste, Skip-Existing überspringt schon fertige Stücke
  7. Stücke mit nur Noteflight-Player werden über den Noteflight-Pfad capturt (SVG-Screenshot pro Seite, Pfeiltasten-Navigation)
  8. Cloudflare-Pausen sind autonom überbrückbar → Batch-Lauf braucht keine Anwesenheit nach dem initialen Login

Wichtige CLI-Optionen

Flag Default Wirkung
--url URL — Produktseite direkt öffnen (Single-Mode)
--list-url URL — Batch-Mode über Kategorie/Suchergebnis
--max-list-pages N 0 Hartes Limit für Pagination-Iterationen; 0 = unbegrenzt (komplette Kategorie). Positiver Wert = Sperre (z.B. 200 ≈ ~5000 Songs)
--margin N 120 Weißer Rand um jede Seite (Pixel)
--viewport B,H,DPR 2400,1800,2 CDP-Render-Viewport (unabh. vom OS-Fenster)
--window B,H 1600,1200 OS-Browserfenster-Startgröße
--scale N 2.0 devicePixelRatio (Schärfe ↔ RAM)
--no-crop — Auto-Crop deaktivieren
--no-trim-footer — DRM-Subscription-Footer NICHT wegcroppen
--no-hide — Pager/Footer nicht verstecken (Debug)
--no-ocr — Tesseract-Textlayer überspringen
--no-omr — Audiveris-Export überspringen
--no-skip-existing — Bereits heruntergeladene Stücke nicht überspringen (default: skippen)
--refresh-list — URL-Cache ignorieren und Produkt-URLs neu sammeln (default: gecached aus downloads/.url-cache/)
--no-stealth — undetected-chromedriver deaktivieren, Standard-Selenium nutzen (default: Stealth an)
--inspect — DOM nach Login analysieren, Selektor-Kandidaten ausgeben

Bildverarbeitung

Pro Screenshot in dieser Reihenfolge:

  1. trim_drm_footer() — SMD brennt eine Zeile SheetMusicDirect.com Monthly Subscription for <Name>… direkt ins PDF. Heuristik: nach „Hintergrund-Lücke" in den unteren 400px suchen, oberhalb croppen.
  2. auto_crop() — pro Spalte/Zeile zählen, wieviele Pixel < 200 (dark) sind. Ränder ohne Inhalt werden weggeschnitten. Histogram-basiert, robust gegen Einzelpixel-Rauschen am Rand.
  3. add_margin() — weißer Rand drumherum (Default 120px).

Anti-Sabotage-Mechanik

SMD/ng2-pdf-viewer hat eingebaute Stolpersteine. Lösungen:

  • Pager-Pfeile am Bildrand → per JS opacity: 0 versteckt (klickbar bleibt). Selenium-Klick umgangen via arguments[0].click(), weil element_to_be_clickable opacity:0 als „nicht klickbar" wertet.
  • Subscription-Footer im DOM → per Pattern-Match auf Klassennamen und Textinhalt versteckt.
  • Subscription im PDF eingebrannt → durch trim_drm_footer() aus dem Bild entfernt.
  • Kleiner iframe-Slot → Viewer wird direkt aufgerufen statt im iframe (Top-Frame, voller Browserrender).
  • Render-Auflösung an OS-Fenstergröße gekoppelt → CDP Emulation.setDeviceMetricsOverride virtualisiert den Viewport auf 2400×1800 @ DPR 2 (4800×3600 px Screenshots) unabhängig vom echten Fenster.
  • Cloudflare-Wand bei zu vielen schnellen Requests → wird per DOM-/Text-Marker erkannt, Viewport-Override temporär aufgehoben (damit du die CF-Buttons siehst), Script pausiert + sendet KDE-Notification. wait_for_cloudflare() pollt parallel auf stdin (User-Enter) und alle 5 s auf die Challenge selbst — sobald die Challenge weg ist und ≥ 120 s vergangen sind, läuft das Script autonom weiter (>>> Auto-Resume nach …s). Damit braucht ein Batch-Lauf keine ständige Anwesenheit; CF-Passive-Checks gehen i. d. R. von selbst durch. Bei manuellem Enter wird der Wait sofort beendet.
  • Häufige CF-Re-Challenges (alle ~10 Min) → standardmäßig nutzt der Driver undetected-chromedriver: gepatchter Chromedriver ohne cdc_*-Marker, kein --test-type=webdriver-Flag, navigator.webdriver = false. SMD/Cloudflare flaggt die Session dadurch weniger aggressiv, cf_clearance-Cookie hält länger. Bei Bedarf via --no-stealth deaktivierbar (Fallback auf Standard-Selenium).
  • Network-Errors / unvollständige Page-Loads → navigate_with_retry() checkt nach jedem driver.get() ob die Seite sauber kam (chrome-error://, ERR_*-Titel, fehlende SMD-Chrome, Stylesheet-Count). Bei Bruch reload mit Backoff (1 → 2 → 4 s, 3 Versuche). Nach CF-Pass nochmal Health-Check, falls SMD die echte Seite nicht sauber nachlädt.

Unterstützte Viewer

Viewer iframe-ID Status
ng2-pdf-viewer (tools.sheetmusic.direct) staticScoreViewer ✅ voll unterstützt
Halleonard PDF-Preview ifConsumerPreview ✅ unterstützt (gleiche Engine)
Noteflight (interaktiv) performanceProduct ✅ unterstützt — SVG-Screenshot pro Seite, Pfeiltasten-Navigation, Print im JWT serverseitig deaktiviert (disablePrinting:true), daher kein freier PDF-Pfad

Verzeichnisstruktur eines Ergebnisses

downloads/
└── Take Me Home, Country Roads (Arr. Roger Emerson)/
    ├── pages/
    │   ├── page_001.png   # gecropt + Margin
    │   └── …
    ├── Take Me Home….pdf      # Roh-PDF aus Bildern
    ├── Take Me Home….ocr.pdf  # mit Tesseract-Textlayer
    └── musicxml/              # Audiveris-Output (optional)
        └── *.xml

Troubleshooting

Symptom Ursache / Fix
TimeoutException beim ersten Capture Selektoren stimmen nicht — mit --inspect DOM analysieren
Nur eine Seite wird gespeichert Next-Klick geht nicht durch → _dump_pager_state() läuft automatisch, Output prüfen
Crop entfernt nichts Hintergrund zu dunkel oder Subscription-Text quer übers Bild → --no-trim-footer testweise aus
Bilder pixelig nach Fensterresize --viewport aktivieren (Default tut das schon)
CF-Button nicht klickbar Viewport-Override muss aufgehoben werden → passiert automatisch via wait_for_cloudflare()
Audiveris-Fehler „Could not export" Erkennungsproblem bei komplexer Notation, nicht der Installation — .omr ist trotzdem da, manuell in Audiveris-GUI nachbearbeiten
GUI: Strg+A markiert nichts Tk bindet das nicht von selbst — ist in der GUI manuell registriert

Architektur-Hinweise

  • Selenium 4 + Chromedriver (matched gegen Chromium). Persistentes Profil in .chrome-profile/, damit Cookies erhalten bleiben.
  • CDP über driver.execute_cdp_cmd() für Viewport-Override und Cleanup.
  • element.screenshot() statt ganzer Viewport — schneidet den iframe-Container präzise aus.
  • GUI-Subprocess wird mit python -u (unbuffered) gestartet. Stdout wird line-iterated und in tk.Text gepiped. Stdin wird per "\n" getriggert für die Login-/CF-Pausen. Die GUI erkennt Auto-Resume per Log-Pattern (Auto-Resume nach / Enter empfangen) und deaktiviert den Weiter-Button wieder, damit ein nachträglicher Klick keinen späteren input()-Aufruf abkürzt.
  • CF-Wait nicht-blockierend via select.select([sys.stdin], …, timeout) — funktioniert sowohl mit TTY als auch Pipe-stdin (GUI-Modus), beide nehmen den gleichen Code-Pfad.
  • Notifications via notify-send (libnotify) — KDE-konfigurierbar unter Anwendungsname „SheetMusicDirect Downloader".

Lizenz / Haftung

Dieses Tool ist ein persönliches Hilfsmittel. Verwende es nur für Inhalte, an denen du die Rechte hast (eigene Käufe, eigene Veröffentlichungen, autorisierte Backups). Massendownload-Aktionen, Weiterverteilung von Inhalten Dritter und Umgehung von Schutzmaßnahmen können gegen SMDs Nutzungsbedingungen und/oder Urheberrecht verstoßen.