Skip to main content

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/pick are IMSLP-only for now; another source's hit raises a TypeError naming the options it does take.

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"

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.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for scoreseek 0.0.5
File Size Uploaded Provenance
scoreseek-0.0.5.tar.gz 37.9 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for scoreseek 0.0.5
File Interpreter ABI Platform
scoreseek-0.0.5-py3-none-any.whl Python 3 none any Details

Total release size: 86.8 kB

Release files / scoreseek-0.0.5.tar.gz

Download URL scoreseek-0.0.5.tar.gz
Size 37.9 kB
Tags Source
SHA-256 checksum
How to use checksums
e761bf4f912f9fe66d0acaa4c6bcc14292d3433e59a04baae98219c1526f27bf
BLAKE2b-256 checksum
How to use checksums
fba4ed634eee4030c6e412f49a004f63186fef98182630d94d8c0c5a1b230fc4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","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.5-py3-none-any.whl

Download URL scoreseek-0.0.5-py3-none-any.whl
Size 49.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1c327a22765a895e0858bf1e6cb3ce479ebf0bb6b32d57c0b5c5cb83024eab73
BLAKE2b-256 checksum
How to use checksums
3ef9e499a19e1403c5d683929f959051521cbf2ce8438fd2774ac4acf1fd178a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","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.5

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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page