Base Resources

You have the following resources available:

  • /artists
  • /artists/<int:artist_id>
  • /artists/<int:artist_id>/shows
  • /albums
  • /albums/<int:album_id>
  • /tracks
  • /tracks/<int:track_id>
  • /tracks/<int:track_id>/lyrics

And some meta resources:

  • /random/<str:resource_name>
  • /whatsnew
  • /clients
  • /about

/artists

Example response for the request GET /artists/3:

{
    "name": "Eterna Inocencia",
    "image": "http://userserve-ak.last.fm/serve/_/8339787/Eterna+Inocencia+Eterna.jpg",
    "uri": "/artists/3",
    "slug": "eterna-inocencia",
    "id": 3
}

Fields

  • id: The object’s ID.
  • image: Link to a photo. (Provided by last.fm)
  • name: The artist’s name.
  • slug: A slug of the artist’s name.
  • uri: The URI of this resource’s instance.

/artists/<int:id>/shows


Information provided by BandsInTown. This is the only resource that is not cached in the local database given to it’s dynamic nature.

Example response for the request GET /artists/1/shows:

[
    {
        "other_artists": [
            {
                "mbid": "5c210861-2ce2-4be3-9307-bbcfc361cc01",
                "facebook_tour_dates_url": "http://bnds.in/kVwY1Y",
                "image_url": "http://www.bandsintown.com/Pennywise/photo/medium.jpg",
                "name": "Pennywise",
            }
        ],
        "artists": [
            {
                "id": 1,
                "uri": "/artists/1"
            }
        ],
        "tickets_left": true,
        "title": "Lagwagon @ Commodore Ballroom in Vancouver, Canada",
        "venue": {
            "latitude": "49.2805760",
            "name": "Commodore Ballroom",
            "longitude": "-123.1207430"
        },
        "id": "6041814",
        "datetime": "Thu, 21 Feb 2013 19:00:00 -0000"

    }
]

Fields

  • other_artists: A list with artists that are not in Shiva’s database.
    • mbid: MusicBrainz.org ID.
    • facebook_tour_dates_url: URI to BandsInTown’s Facebook app for this artist.
    • image_url: URI to an image of the artist.
    • name: Name of the artist.
  • artists: A list of artist resources.
  • tickets_left: A boolean representing the availability (or not) of tickets for the concert.
  • title: The title of the event.
  • venue: A structure identifying the venue where the event takes place.
    • latitude: Venue’s latitude.
    • name: Venue’s name.
    • longitude: Venue’s longitude.
  • id: BandsInTown’s ID for this event.
  • datetime: String representation of the date and time of the show.

Parameters

The Shows resource accepts, optionally, two pairs of parameters:

  • latitude and longitude
  • country and city

By providing one of this two pairs you can filter down the result list only to a city. If only one of the pair is provided (e.g., only city) will be ignored, and if both pairs are provided, the coordinates will take precedence.

/albums

Example response for the request GET /albums/9:

{
    "artists": [
        {
            "id": 2,
            "uri": "/artists/2"
        },
        {
            "id": 5,
            "uri": "/artists/5"
        }
    ],
    "name": "NOFX & Rancid - BYO Split Series (Vol. III)",
    "year": 2002,
    "uri": "/albums/9",
    "cover": "http://userserve-ak.last.fm/serve/300x300/72986694.jpg",
    "id": 9,
    "slug": "nofx-rancid-byo-split-series-vol-iii"
}

Fields

  • artists: A list of the artists involved in that record.
  • cover: A link to an image of the album’s cover. (Provided by last.fm)
  • id: The object’s ID.
  • name: The album’s name.
  • slug: A slug of the album’s name.
  • uri: The URI of this resource’s instance.
  • year: The release year of the album.

Filtering

The album list accepts an artist parameter in which case will filter the list of albums only to those corresponding to that artist.

Example response for the request GET /albums?artist=7:

[
    {
        "artists": [
            {
                "id": 7,
                "uri": "/artists/7"
            }
        ],
        "name": "Anesthesia",
        "year": 1995,
        "uri": "/albums/12",
        "cover": "http://userserve-ak.last.fm/serve/300x300/3489534.jpg",
        "id": 12,
        "slug": "anesthesia"
    },
    {
        "artists": [
            {
                "id": 7,
                "uri": "/artists/7"
            }
        ],
        "name": "Kum Kum",
        "year": 1996,
        "uri": "/albums/27",
        "cover": "http://userserve-ak.last.fm/serve/300x300/62372889.jpg",
        "id": 27,
        "slug": "kum-kum"
    }
]

Giving ‘0’ instead you get the albums with no artist. If the argument is non-numeric you will get a 400 Bad Request error.

/tracks

Example response for the request GET /tracks/510:

{

    "ordinal": 4,
    "bitrate": 128,
    "slug": "dinosaurs-will-die",
    "album": {
        "id": 35,
        "uri": "/albums/35"
    },
    "title": "Dinosaurs Will Die",
    "artist": {
        "id": 2,
        "uri": "/artists/2"
    },
    "uri": "/tracks/510",
    "id": 510,
    "length": 180,
    "files": {
        "audio/mp3": "http://localhost:8080/nofx-pump_up_the_valuum/04. Dinosaurs Will Die.mp3",
        "audio/ogg": "/tracks/510/convert?mimetype=audio%2Fogg"
    }

}

Fields

  • album: The album to which this track belongs.
  • bitrate: In MP3s this value is directly proportional to the sound quality.
  • id: The object’s ID.
  • length: The length in seconds of the track.
  • ordinal: The ordinal of this track with respect to this album.
  • slug: A slug of the track’s title.
  • title: The title of the track.
  • uri: The URI of this resource’s instance.
  • files: A list of URIs to access the files in the different formats, according to the MEDIA_DIRS setting.

Filtering by artist

Example response for the request GET /tracks?artist=16:

[
    {
        "ordinal": 1,
        "bitrate": 196,
        "slug": "pay-cheque-heritage-ii",
        "album": {
            "id": 36,
            "uri": "/albums/36"
        },
        "title": "Pay Cheque (Heritage II)",
        "artist": {
            "id": 16,
            "uri": "/artists/16"
        },
        "uri": "/tracks/523",
        "id": 523,
        "length": 189,
        "files": {
            "audio/mp3": "http://localhost:8080/ftd-2003-sofa_so_good/01 For The Day - Pay Cheque (Heritage II).mp3",
            "audio/ogg": "/tracks/523/convert?mimetype=audio%2Fogg"
        }
    },
    {
        "ordinal": 2,
        "bitrate": 186,
        "slug": "in-your-dreams",
        "album": {
            "id": 36,
            "uri": "/albums/36"
        },
        "title": "In Your Dreams",
        "artist": {
            "id": 16,
            "uri": "/artists/16"
        },
        "uri": "/tracks/531",
        "id": 531,
        "length": 171,
        "files": {
            "audio/mp3": "http://localhost:8080/ftd-2003-sofa_so_good/02 For The Day - In Your Dreams.mp3",
            "audio/ogg": "/tracks/523/convert?mimetype=audio%2Fogg"
        }
    }
]

Giving ‘0’ instead you get the tracks with no artist. If the argument is non-numeric you will get a 400 Bad Request error.

Filtering by album

Example response for the request GET /tracks?album=18:

[

    {
        "album": {
            "id": 18,
            "uri": "/albums/18"
        },
        "length": 132,
        "files": {
            "audio/mp3": "http://localhost:8080/flip-keep_rockin/flip-01-shapes.mp3",
            "audio/ogg": "/tracks/277/convert?mimetype=audio%2Fogg"
        }
        "ordinal": 1,
        "title": "Shapes",
        "slug": "shapes",
        "artist": {
            "id": 9,
            "uri": "/artists/9"
        },
        "bitrate": 192,
        "id": 277,
        "uri": "/tracks/277"
    },
    {
        "album": {
            "id": 18,
            "uri": "/albums/18"
        },
        "length": 118,
        "files": {
            "audio/mp3": "http://localhost:8080/flip-keep_rockin/flip-02-stucked_to_the_ground.mp3",
            "audio/ogg": "/tracks/281/convert?mimetype=audio%2Fogg"
        }
        "ordinal": 2,
        "title": "Stucked to The Ground",
        "slug": "stucked-to-the-ground",
        "artist": {
            "id": 9,
            "uri": "/artists/9"
        },
        "bitrate": 192,
        "id": 281,
        "uri": "/tracks/281"
    }
]

Giving ‘0’ instead you get the tracks with no album. If the argument is non-numeric you will get a 400 Bad Request error.

Listing orphan tracks

It is possible to obtain the list of tracks with no album by giving ‘0’ to the album parameter. The same is true for the artist parameter. By combining both (/tracks?album=0&artist=0) you can get the list of “orphan” tracks.

Track creation

To create a track, POST a Multipart-Encoded file as a track argument to the /tracks resource. This is the only required parameter. If you include the arguments artist_id or album_id, it will take precedence and the file’s metadata for artist (or album) will be ignored. If you send multimple IDs, they will all be used, but if any of them doesn’t exist in the DB, the system will return a 400 Bad Request and the track won’t be saved.

curl -F "track=@file.mp3" -F "artist_id=1" -F "artist_id=17" http://127.0.0.1:9002/tracks

Use the query arguments hash_file and no_metadata to define if the file gets hashed and its metadata read.

curl -F "track=@file.mp3" http://127.0.0.1:9002/tracks?hash_file=true&no_metadata=true

/tracks/<int:id>/lyrics

Example response for the request GET /tracks/256/lyrics:

{
    "track": {
        "id": 256,
        "uri": "/tracks/256"
    },
    "source_uri": "http://lyrics.com/eterna-inocencia/my-family/",
    "id": 6,
    "uri": "/lyrics/6"
}

Fields

  • id: The object’s ID.
  • source_uri: The URI where the lyrics were fetched from.
  • track: The track for which the lyrics are.
  • uri: The URI of this resource’s instance.

Adding more lyric sources

Everytime you request a lyric, Shiva checks if there’s a lyric associated with that track in the database. If it’s there it will immediately retrieve it, otherwise will iterate over a list of scrapers, asking each one of them if they can fetch it. This list is in your local config file and looks like this:

SCRAPERS = {
    'lyrics': (
        'modulename.ClassName',
    ),
}

This will look for a class ClassName in shiva/lyrics/modulename.py. If more scrapers are added, each one of them is called sequentially, until one of them finds the lyrics and the rest are not executed.

Adding scrapers

If you want to add your own scraper just create a file under the lyrics directory, let’s say mylyrics.py with this structure:

from shiva.lyrics import LyricScraper

class MyLyricsScraper(LyricScraper):
    """ Fetches lyrics from mylyrics.com """

    def fetch(self, artist, title):
        # Magic happens here

        if not lyrics:
            return False

        self.lyrics = lyrics
        self.source = lyrics_url

        return True

And then add it to the scrapers list:

SCRAPERS = {
    'lyrics': (
        'modulename.ClassName',
        'mylyrics.MyLyricsScraper',
    ),
}

Remember that the fetch() method has to return True in case the lyrics were found or False otherwise. It must also store the URL where they were fetched from in self.source. That’s where Shiva looks for the information.

Shiva will not store the actual lyrics, only the URI where the lyric was found.

For more details check the source of the other scrapers.

The fulltree modifier

The three main resources accept a fulltree parameter when retrieving an instance. Those are:

  • /artists/<int:artist_id>
  • /albums/<int:album_id>
  • /tracks
  • /tracks/<int:track_id>

Whenever you set fulltree to any value that evaluates to True (i.e., any string except 'false' and '0') Shiva will include not only the information of the object you are requesting, but also the child objects.

Here’s an example response for the request GET /artists/2?fulltree=true:

{
    "name": "Eterna Inocencia",
    "image": "http://userserve-ak.last.fm/serve/_/8339787/Eterna+Inocencia+Eterna.jpg",
    "uri": "/artists/2",
    "events_uri": null,
    "id": 2,
    "slug": "eterna-inocencia",
    "albums": [
        {
            "artists": [
                {
                    "id": 2,
                    "uri": "/artists/2"
                }
            ],
            "name": "Tomalo Con Calma EP",
            "year": 2002,
            "uri": "/albums/2",
            "cover": "http://spe.fotolog.com/photo/30/54/51/alkoldinamita/1230537010699_f.jpg",
            "id": 2,
            "slug": "tomalo-con-calma-ep",
            "tracks": [
                {
                    "album": {
                        "id": 2,
                        "uri": "/albums/2"
                    },
                    "length": 161,
                    "files": {
                        "audio/mp3": "http://127.0.0.1:8001/eterna_inocencia/tomalo-con-calma.mp3",
                        "audio/ogg": "/tracks/27/convert?mimetype=audio%2Fogg"
                    }
                    "ordinal": 0,
                    "title": "02 - Rio Lujan",
                    "slug": "02-rio-lujan",
                    "artist": {
                        "id": 2,
                        "uri": "/artists/2"
                    },
                    "bitrate": 192,
                    "id": 27,
                    "uri": "/tracks/27"
                },
                {
                    "album": {
                        "id": 2,
                        "uri": "/albums/2"
                    },
                    "length": 262,
                    "files": {
                        "audio/mp3": "http://127.0.0.1:8001/eterna_inocencia/estoy-herido-en-mi-interior.mp3",
                        "audio/ogg": "/tracks/28/convert?mimetype=audio%2Fogg"
                    }
                    "ordinal": 0,
                    "title": "03 - Estoy herido en mi interior",
                    "slug": "03-estoy-herido-en-mi-interior",
                    "artist": {
                        "id": 2,
                        "uri": "/artists/2"
                    },
                    "bitrate": 192,
                    "id": 28,
                    "uri": "/tracks/28"
                },
            ]
        }
    ]
}

Using fulltree on tracks

The behaviour on a track resource is a little different. In the previous example tracks are the leaves of the tree, but when the full tree of a track is requested then all the scraped resources are also included, like lyrics.

This is not the default behaviour to avoid DoS’ing scraped websites when fetching the complete discography of an artist.

Note that if you request the list of tracks with fulltree, only the related resources will be included (i.e.: artists and albums) but not the scraped ones.

Using fulltree on artists

The tree for artists will contain the extra field no_album_tracks, which is simply a list of tracks that are not related to any album:

no_album_tracks: [
    {
        album: null,
        artist: {
            id: 4,
            uri: "/artists/4"
        },
        bitrate: 192,
        files: {
            audio/mp3: "http://127.0.0.1:8001/music/dead_fish-1998-sirva-se/14-dead_fish-the_party-buc.mp3",
            audio/ogg: "/tracks/82/convert?mimetype=audio%2Fogg"
        },
        id: 82,
        length: 1,
        ordinal: 0,
        slug: "14-dead-fish-the-party-buc",
        title: "14-dead fish-the party-buc",
        uri: "/tracks/82"
    }
],

Using slugs instead of IDs

In previous versions of Shiva it was possible to use slugs instead of ID to request a specific resource. This is not possible anymore, since we use UUID values as IDs, and it’s not possible to differentiate slugs from UUIDs without hitting the database every time. For performance reasons, slugs as record identifiers were discarded completely. However, slugs are still generated, stored, and present in the resulting JSON. Feel free to use them, but Shiva doesn’t commit to keeping them unique.

Uniqueness of slugs

Slugs are not unique. Shiva does not commit to keeping slugs unique. For this reason, don’t use them as identifiers.