npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@encode42/bandcamp-fetch

v1.2.3

Published

Scrape Bandcamp content (supports Cloudflare Pages)

Downloads

1,819

Readme

bandcamp-fetch

This fork removes Node dependencies to work with Cloudflare Pages. It might work in other environments that provide fetch as well!

  • node-cache has been replaced with a simple in-memory record store.
  • URL has been replaced with a barebones implementation.
  • EOL has been replaced with \\n.
    • Development outside of Linux might not be supported!
  • node-fetch has been removed, falling back to the environment's native fetch functions.

Additionally, some quality-of-life changes have been made.

  • description field has been added to Track.
  • Both the above and Album's description will use Linux newlines.
  • url option to replace albumUrl and trackUrl.
    • The original fields will still take priority!

This fork will likely not be maintained outside my own interest!


Library for scraping Bandcamp content.

Coverage:

  • Bandcamp Discover
  • Album and track info
  • Artists, labels, label artists, discography
  • Articles (aka. Bandcamp Daily)
  • Shows
  • Tags, including releases and highlights by tag
  • Search
  • Fan collections, wishlists and following artists / genres

Packaged as ESM + CJS hybrid module with typings.

Installation

npm i bandcamp-fetch --save

Usage

import bcfetch from 'bandcamp-fetch';

const results = await bcfetch.discovery.discover(...);

User Sessions

When you sign into Bandcamp, a "Cookie" is created to identify the user session. You can pass the value of this cookie to the library and gain access to your private collection as well as high-quality MP3 streams of purchased media:

bcfetch.setCookie('xxxx');

const album = await bcfetch.album.getInfo({
    albumUrl: '...'  // URL of purchased album
});

// Normal quality stream
const streamUrl = album.tracks[0].streamUrl;

// High quality stream - only available when `cookie` is set
const streamUrlHQ = album.tracks[0].streamUrlHQ;

Guide: How to obtain Cookie

BandcampFetch

The library exports a default BandcampFetch instance mainly for backward compatibility with previous versions:

// Imports the default `BandcampFetch` instance
import bcfetch from 'bandcamp-fetch';

You can also create separate instances. This is useful when you want to support multiple user sessions:

import { BandcampFetch } from 'bandcamp-fetch';

const bcfetch1 = new BandcampFetch({
    cookie: 'xxxx' // Cookie for user session 1
});

const bcfetch2 = new BandcampFetch();
bcfetch2.setCookie('yyyy'); // Cookie for user sesion 2

API

Discovery API

To access the Discovery API:

import bcfetch from 'bandcamp-fetch';

const discovery = bcfetch.discovery;

const options = await discovery.getAvailableOptions();
const results = await discovery.discover(...);

Methods:

Example (output)

Params

  • params: (DiscoverParams) (optional and all properties optional)
    • genre: (string)
    • subgenre: (string) only valid when genre is set to something other than 'all'.
    • location: (string)
    • sortBy: (string)
    • artistRecommendationType: (string) only valid when sortBy is 'rec' (artist recommended).
    • format: (string)
    • time: (number)
    • page: (number)
    • albumImageFormat: (string | number | ImageFormat)
    • artistImageFormat: (string | number | ImageFormat)

To see what values can be set in params, call getAvailableOptions().

Returns

Promise resolving to DiscoverResult.


Example (output)

Returns

Promise resolving to DiscoverOptions.


Example (output)

Params

Returns

Promise resolving to sanitized DiscoverParams.


Image API

To access the Image API:

import bcfetch, { ImageFormatFilter } from 'bandcamp-fetch';

const image = bcfetch.image;

const formats = await image.getFormats(ImageFormatFilter.Album);

Methods:

Example (output)

Params

  • filter: (ImageFormatFilter) (optional) if specified, narrows down the result to include only formats applicable to the specified value.

Returns

Promise resolving to Array<ImageFormat>.


Params

  • target: (string | number | ImageFormat)
    • If target is string or number, the method finds the image format with matching name or Id (as appropriate).
    • If target satisfies the ImageFormat interface constraint, then it is returned as is.
  • fallbackId: (number) (optional) if no match is found for target, try to obtain format with Id matching fallbackId.

Returns

Promise resolving to matching ImageFormat, or null if none matches target nor fallbackId (if specified).


Band API

A band can be an artist or label. To access the Band API:

import bcfetch from 'bandcamp-fetch';

const band = bcfetch.band;

const info = await band.getInfo(...);

Methods:

Example (output)

Params

The method tries to assemble the most complete set of data by scraping the following pages (returning immediately at any point the data becomes complete):

  1. The page referred to by bandUrl
  2. The 'music' page of the artist or label (bandUrl/music)
  3. The first album or track in the artist's or label's discography

Sometimes, label information is missing for artists even when they do belong to a label. If you know the labelId of the label that the artist belongs to, you can specify it in params. This will ensure that label will not be null in the artist info. If you pass a label URL to this function, you can find the labelId in the result.

Returns

Promise resolving to Artist or Label.


Example (output)

Params

Returns

Promise resolving to Array<LabelArtist>.


Example (output)

Params

Returns

Promise resolving to Array<Album | Track>.


Album API

To access the Album API:

import bcfetch from 'bandcamp-fetch';

const album = bcfetch.album;

const info = await album.getInfo(...);

Methods:

Example (output)

Params

Returns

Promise resolving to Album.

If artist URL is not found in the scraped data, then artist.url will be set to the same value as publisher.url


Track API

To access the Track API:

import bcfetch from 'bandcamp-fetch';

const track = bcfetch.track;

const info = await track.getInfo(...);

Methods:

Example (output)

Params

Returns

Promise resolving to Track.

If artist URL is not found in the scraped data, then artist.url will be set to the same value as publisher.url


Tag API

To access the Tag API:

import bcfetch from 'bandcamp-fetch';

const tag = bcfetch.tag;

const info = await tag.getInfo(...);
const highlights = await tag.getAlbumHighlights(...);

Methods:

Example (output)

Params

  • tagUrl: (string)

Returns

Promise resolving to Tag.


Example (output)

Returns

Promise resolving to TagList, which groups results into tags(for non-location tags) and locations (for location tags).


Example (output)

Albums are placed in groups. Each group corresponds to a highlight category such as 'new and notable' and 'all-time best selling'.

Params

Returns

Promise resolving to Array<AlbumHighlightsByTag>.


Example (output)

Params

  • params: (TagAPIGetReleasesParams)
    • tagUrl: (string)
    • imageFormat: (string | number | ImageFormat) (optional)
    • useHardcodedDefaultFilters: (boolean) (optional) if true, use hardcoded default values for filters not specified in params.filters. If false or unspecified, default filter values will be obtained by calling ``getReleasesAvailableFilters() (extra query means slower performance).
    • filters: (object{ string: string | number | Array<string | number >}) (optional)
    • page: (number) (optional) 1 if omitted.

params.filters:

Properties of params.filters are not strictly defined. As of this documentation, the curent filters available on Bandcamp are:

  • location: (number)
  • tags: (Array<string>) list of tags to match, in addition to the one referred to by params.tagUrl.
  • sort: (string)
  • format: (string)

Omitted properties are populated with default values obtained from params.tagUrl. Possible filter values can be obtained by calling getReleasesAvailableFilters(). For location and tag filters, you may look up additional values not returned by getReleasesAvailableFilters() through getSuggestions() of the Autocomplete API.

Returns

Promise resolving to ReleasesByTag.


Example (output)

For location and tag filters, this method does not return an exhaustive list of values. You may use getSuggestions() of the Autocomplete API to look up additional values.

Params

  • tagUrl: (string) the URL of the tag for which filter values are to be returned.

Returns

Promise resolving to Array<ReleasesByTag.Filter>.


Show API

To access the Show API:

import bcfetch from 'bandcamp-fetch';

const show = bcfetch.show;

const list = await show.list(...);

Methods:

Example (output)

Each list entry contains basic info about a show. To obtain full details, pass its url to getShow().

Params

Returns

Promise resolving to Array<Show>.


Example (output)

Params

Returns

Promise resolving to Show.


Article API

To access the Article API:

import bcfetch from 'bandcamp-fetch';

const article = bcfetch.article;

const list = await article.list(...);

Methods:

Example (output)

Returns

Promise resolving to Array<ArticleCategorySection>.


Example (output)

Params

Returns

Promise resolving to ArticleList.


Example (output)

Params

Returns

Promise resolving to Article.


Fan API

To access the Fan API:

import bcfetch from 'bandcamp-fetch';

const fan = bcfetch.fan;

const info = await fan.getInfo(...);
const collection = await fan.getCollection(...);

Methods:

Example (output)

Params

If username is not specified, result will be obtained for the user of the session tied to the BandcampFetch instance.

Returns

Promise resolving to Fan.


Example (output)

Params

  • params: (FanAPIGetItemsParams)
    • target: (string | FanItemsContinuation) (optional) if username (string) is specified, returns the first batch of items in the collection. To obtain further items, call the method again but, instead of username, pass continuation from the result of the first call. If there are no further items available, continuation will be null.
    • imageFormat: (string | number | ImageFormat) (optional)

If target is not specified, result will be obtained for the user of the session tied to the BandcampFetch instance.

Returns

Promise resolving to (FanPageItemsResult | FanContinuationItemsResult)<Album | Track>.


Example (output)

Params

  • params: (FanAPIGetItemsParams)
    • target: (string | FanItemsContinuation) (optional) if username (string) is specified, returns the first batch of items in the wishlist. To obtain further items, call the method again but, instead of username, pass continuation from the result of the first call. If there are no further items available, continuation will be null.
    • imageFormat: (string | number | ImageFormat) (optional)

If target is not specified, result will be obtained for the user of the session tied to the BandcampFetch instance.

Returns

Promise resolving to (FanPageItemsResult | FanContinuationItemsResult)<Album | Track>.


Example (output)

Params

  • params: (FanAPIGetItemsParams)
    • target: (string | FanItemsContinuation) (optional) if username (string) is specified, returns the first batch of artists and labels. To obtain further items, call the method again but, instead of username, pass continuation from the result of the first call. If there are no further items available, continuation will be null.
    • imageFormat: (string | number | ImageFormat) (optional)

If target is not specified, result will be obtained for the user of the session tied to the BandcampFetch instance.

Returns

Promise resolving to (FanPageItemsResult | FanContinuationItemsResult)<UserKind>.


Example (output)

Each genre is actually a Bandcamp tag, so you can, for example, pass its url to getReleases() of the Tag API.

Params

  • params: (FanAPIGetItemsParams)
    • target: (string | FanItemsContinuation) (optional) if username (string) is specified, returns the first batch of genres. To obtain further items, call the method again but, instead of username, pass continuation from the result of the first call. If there are no further items available, continuation will be null.
    • imageFormat: (string | number | ImageFormat) (optional)

If target is not specified, result will be obtained for the user of the session tied to the BandcampFetch instance.

Returns

Promise resolving to (FanPageItemsResult | FanContinuationItemsResult)<Tag>.


Search API

To access the Search API:

import bcfetch from 'bandcamp-fetch';

const search = bcfetch.search;

const albums = await search.albums(...);
const all = await search.all(...);

Methods:

Example (output)

  • all(params): search all item types
  • artistsAndLabels(params): search artists and labels
  • albums(params): search albums
  • tracks(params): search tracks
  • fans(params): search fans

Params

Returns

Promise resolving to SearchResults<T>, where T depends on the item type being searched and can be one of:

You can use the type property to determine the search result item type.


Autocomplete API

To access the Autocomplete API:

import bcfetch from 'bandcamp-fetch';

const autocomplete = bcfetch.autocomplete;

const suggestions = await autocomplete.getSuggestions(...);

Methods:

Example (output)

The value property of returned suggestions can be used to set the location or tags property (as the case may be) of params.filters that is passed into getReleases() of the Tag API.

Params

Returns

  • If params.itemType is AutocompleteItemType.Tag, a Promise resolving to Array<AutocompleteTag>.
  • If params.itemType is AutocompleteItemType.Location, a Promise resolving to Array<AutocompleteLocation>.

Stream API

Stream URLs returned by Bandcamp can sometimes be invalid (perhaps expired). Before playing a stream, you are recommended to test its URL and refresh it if necessary with the Stream API.

To access the Stream API:

import bcfetch from 'bandcamp-fetch';

const stream = bcfetch.stream;

// Test a stream URL
const streamURL = '...';
const testResult = await stream.test(streamUrl);

if (!testResult.ok) {
    const refreshedStreamURL = await stream.refresh(streamURL);
}

Methods:

Example (output)

Params

  • url: (string) the URL of the stream to test

Returns

Promise resolving to StreamTestResult:

  • ok: (boolean) whether the stream is valid
  • status: (number) the HTTP response status code returned by the test

Example (output)

Params

  • url: (string) the URL of the stream to refresh

Returns

Promise resolving to the refreshed URL of the stream or null if no valid result was obtained.


Rate Limiting

Each BandcampFetch instance comes with a rate limiter, which limits the number of requests made within a specific time period.

Rate limiting is useful when you need to make a large number of queries and don't want to run the risk of getting rejected by the server for making too many requests within a short time interval. If you get a '429 Too Many Requests' error, then you should consider using the rate limiter.

Each API has a limiter-enabled counterpart which you can access in the following manner:

import bcfetch from 'bandcamp-fetch';

// Album API - no limiter enabled
const albumAPI = bcfetch.album;

// Album API - limiter enabled
const limiterAlbumAPI = bcfetch.limiter.album;

Example (output)

The library uses Bottleneck for rate limiting. You can configure the rate limiter like this:

bcfetch.limiter.updateSettings({
    maxConcurrent: 10,  // default: 5
    minTime: 100        // default: 200
});

updateSettings() is just a passthrough function to Bottleneck. Check the Bottleneck doc for the list of options you can set.

Cache

Each BandcampFetch instance has an in-memory cache for two types of data (as defined by CacheDataType):

  1. CacheDataType.Page - pages fetched during scraping
  2. CacheDataType.Constants - image formats and discover options

To access the cache:

import bcfetch, { CacheDataType } from 'bandcamp-fetch';

const cache = bcfetch.cache;

cache.setTTL(CacheDataType.Page, 500);

Methods:

Params

  • type: (CacheDataType)
  • TTL: (number) expiry time in seconds (default: 300 for CacheDataType.Page and 3600 for CacheDataType.Constants)

Params

  • maxPages: (number)

Params


Changelog

1.2.1

  • Fix duration not returned in result of TrackAPI::getInfo() (#7)

1.2.0

  • Add Stream API for testing and refreshing stream URLs

1.1.1

  • Fix exports

1.1.0

  • Add support for user sessions through cookies. This means you can access your private collection and high-quality MP3 streams of purchased media.
  • Add ability to create multiple BandcampFetch instances

1.0.2

  • Improve parsing of album track info

1.0.1

  • Fix limiter throwing private access errors

1.0.0 (breaking changes!)

  • Move to TypeScript
  • Package as ESM + CJS hybrid module
  • Restructure API
  • Remove safe-eval dependency

0.3.1-b.1

  • Add getFanCollection() function

0.3.0-b.1

  • Add fan functions

0.2.2-b.1

  • Add itemType option to search params

0.2.1-b.20211020b

  • Fix URL sometimes null in result of getArtistOrLabelInfo()

0.2.1-b.20211020

  • Improve data fetching in getArtistOrLabelInfo()

0.2.0-b.20211020

  • Adapt to Bandcamp changes since last version
  • Add publisher and label to data fetched by getAlbumInfo() and getTrackInfo()
  • Add labelId to data fetched by getArtistOrLabelInfo(labelUrl)
  • Add labelId option to getArtistOrLabelInfo() for artist URLs

...(no changelog for earlier versions due to laziness)

License

MIT