# `MusicBrainz`
[🔗](https://github.com/alexdont/musicbrainz/blob/v0.1.0/lib/music_brainz.ex#L1)

The music of a film or a show, from [MusicBrainz](https://musicbrainz.org):
the soundtrack albums named after the title, and their tracks.

    {:ok, albums} = MusicBrainz.albums("Heat", year: 1995)
    #=> [%{id: "…", title: "Heat (Music From the Motion Picture)", year: 1995,
    #      artist: "Various Artists"}, %{title: "Heat: The Score", …}]

    {:ok, tracks} = MusicBrainz.tracks(hd(albums).id)
    #=> [%{n: 1, title: "Heat", artist: "Elliot Goldenthal feat. Kronos Quartet", secs: 461}, …]

    MusicBrainz.youtube([hd(tracks).artist, hd(tracks).title, "Heat"])
    #=> "https://www.youtube.com/results?search_query=Elliot+Goldenthal+feat.+Kronos+Quartet+Heat"

MusicBrainz is asked for *release groups* of secondary type **soundtrack**
whose name begins with the title's words (a search also matches on one
word: *Body Heat* is not *Heat*'s), for a film within a year of its
release (`:year`), oldest first — the original album before its expanded
editions, a show's seasons in order. An album's tracks are those of its
fullest official release, numbered through its discs; an artist credited
only in kana is named by the Latin sort name ("Kanno, Yoko" → "Yoko
Kanno"), so a search finds the song.

## Being a good client

MusicBrainz is free and keyless, and asks two things: a **user agent that
names your application** with a way to reach you, and **one request a
second**. Set the first:

    config :musicbrainz, user_agent: "MyApp/1.0 (https://example.com)"

and keep the second yourself: cache what comes back (albums for a month,
tracks for a year — they do not change) and do not ask in a loop. Nothing
here plays music and no YouTube key is needed: `youtube/1` is a search link.

Every request merges `:req_options` (per call or `config :musicbrainz,
:req_options`), so the client can be pointed at `Req.Test`.

# `album`

```elixir
@type album() :: %{
  id: String.t(),
  title: String.t(),
  year: integer() | nil,
  artist: String.t() | nil
}
```

A release group: `year` is its first release's, `artist` the credit as printed.

# `track`

```elixir
@type track() :: %{
  n: pos_integer(),
  title: String.t(),
  artist: String.t() | nil,
  secs: integer() | nil
}
```

A track of the album's fullest release, numbered through its discs; `secs` its length.

# `albums`

```elixir
@spec albums(String.t(), keyword()) :: {:ok, [album()]} | {:error, term()}
```

The soundtrack albums named after `name`, best first: `{:ok, [album]}`
(`{:ok, []}` when MusicBrainz lists none) or `{:error, reason}`.

Options:

  * `:year` — the film's release year: only albums first released within
    a year of it. Leave it out for a show, whose albums span its seasons.
  * `:names` — other names to try when the first finds nothing (the
    original title, say); the first name with albums wins.
  * `:limit` — albums at most (default 8).
  * `:req_options` — merged into the request.

# `parse_tracks`

```elixir
@spec parse_tracks(term(), pos_integer()) :: [track()]
```

The tracks of the fullest of these releases, numbered through its discs.

# `pick_albums`

```elixir
@spec pick_albums(term(), String.t(), pos_integer()) :: [album()]
```

The release groups of a search answer whose name begins with `name`'s
words, as albums: albums and EPs with a search score of 50 or more, oldest
first, `limit` at most.

# `query`

```elixir
@spec query(String.t(), keyword()) :: String.t()
```

The Lucene query `albums/2` sends: the name as a release-group title,
soundtracks only, and with `:year` a first release within a year of it.

# `tracks`

```elixir
@spec tracks(String.t(), keyword()) :: {:ok, [track()]} | {:error, term()}
```

The tracks of the album (a release-group id): those of its fullest
official release, `:limit` at most (default 60). `{:ok, []}` for a group
with no release listed.

# `youtube`

```elixir
@spec youtube([String.t() | nil]) :: String.t()
```

A YouTube search for these words (blanks dropped, a word said twice once).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
