songleaf.sources¶
Where songs are found: sources, and a search that composes them.
A source is any object with a name, a
search(query, *, title, artist, lyrics, limit) -> list[Hit] method and a
get(song_id) -> Song method that raises KeyError for an id it does not
have (get_song() reports that as SongNotFound; a source that cannot
load its data at all raises SourceUnavailable). search() and get_song() take
several through sources=; the default is the Kaggle chords-and-lyrics
corpus, read through sung.
A hit’s Hit.key ("<source>:<id>") names the song everywhere: it is
what get_song() resolves and what the store is keyed by.
search_tiers() is the one-field search: the songs whose title, artist or lyrics
match a query, each reported once, in the first of those that matches. A source may answer
it itself with a search_tiers(query, *, limit) method (the Kaggle sources do, in one
pass); any other source is asked three times through search.
Module Attributes
Where a copy of the corpus zip is found under the data root, before sung looks. |
|
a song is in the first that fits. |
Functions
The sources used when none are given, built once per process. |
|
|
The song named by |
The corpus zip under the data root ( |
|
|
Search every source and merge the hits, best first. |
|
Every source's songs whose title, artist or lyrics match |
Classes
|
One search result: a song a source can |
|
The Kaggle chords-and-lyrics corpus (~135K songs, chords over lyrics). |
|
A folder of files you exported yourself, indexed and searched by name. |
Exceptions
No song by that key, id or query. |
|
A source's data cannot be loaded here (a corpus that is not on this machine, say). |
- class songleaf.sources.Hit(source, id, title, artist='', score=0.0, meta=dict())[source]¶
Bases:
objectOne search result: a song a source can
get().- property key: str¶
"<source>:<id>", the song’s name forget_song()and the store.
- songleaf.sources.KAGGLE_CHORDS_ZIP = 'chords-and-lyrics/chords-and-lyrics-dataset.zip'¶
Where a copy of the corpus zip is found under the data root, before sung looks.
- class songleaf.sources.KaggleChordsSource(*, loader=None, min_score=70)[source]¶
Bases:
objectThe Kaggle chords-and-lyrics corpus (~135K songs, chords over lyrics).
Read through
sungfrom a local copy of the zip, with no Kaggle credentials needed when that copy exists. The corpus was scraped from a chords site: personal use only (licence taggray).Title and artist are matched fuzzily (typos, punctuation and word order do not matter); lyrics must contain every word of the
lyricsquery as a whole word. Lyrics matches are not ranked beyond popularity.- Parameters:
loader –
() -> pandas.DataFramewithcolumns. Defaults to sung’s loader of the local corpus zip.min_score (
float) – Fuzzy-match cutoff, 0-100.
- search(query='', *, title='', artist='', lyrics='', limit=10)[source]¶
Songs matching every given constraint, best first.
- search_tiers(query, *, limit=10)[source]¶
The songs whose title, artist or lyrics match
query, by where they match.{"title": [...], "artist": [...], "lyrics": [...]}, at mostlimitin each, best first. A song is in the first kind it matches, even when it ranks below the limit there, so a song is never shown as an artist match because more titles matched.Title and artist match word by word (
WordIndex): the query is inside the field ("wonder"finds Wonderwall) or the field is inside the query ("wonderwall oasis"finds the title Wonderwall and the artist Oasis), a word of four letters or more forgiving a typo; or every word of the query is in the title or in the artist ("paper boats invented band"). A song whose title holds a word of the query that its artist does not is a title match ("wonderwall oasis": Wonderwall (ver. 2) by Oasis), otherwise an artist match. Within each kind, the songs whose title and artist together hold the most words of the query come first (so Wonderwall by Oasis leads), then the closest by fuzzy score (of the title, or of title and artist, whichever is closer; of the artist), then the most popular. Lyrics hold every word of the query, rank by popularity, and carrymeta["snippet"]: the line that matched, with its spans.
- class songleaf.sources.LocalFolderSource(root=None, *, name='local_folder')[source]¶
Bases:
objectA folder of files you exported yourself, indexed and searched by name.
For files a chords/tab site lets a paying user export – Ultimate Guitar’s Guitar Pro/PDF downloads, MuseScore’s MusicXML/MSCZ/MIDI/PDF exports – with no automated route into either site (songleaf#5, songleaf#6): the user exports the file through the site as usual and drops it here. No network calls, no login; this only reads what is already on disk.
Text files (
.txt,.cho,.chopro,.crd,.pro) are parsed as chords-over-lyrics charts, the same format as the Kaggle corpus. Files with no lyrics to parse (Guitar Pro, MusicXML, MSCZ, MIDI, PDF) become aSongwith empty text and onescore_link()annotation pointing at the file, so a renderer can still show it as an attached score snippet.A file is named
"Artist - Title.ext"(the artist part is optional; a plain"Title.ext"works too).- Parameters:
>>> import tempfile, pathlib >>> root = tempfile.mkdtemp() >>> _ = (pathlib.Path(root) / "Nobody - Paper Boats.txt").write_text( ... "C\nla la la" ... ) >>> local = LocalFolderSource(root) >>> hit = local.search("paper boats")[0] >>> (hit.title, hit.artist) ('Paper Boats', 'Nobody') >>> local.get(hit.id).of_kind("chord")[0].body["symbol"] 'C'
- songleaf.sources.SONG_TIERS = ('title', 'artist', 'lyrics')¶
a song is in the first that fits.
- Type:
The kinds of match
search_tiers()reports, in order
- exception songleaf.sources.SongNotFound[source]¶
Bases:
KeyErrorNo song by that key, id or query. A
KeyError, soexcept KeyErrorstill catches it.
Bases:
RuntimeErrorA source’s data cannot be loaded here (a corpus that is not on this machine, say).
- songleaf.sources.default_sources()[source]¶
The sources used when none are given, built once per process.
The Kaggle corpus is read from its search index when one has been built (
songleaf build-index): the same songs and keys, in tens of MB rather than the GB the whole corpus takes in memory.- Return type:
- songleaf.sources.get_song(key, *, sources=None)[source]¶
The song named by
key("<source>:<id>"), from the source of that name.- Return type:
- songleaf.sources.kaggle_chords_zip()[source]¶
The corpus zip under the data root (
datasets/+KAGGLE_CHORDS_ZIP), if there is one.$SUNG_CHORDS_AND_LYRICS_ZIP, when set, wins: then this isNoneand sung reads the variable itself.
- songleaf.sources.search(query='', *, title='', artist='', lyrics='', limit=10, sources=None)[source]¶
Search every source and merge the hits, best first.
>>> search("paper boats", sources=[]) []
- songleaf.sources.search_tiers(query, *, limit=10, sources=None)[source]¶
Every source’s songs whose title, artist or lyrics match
query, by where they match.{"title": [...], "artist": [...], "lyrics": [...]}(SONG_TIERS), at mostlimitin each; each song in the first kind it matches. Within a kind, each source’s hits come in its own ranking, the sources in the order given.>>> search_tiers("paper boats", sources=[]) {'title': [], 'artist': [], 'lyrics': []}