scoreseek
Find a musical score by metadata — across many sources, behind one call.
import scoreseek
hits = scoreseek.search("Cooley's") # out of the box: The Session (CC-BY-SA)
ref = hits[0]
path = ref.fetch() # -> a local .abc file
# hand straight to audiate.render(path) to hear it
Every hit is a license-tagged ScoreRef, so public-domain / permissive
results stay cleanly separated from copyrighted / gray ones — searches are
copyright-safe by default.
Install
pip install scoreseek # zero required dependencies
pip install "scoreseek[render]" # + audiate, to render hits to audio
Why
Scores live in many places — public-domain corpora (PDMX, IMSLP, OpenScore),
downloadable datasets, chord/lyric collections, and your own folders. scoreseek
puts them behind one pluggable-source interface and tags every result with a
license, so you can ask for "only what I can legally build on" or opt into
copyrighted material explicitly.
Sources
A source answers search(query) -> [ScoreRef] and optionally fetch(ref) -> path.
Register any object with that shape.
On by default (zero-config, lightweight, cleanly licensed):
| Source | What | License |
|---|---|---|
thesession |
thesession.org traditional tunes (ABC) | CC-BY-SA 4.0 |
openscore_lieder |
~1,356 OpenScore art songs (compressed MusicXML) | CC0 |
Opt-in (import from scoreseek.sources and register_source(...) — heavier, or license-gated):
| Source | What | License | Notes |
|---|---|---|---|
IMSLPSource |
IMSLP/Petrucci public-domain classical | Public Domain | live API; mostly PDF, some MIDI; size-capped fetch (see below) |
PDMXSource |
250K Public-Domain MusicXML corpus | PD / CC0 | local corpus — one-time download (see below) |
ChordonomiconSource |
680K chord progressions | CC-BY-NC (gray lane) | no title/artist — genre/chord-text search |
KaggleChordsSource |
135K chords+lyrics songs (via sung) |
scraped / gray | needs Kaggle creds + a ~283 MB download |
LocalFolderSource |
a folder of .mid/MusicXML/ABC/kern files |
you set it | offline |
WebMidiSource |
free-MIDI websites (bitmidi, midis101, midiworld): pop, film, game music | scraped / gray | HTML scraping, best effort; add sites with MidiSite |
LakhMidiSource |
Lakh MIDI Dataset clean_midi (~17K Artist/Title.mid) |
scraped / gray | local: the 234 MB tarball (searched unextracted) or the folder |
from scoreseek import register_source, License
from scoreseek.sources import IMSLPSource, LocalFolderSource
register_source(IMSLPSource()) # opt in to IMSLP
register_source(LocalFolderSource("~/scores", license=License.PUBLIC_DOMAIN))
hits = scoreseek.search("nocturne", composer="Chopin", sources=["imslp"])
hits = scoreseek.search("hey jude", allow_copyrighted=True) # opt into gray/copyrighted
# film / pop MIDI from the web (gray lane: private study)
from scoreseek.sources import WebMidiSource, LakhMidiSource
register_source(WebMidiSource())
register_source(LakhMidiSource("~/Downloads/clean_midi.tar.gz"))
hits = scoreseek.search("star wars", allow_copyrighted=True)
Choosing an IMSLP file (and not downloading 800 MB)
An IMSLP hit is a whole work page: full scores, parts, arrangements and MIDI
renditions, some of them hundreds of MB. Hits carry their formats, ref.files() lists every file with its
kind and size, and fetch takes a size cap and a choice strategy:
from scoreseek.picks import smallest
ref = scoreseek.search("Symphony No.5", composer="Beethoven", sources=["imslp"])[0]
ref.formats # ('pdf', 'zip')
for f in ref.files(): # ScoreFile(filename, kind, size, license, section, ...)
print(f.kind, f.size, f.section, f.filename)
ref.fetch() # default: <= 50 MB; a PD/CC0 file first, then MIDI/MusicXML,
# a score over a part, then IMSLP's page order
ref.fetch(fmt="pdf") # only PDFs (fmt="midi" raises if the page has no MIDI)
ref.fetch(pick=smallest) # a strategy: (List[ScoreFile]) -> ScoreFile
ref.fetch(pick="PMLP01586-BH1826fsc.pdf") # a filename from ref.files()
ref.fetch(max_bytes=None) # no cap
When every candidate is over the cap, fetch raises ScoreTooLargeError naming
the smallest ones. If a file IMSLP lists without a size turns out too big while
downloading, the next choice is tried. Set source-wide defaults with
IMSLPSource(max_bytes=..., pick=...).
max_bytes and pick work on every hit, so one loop serves mixed results:
for ref in scoreseek.search("waltz"):
path = ref.fetch(max_bytes=5_000_000) # ScoreTooLargeError if over
Sources that download (IMSLP, OpenScore, The Session, WebMidi, Chordonomicon)
default to a 50 MB cap; local corpora (folders, PDMX, Lakh, Kaggle) to none. IMSLP
caps the download as it streams; other sources check the fetched file. Hits with
several files (IMSLP, PDMX's MusicXML + MIDI) support ref.files() and
pick=<filename>; on a one-file source a pick strategy has nothing to choose.
Canonicalize a messy query (MusicBrainz)
normalize=True resolves a fuzzy "title [+ artist]" to a canonical work via
MusicBrainz before searching (adds a ~1 s network round-trip), and stamps the
resolved MBID onto each hit:
hits = scoreseek.search("moonlight sonata beethovan", normalize=True)
PDMX: a local public-domain corpus
PDMX ships only as bulk tarballs, so PDMXSource searches a local extraction.
Download once (index-only = 225 MB; + MusicXML = ~2.1 GB) from
Zenodo record 15571083 and point the
source at it:
from scoreseek.sources import PDMXSource
register_source(PDMXSource("~/pdmx")) # dir with PDMX.csv (+ extracted mxl/, mid/)
search() works with just PDMX.csv; fetch() needs the extracted mxl//mid/
trees and otherwise raises with the exact tarball URL to download.
Write your own source
from scoreseek.sources import Source
from scoreseek.base import License
class MySource(Source):
name = "mine"
license = License.CC0
def search(self, query="", *, title="", composer="", limit=10):
return [self._ref(title="...", id="...", formats=("musicxml",))]
def fetch(self, ref, *, fmt=None):
...
return "/path/to/score.musicxml"
ScoreRef.fetch enforces max_bytes on the returned file for you (set a class
attribute max_bytes for a default cap). Take max_bytes / pick in your own
fetch, and implement files(ref), when you can do better: cap a download while
it streams, or choose among several files.
License tags
License classifies each hit: PUBLIC_DOMAIN, CC0, PERMISSIVE (CC-BY/-SA,
MIT, …), NONCOMMERCIAL (CC-BY-NC), COPYRIGHTED, GRAY (scrapes/unspecified),
UNKNOWN.
search(..., allow_copyrighted=False) (the default) is copyright-safe: it
returns the commercial-safe lanes (PUBLIC_DOMAIN/CC0/PERMISSIVE) plus
UNKNOWN (unclassified provenance, e.g. your own local folders), and drops the
restricted lanes — COPYRIGHTED, GRAY, and NONCOMMERCIAL (a known usage
restriction). Pass allow_copyrighted=True to include them.
Where it fits
scoreseek is the search stage of a larger pipeline:
scoreseek (find a score) → audiate
(render to audio) → arioso
(AI-enhance).
License
MIT
Metadata
Release files for scoreseek 0.0.7
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| scoreseek-0.0.7.tar.gz | 41.1 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| scoreseek-0.0.7-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 93.6 kB
Release files / scoreseek-0.0.7.tar.gz
| Download URL | scoreseek-0.0.7.tar.gz |
|---|---|
| Size | 41.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b0ac4019ee443d37ebdcb554ba55cf61faf7d492c50592d8cb093110fdac34fa
|
|
BLAKE2b-256 checksum How to use checksums |
d5a4c16960e8fe8c27b0bde7d6dd53a4fd26c859481dd0a1df9d545fbfae59b9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / scoreseek-0.0.7-py3-none-any.whl
| Download URL | scoreseek-0.0.7-py3-none-any.whl |
|---|---|
| Size | 52.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
efd08d0947797f37154466071e7079f49fa67a1aa0dfbe720aa06d1f5661f532
|
|
BLAKE2b-256 checksum How to use checksums |
7556530d9f3afe042b7d2d2c463574d2e0728a30ceee9d4e40882144d7d25bf2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for scoreseek 0.0.7
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.