docs: document packages, exported API and non-obvious logic
This commit is contained in:
@@ -15,6 +15,15 @@ import (
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
// songbpm.com publishes the tempo and key as prose rather than as structured
|
||||
// data, so these match the surrounding sentence instead of a CSS selector.
|
||||
// The attribute wildcards absorb the utility classes the site regenerates on
|
||||
// every deploy, but the wording itself is load bearing: if the sentence
|
||||
// changes, the lookup starts returning no data. The double space in modeRegex
|
||||
// is present in the real markup and is not a typo.
|
||||
//
|
||||
// The key pattern accepts both the typographic accidentals the page renders
|
||||
// and their ASCII equivalents, since which one appears varies by track.
|
||||
var (
|
||||
bpmRegex = regexp.MustCompile(`tempo of <span[^>]*>(\d+) BPM`)
|
||||
keyRegex = regexp.MustCompile(`with a <span[^>]*>([A-G](?:♯|#|♭|b)?(?:/[A-G](?:♯|#|♭|b)?)?)</span> key`)
|
||||
@@ -35,6 +44,14 @@ func fetchBPM(ctx context.Context, httpClient *http.Client, artist, title, durat
|
||||
return parseBPM(html)
|
||||
}
|
||||
|
||||
// findTrackURL searches songbpm.com and returns the page for the track.
|
||||
//
|
||||
// Artist and title alone are not enough to identify a track, since the search
|
||||
// happily returns remixes, live versions and covers under the same names.
|
||||
// Duration is used as the tiebreaker, with a couple of seconds of tolerance
|
||||
// to absorb the disagreement between Deezer's rounding and songbpm's. No
|
||||
// match within tolerance is treated as not found rather than guessed at,
|
||||
// because a wrong BPM is worse than a missing one.
|
||||
func findTrackURL(ctx context.Context, httpClient *http.Client, artist, title, duration string) (string, error) {
|
||||
const rootURL = "https://songbpm.com"
|
||||
|
||||
@@ -133,6 +150,13 @@ func fetchBPMPage(ctx context.Context, httpClient *http.Client, pageURL string)
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
// parseBPM extracts the tempo and musical key from a track page.
|
||||
//
|
||||
// All three patterns must match: a page with a tempo but no key is treated as
|
||||
// no data, since a half filled tag is not worth writing. Enharmonic keys are
|
||||
// published as pairs like "C#/Db" and only the first spelling is kept, the
|
||||
// accidentals are folded to ASCII for tag compatibility, and a minor mode is
|
||||
// encoded with a trailing "m" to match the convention DJ software expects.
|
||||
func parseBPM(html string) (bpmKey, error) {
|
||||
bpmMatch := bpmRegex.FindStringSubmatch(html)
|
||||
keyMatch := keyRegex.FindStringSubmatch(html)
|
||||
|
||||
Reference in New Issue
Block a user