diff --git a/README.md b/README.md index 803ad82..b66efec 100644 --- a/README.md +++ b/README.md @@ -286,6 +286,46 @@ Fetches the list of tags matching `params.q`. Results include both partial and f - q: the string to match - limit: the maximum number of results to return +### `getFanInfo(username, [options])` + +[**Example**](examples/getFanInfo.js) ([output](examples/getFanInfo_output.txt)) + +Fetches information about a fan. + +- `username` +- `options` (optional) + - imageFormat + +### `getFanWishlist(usernameOrContinuationToken, [options])` + +[**Example**](examples/getFanWishlist.js) ([output](examples/getFanWishlist_output.txt)) + +Fetches the list of albums / tracks added to a fan's wishlist. + +- `usernameOrContinuationToken`: if username is provided, returns the first batch of wishlist items. To obtain further items, call the function again but, instead of username, pass `continuationToken` from the result of the first call. If there are no further items available, `continuationToken` will be `null`. +- `options` (optional) + - imageFormat + +### `getFanFollowingArtistsAndLabels(usernameOrContinuationToken, [options])` + +[**Example**](examples/getFanFollowingArtistsAndLabels.js) ([output](examples/getFanFollowingArtistsAndLabels_output.txt)) + +Fetches the list of artists and labels followed by a fan. + +- `usernameOrContinuationToken`: if username is provided, returns the first batch of artists and labels. To obtain further items, call the function again but, instead of username, pass `continuationToken` from the result of the first call. If there are no further items available, `continuationToken` will be `null`. +- `options` (optional) + - imageFormat + +### `getFanFollowingGenres(usernameOrContinuationToken, [options])` + +[**Example**](examples/getFanFollowingGenres.js) ([output](examples/getFanFollowingGenres_output.txt)) + +Fetches the list of genres followed by a fan. Each genre is actually a Bandcamp tag, so you can, for example, pass its `url` value to `getReleasesByTag()`. + +- `usernameOrContinuationToken`: if username is provided, returns the first batch of genres. To obtain further items, call the function again but, instead of username, pass `continuationToken` from the result of the first call. If there are no further items available, `continuationToken` will be `null`. +- `options` (optional) + - imageFormat + ## Rate Limiting The API functions can be called with rate limiting like this: diff --git a/examples/getFanFollowingArtistsAndLabels.js b/examples/getFanFollowingArtistsAndLabels.js new file mode 100644 index 0000000..ba23e0a --- /dev/null +++ b/examples/getFanFollowingArtistsAndLabels.js @@ -0,0 +1,19 @@ +const bcfetch = require('../lib'); +const util = require('util'); + +const username = 'patrickkfkan'; + +const options = { + imageFormat: 'bio_featured' +} + +bcfetch.getFanFollowingArtistsAndLabels(username, options).then( async (results) => { + console.log(util.inspect(results, false, null, false)); + + if (results.continuationToken) { + console.log('Fetching more with continuation token...'); + + const moreResults = await bcfetch.getFanFollowingArtistsAndLabels(results.continuationToken); + console.log(util.inspect(moreResults, false, null, false)); + } +}); diff --git a/examples/getFanFollowingArtistsAndLabels_output.txt b/examples/getFanFollowingArtistsAndLabels_output.txt new file mode 100644 index 0000000..90e73c8 --- /dev/null +++ b/examples/getFanFollowingArtistsAndLabels_output.txt @@ -0,0 +1,402 @@ +{ + items: [ + { + name: 'M. Ward', + url: 'https://m-ward.bandcamp.com', + location: 'Portland, Oregon', + imageUrl: 'https://f4.bcbits.com/img/18177684_28.jpg' + }, + { + name: 'Allison Crutchfield', + url: 'https://allisoncrutchfield.bandcamp.com', + location: 'Philadelphia, Pennsylvania', + imageUrl: 'https://f4.bcbits.com/img/8598378_28.jpg' + }, + { + name: 'Coco Hames', + url: 'https://cocohames.bandcamp.com', + location: 'Memphis, Tennessee', + imageUrl: 'https://f4.bcbits.com/img/9197807_28.jpg' + }, + { + name: 'Neutral Milk Hotel', + url: 'https://neutralmilkhotel.bandcamp.com', + location: '', + imageUrl: 'https://f4.bcbits.com/img/13466114_28.jpg' + }, + { + name: 'Spoon', + url: 'https://spoontheband.bandcamp.com', + location: 'Austin, Texas', + imageUrl: 'https://f4.bcbits.com/img/21448647_28.jpg' + }, + { + name: 'Crooked Fingers', + url: 'https://crookedfingers.bandcamp.com', + location: 'North Carolina', + imageUrl: 'https://f4.bcbits.com/img/13404323_28.jpg' + }, + { + name: 'The Rock*A*Teens', + url: 'https://therockateens.bandcamp.com', + location: 'Atlanta, Georgia', + imageUrl: 'https://f4.bcbits.com/img/13223966_28.jpg' + }, + { + name: 'The Essex Green', + url: 'https://theessexgreen.bandcamp.com', + location: 'Brooklyn, New York', + imageUrl: 'https://f4.bcbits.com/img/13167432_28.jpg' + }, + { + name: 'She & Him', + url: 'https://sheandhim.bandcamp.com', + location: 'Los Angeles, California', + imageUrl: 'https://f4.bcbits.com/img/28704726_28.jpg' + }, + { + name: 'Ought', + url: 'https://ought.bandcamp.com', + location: 'Montréal, Québec', + imageUrl: 'https://f4.bcbits.com/img/11617874_28.jpg' + }, + { + name: 'Spider Bags', + url: 'https://spiderbags.bandcamp.com', + location: 'Chapel Hill, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/6370483_28.jpg' + }, + { + name: 'Tracyanne & Danny', + url: 'https://tracyanneanddanny.bandcamp.com', + location: 'UK', + imageUrl: 'https://f4.bcbits.com/img/12612375_28.jpg' + }, + { + name: 'Fucked Up', + url: 'https://fuckedup.bandcamp.com', + location: 'Toronto, Ontario', + imageUrl: 'https://f4.bcbits.com/img/7029567_28.jpg' + }, + { + name: "Swearin'", + url: 'https://swearin.bandcamp.com', + location: 'Philadelphia, Pennsylvania', + imageUrl: 'https://f4.bcbits.com/img/19671909_28.jpg' + }, + { + name: 'The Spinanes', + url: 'https://thespinanes.bandcamp.com', + location: 'Portland, Oregon', + imageUrl: 'https://f4.bcbits.com/img/14533028_28.jpg' + }, + { + name: 'Telekinesis', + url: 'https://telekinesis.bandcamp.com', + location: 'Seattle, Washington', + imageUrl: 'https://f4.bcbits.com/img/14904916_28.jpg' + }, + { + name: 'The Broken West', + url: 'https://thebrokenwest.bandcamp.com', + location: 'Los Angeles, California', + imageUrl: 'https://f4.bcbits.com/img/16185383_28.jpg' + }, + { + name: 'Titus Andronicus', + url: 'https://titusandronicus.bandcamp.com', + location: '', + imageUrl: 'https://f4.bcbits.com/img/15982887_28.jpg' + }, + { + name: 'SACRED//PAWS', + url: 'https://sacredpaws.bandcamp.com', + location: 'Glasgow, UK', + imageUrl: 'https://f4.bcbits.com/img/41645_28.jpg' + }, + { + name: 'Imperial Teen', + url: 'https://imperialteen.bandcamp.com', + location: 'San Francisco, California', + imageUrl: 'https://f4.bcbits.com/img/16255829_28.jpg' + }, + { + name: 'Ex Hex', + url: 'https://exhexband.bandcamp.com', + location: 'Washington, D.C.', + imageUrl: 'https://f4.bcbits.com/img/15244159_28.jpg' + }, + { + name: 'Apex Manor', + url: 'https://apexmanor.bandcamp.com', + location: 'Los Angeles, California', + imageUrl: 'https://f4.bcbits.com/img/15921186_28.jpg' + }, + { + name: 'David Kilgour', + url: 'https://davidkilgour.bandcamp.com', + location: 'Dunedin', + imageUrl: 'https://f4.bcbits.com/img/6370477_28.jpg' + }, + { + name: 'Gauche', + url: 'https://g-a-u-c-h-e.bandcamp.com', + location: 'Washington, D.C.', + imageUrl: 'https://f4.bcbits.com/img/16153963_28.jpg' + }, + { + name: 'Eric Bachmann', + url: 'https://ericbachmann.bandcamp.com', + location: 'North Carolina', + imageUrl: 'https://f4.bcbits.com/img/17551127_28.jpg' + }, + { + name: 'Little Scream', + url: 'https://littlescream.bandcamp.com', + location: 'Montréal, Québec', + imageUrl: 'https://f4.bcbits.com/img/5272073_28.jpg' + }, + { + name: 'The Love Language', + url: 'https://thelovelanguage.bandcamp.com', + location: 'Chapel Hill, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/13444279_28.jpg' + }, + { + name: 'Richard Buckner', + url: 'https://richardbuckner.bandcamp.com', + location: 'New York, New York', + imageUrl: 'https://f4.bcbits.com/img/17863325_28.jpg' + }, + { + name: 'Polvo', + url: 'https://polvonc.bandcamp.com', + location: 'Chapel Hill, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/18801929_28.jpg' + }, + { + name: 'Martin Frawley', + url: 'https://martinfrawley.bandcamp.com', + location: 'Australia', + imageUrl: 'https://f4.bcbits.com/img/18908062_28.jpg' + }, + { + name: 'The Music Tapes', + url: 'https://themusictapes.bandcamp.com', + location: 'Athens, Georgia', + imageUrl: 'https://f4.bcbits.com/img/17776773_28.jpg' + }, + { + name: 'Mt. Wilson Repeater', + url: 'https://mtwilsonrptr.bandcamp.com', + location: '', + imageUrl: 'https://f4.bcbits.com/img/19621062_28.jpg' + }, + { + name: 'Sweet Spirit', + url: 'https://sweetspirittheband.bandcamp.com', + location: 'Austin, Texas', + imageUrl: 'https://f4.bcbits.com/img/19349627_28.jpg' + }, + { + name: 'Jade Hairpins', + url: 'https://jadehairpins.bandcamp.com', + location: '', + imageUrl: 'https://f4.bcbits.com/img/18584374_28.jpg' + }, + { + name: 'Archers of Loaf', + url: 'https://archersofloaf.bandcamp.com', + location: 'North Carolina', + imageUrl: 'https://f4.bcbits.com/img/5368479_28.jpg' + }, + { + name: 'Mikal Cronin', + url: 'https://mikalcronin.bandcamp.com', + location: 'Los Angeles, California', + imageUrl: 'https://f4.bcbits.com/img/16983063_28.jpg' + }, + { + name: 'Lou Barlow', + url: 'https://loubarlow.bandcamp.com', + location: 'Massachusetts', + imageUrl: 'https://f4.bcbits.com/img/23720984_28.jpg' + }, + { + name: 'Sneaks', + url: 'https://sneaks.bandcamp.com', + location: 'Washington, D.C.', + imageUrl: 'https://f4.bcbits.com/img/19714164_28.jpg' + }, + { + name: 'H.C. McEntire', + url: 'https://hcmcentire.bandcamp.com', + location: 'Durham, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/20946952_28.jpg' + }, + { + name: 'William Tyler', + url: 'https://williamtyler.bandcamp.com', + location: 'Los Angeles, California', + imageUrl: 'https://f4.bcbits.com/img/18844022_28.jpg' + }, + { + name: 'The Clientele', + url: 'https://theclientele.bandcamp.com', + location: 'London, UK', + imageUrl: 'https://f4.bcbits.com/img/10580560_28.jpg' + }, + { + name: 'Bob Mould', + url: 'https://bobmould.bandcamp.com', + location: 'San Francisco, California', + imageUrl: 'https://f4.bcbits.com/img/20299197_28.jpg' + }, + { + name: 'Mike Krol', + url: 'https://mikekrol.bandcamp.com', + location: 'Los Angeles, California', + imageUrl: 'https://f4.bcbits.com/img/10450062_28.jpg' + }, + { + name: 'Fruit Bats', + url: 'https://fruit-bats.bandcamp.com', + location: '', + imageUrl: 'https://f4.bcbits.com/img/23214991_28.jpg' + }, + { + name: 'The Clean', + url: 'https://theclean.bandcamp.com', + location: 'New Zealand', + imageUrl: 'https://f4.bcbits.com/img/23754516_28.jpg' + } + ], + total: 68, + continuationToken: { fanId: 8563805, token: '1655975469:2121405720' } +} +Fetching more with continuation token... +{ + items: [ + { + name: 'Will Butler', + url: 'https://willbutler.bandcamp.com', + location: 'Brooklyn, New York', + imageUrl: 'https://f4.bcbits.com/img/24918652_21.jpg' + }, + { + name: 'Teenage Fanclub', + url: 'https://teenage-fanclub.bandcamp.com', + location: 'Glasgow, UK', + imageUrl: 'https://f4.bcbits.com/img/24101151_21.jpg' + }, + { + name: 'Cable Ties', + url: 'https://cableties.bandcamp.com', + location: 'Melbourne, Australia', + imageUrl: 'https://f4.bcbits.com/img/8448159_21.jpg' + }, + { + name: 'Reigning Sound', + url: 'https://reigningsound.bandcamp.com', + location: 'Asheville, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/23915882_21.jpg' + }, + { + name: 'Hiss Golden Messenger', + url: 'https://hissgoldenmessenger.bandcamp.com', + location: 'North Carolina', + imageUrl: 'https://f4.bcbits.com/img/24016536_21.jpg' + }, + { + name: 'Torres', + url: 'https://torrestorrestorres.bandcamp.com', + location: 'New York, New York', + imageUrl: 'https://f4.bcbits.com/img/24864649_21.jpg' + }, + { + name: 'DAWN', + url: 'https://dawnrichard.bandcamp.com', + location: 'Los Angeles, California', + imageUrl: 'https://f4.bcbits.com/img/23711418_21.jpg' + }, + { + name: 'Caribou', + url: 'https://caribouband.bandcamp.com', + location: 'London, UK', + imageUrl: 'https://f4.bcbits.com/img/18041279_21.jpg' + }, + { + name: 'A Giant Dog', + url: 'https://agiantdog.bandcamp.com', + location: 'Austin, Texas', + imageUrl: 'https://f4.bcbits.com/img/651146_21.jpg' + }, + { + name: 'Mac McCaughan', + url: 'https://macmccaughan.bandcamp.com', + location: 'Chapel Hill, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/4961611_21.jpg' + }, + { + name: 'The Magnetic Fields', + url: 'https://themagneticfields.bandcamp.com', + location: 'New York, New York', + imageUrl: 'https://f4.bcbits.com/img/14275749_21.jpg' + }, + { + name: 'Carson McHone', + url: 'https://carsonmchone.bandcamp.com', + location: 'Austin, Texas', + imageUrl: 'https://f4.bcbits.com/img/26448389_21.jpg' + }, + { + name: 'Destroyer', + url: 'https://destroyer.bandcamp.com', + location: 'Vancouver, British Columbia', + imageUrl: 'https://f4.bcbits.com/img/27319221_21.jpg' + }, + { + name: 'Ibibio Sound Machine', + url: 'https://ibibiosoundmachine.bandcamp.com', + location: 'UK', + imageUrl: 'https://f4.bcbits.com/img/27398052_21.jpg' + }, + { + name: 'Superchunk', + url: 'https://superchunk.bandcamp.com', + location: 'Chapel Hill, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/26971728_21.jpg' + }, + { + name: 'Hollie Cook', + url: 'https://holliecook.bandcamp.com', + location: 'London, UK', + imageUrl: 'https://f4.bcbits.com/img/28056444_21.jpg' + }, + { + name: 'Redd Kross', + url: 'https://reddkross.bandcamp.com', + location: 'Hawthorne, California', + imageUrl: 'https://f4.bcbits.com/img/28555577_21.jpg' + }, + { + name: 'Friendship', + url: 'https://friendshipphl.bandcamp.com', + location: 'Philadelphia, Pennsylvania', + imageUrl: 'https://f4.bcbits.com/img/28727388_21.jpg' + }, + { + name: 'Tall Dwarfs', + url: 'https://talldwarfs.bandcamp.com', + location: 'New Zealand', + imageUrl: 'https://f4.bcbits.com/img/28914236_21.jpg' + }, + { + name: 'The Mountain Goats', + url: 'https://themountaingoats.bandcamp.com', + location: 'Durham, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/28851781_21.jpg' + } + ], + continuationToken: { fanId: 8563805, token: '1655975366:3856108936' } +} diff --git a/examples/getFanFollowingGenres.js b/examples/getFanFollowingGenres.js new file mode 100644 index 0000000..a9c9a77 --- /dev/null +++ b/examples/getFanFollowingGenres.js @@ -0,0 +1,19 @@ +const bcfetch = require('../lib'); +const util = require('util'); + +const username = 'patrickkfkan'; + +const options = { + imageFormat: 'art_tags_large' +} + +bcfetch.getFanFollowingGenres(username, options).then( async (results) => { + console.log(util.inspect(results, false, null, false)); + + if (results.continuationToken) { + console.log('Fetching more with continuation token...'); + + const moreResults = await bcfetch.getFanFollowingGenres(results.continuationToken); + console.log(util.inspect(moreResults, false, null, false)); + } +}); diff --git a/examples/getFanFollowingGenres_output.txt b/examples/getFanFollowingGenres_output.txt new file mode 100644 index 0000000..dd00633 --- /dev/null +++ b/examples/getFanFollowingGenres_output.txt @@ -0,0 +1,492 @@ +{ + items: [ + { + type: 'tag', + name: 'alt-country', + value: 'alt-country', + url: 'http://bandcamp.com/tag/alt-country', + imageUrls: [ + 'https://f4.bcbits.com/img/a2812018975_9.jpg', + 'https://f4.bcbits.com/img/a4021760960_9.jpg', + 'https://f4.bcbits.com/img/a3180368360_9.jpg', + 'https://f4.bcbits.com/img/a4249304312_9.jpg' + ] + }, + { + type: 'tag', + name: 'alternative', + value: 'alternative', + url: 'http://bandcamp.com/tag/alternative', + imageUrls: [ + 'https://f4.bcbits.com/img/a170407559_9.jpg', + 'https://f4.bcbits.com/img/a3131938012_9.jpg', + 'https://f4.bcbits.com/img/a249396355_9.jpg', + 'https://f4.bcbits.com/img/a2449560999_9.jpg' + ] + }, + { + type: 'tag', + name: 'americana', + value: 'americana', + url: 'http://bandcamp.com/tag/americana', + imageUrls: [ + 'https://f4.bcbits.com/img/a464276499_9.jpg', + 'https://f4.bcbits.com/img/a2640171700_9.jpg', + 'https://f4.bcbits.com/img/a1096304773_9.jpg', + 'https://f4.bcbits.com/img/a243223324_9.jpg' + ] + }, + { + type: 'tag', + name: 'bluegrass', + value: 'bluegrass', + url: 'http://bandcamp.com/tag/bluegrass', + imageUrls: [ + 'https://f4.bcbits.com/img/a1809221678_9.jpg', + 'https://f4.bcbits.com/img/a1307988683_9.jpg', + 'https://f4.bcbits.com/img/a3331001123_9.jpg', + 'https://f4.bcbits.com/img/a2518135234_9.jpg' + ] + }, + { + type: 'tag', + name: 'blues', + value: 'blues', + url: 'http://bandcamp.com/tag/blues', + imageUrls: [ + 'https://f4.bcbits.com/img/a1569928325_9.jpg', + 'https://f4.bcbits.com/img/a464276499_9.jpg', + 'https://f4.bcbits.com/img/a1629143589_9.jpg', + 'https://f4.bcbits.com/img/a3729345272_9.jpg' + ] + }, + { + type: 'tag', + name: 'blues rock', + value: 'blues-rock', + url: 'http://bandcamp.com/tag/blues-rock', + imageUrls: [ + 'https://f4.bcbits.com/img/a1933779616_9.jpg', + 'https://f4.bcbits.com/img/a3261738963_9.jpg', + 'https://f4.bcbits.com/img/a3863213642_9.jpg', + 'https://f4.bcbits.com/img/a2567029617_9.jpg' + ] + }, + { + type: 'tag', + name: 'britpop', + value: 'britpop', + url: 'http://bandcamp.com/tag/britpop', + imageUrls: [ + 'https://f4.bcbits.com/img/a3480175917_9.jpg', + 'https://f4.bcbits.com/img/a784126120_9.jpg', + 'https://f4.bcbits.com/img/a2644607321_9.jpg', + 'https://f4.bcbits.com/img/a979340940_9.jpg' + ] + }, + { + type: 'tag', + name: 'comedy', + value: 'comedy', + url: 'http://bandcamp.com/tag/comedy', + imageUrls: [ + 'https://f4.bcbits.com/img/a2417971529_9.jpg', + 'https://f4.bcbits.com/img/a1433758941_9.jpg', + 'https://f4.bcbits.com/img/a450963178_9.jpg', + 'https://f4.bcbits.com/img/a1684468871_9.jpg' + ] + }, + { + type: 'tag', + name: 'country', + value: 'country', + url: 'http://bandcamp.com/tag/country', + imageUrls: [ + 'https://f4.bcbits.com/img/a2269776116_9.jpg', + 'https://f4.bcbits.com/img/a4073383153_9.jpg', + 'https://f4.bcbits.com/img/a1374205961_9.jpg', + 'https://f4.bcbits.com/img/a387546155_9.jpg' + ] + }, + { + type: 'tag', + name: 'country blues', + value: 'country-blues', + url: 'http://bandcamp.com/tag/country-blues', + imageUrls: [ + 'https://f4.bcbits.com/img/a2668431628_9.jpg', + 'https://f4.bcbits.com/img/a976206506_9.jpg', + 'https://f4.bcbits.com/img/a1402311003_9.jpg', + 'https://f4.bcbits.com/img/a1934752360_9.jpg' + ] + }, + { + type: 'tag', + name: 'country folk', + value: 'country-folk', + url: 'http://bandcamp.com/tag/country-folk', + imageUrls: [ + 'https://f4.bcbits.com/img/a2369233251_9.jpg', + 'https://f4.bcbits.com/img/a3432467807_9.jpg', + 'https://f4.bcbits.com/img/a633308688_9.jpg', + 'https://f4.bcbits.com/img/a4161975053_9.jpg' + ] + }, + { + type: 'tag', + name: 'country rock', + value: 'country-rock', + url: 'http://bandcamp.com/tag/country-rock', + imageUrls: [ + 'https://f4.bcbits.com/img/a3432467807_9.jpg', + 'https://f4.bcbits.com/img/a2621845460_9.jpg', + 'https://f4.bcbits.com/img/a1404033992_9.jpg', + 'https://f4.bcbits.com/img/a3388514058_9.jpg' + ] + }, + { + type: 'tag', + name: 'crust punk', + value: 'crust-punk', + url: 'http://bandcamp.com/tag/crust-punk', + imageUrls: [ + 'https://f4.bcbits.com/img/a3468490381_9.jpg', + 'https://f4.bcbits.com/img/a2468512644_9.jpg', + 'https://f4.bcbits.com/img/a2490597736_9.jpg', + 'https://f4.bcbits.com/img/a1933880040_9.jpg' + ] + }, + { + type: 'tag', + name: 'dream pop', + value: 'dream-pop', + url: 'http://bandcamp.com/tag/dream-pop', + imageUrls: [ + 'https://f4.bcbits.com/img/a2127751780_9.jpg', + 'https://f4.bcbits.com/img/a3307224930_9.jpg', + 'https://f4.bcbits.com/img/a2151856429_9.jpg', + 'https://f4.bcbits.com/img/a2340015657_9.jpg' + ] + }, + { + type: 'tag', + name: 'electric blues', + value: 'electric-blues', + url: 'http://bandcamp.com/tag/electric-blues', + imageUrls: [ + 'https://f4.bcbits.com/img/a2272366912_9.jpg', + 'https://f4.bcbits.com/img/a3261738963_9.jpg', + 'https://f4.bcbits.com/img/a3762950636_9.jpg', + 'https://f4.bcbits.com/img/a557154008_9.jpg' + ] + }, + { + type: 'tag', + name: 'electronic', + value: 'electronic', + url: 'http://bandcamp.com/tag/electronic', + imageUrls: [ + 'https://f4.bcbits.com/img/a2959214773_9.jpg', + 'https://f4.bcbits.com/img/a457848067_9.jpg', + 'https://f4.bcbits.com/img/a693931791_9.jpg', + 'https://f4.bcbits.com/img/a1806928460_9.jpg' + ] + }, + { + type: 'tag', + name: 'emo', + value: 'emo', + url: 'http://bandcamp.com/tag/emo', + imageUrls: [ + 'https://f4.bcbits.com/img/a465346081_9.jpg', + 'https://f4.bcbits.com/img/a3727718257_9.jpg', + 'https://f4.bcbits.com/img/a2673308805_9.jpg', + 'https://f4.bcbits.com/img/a786227558_9.jpg' + ] + }, + { + type: 'tag', + name: 'film music', + value: 'film-music', + url: 'http://bandcamp.com/tag/film-music', + imageUrls: [ + 'https://f4.bcbits.com/img/a1742290515_9.jpg', + 'https://f4.bcbits.com/img/a50784949_9.jpg', + 'https://f4.bcbits.com/img/a1075802195_9.jpg', + 'https://f4.bcbits.com/img/a244758941_9.jpg' + ] + }, + { + type: 'tag', + name: 'flamenco', + value: 'flamenco', + url: 'http://bandcamp.com/tag/flamenco', + imageUrls: [ + 'https://f4.bcbits.com/img/a885121826_9.jpg', + 'https://f4.bcbits.com/img/a3664761073_9.jpg', + 'https://f4.bcbits.com/img/a174589227_9.jpg', + 'https://f4.bcbits.com/img/a757919155_9.jpg' + ] + }, + { + type: 'tag', + name: 'folk punk', + value: 'folk-punk', + url: 'http://bandcamp.com/tag/folk-punk', + imageUrls: [ + 'https://f4.bcbits.com/img/a1705673353_9.jpg', + 'https://f4.bcbits.com/img/a1760380718_9.jpg', + 'https://f4.bcbits.com/img/a3775824951_9.jpg', + 'https://f4.bcbits.com/img/a18440001_9.jpg' + ] + } + ], + total: 45, + continuationToken: { fanId: 8563805, token: 'folk-punk' } +} +Fetching more with continuation token... +{ + items: [ + { + type: 'tag', + name: 'grunge', + value: 'grunge', + url: 'http://bandcamp.com/tag/grunge', + imageUrls: [ + 'https://f4.bcbits.com/img/a3259891097_3.jpg', + 'https://f4.bcbits.com/img/a3310898310_3.jpg', + 'https://f4.bcbits.com/img/a964923157_3.jpg', + 'https://f4.bcbits.com/img/a951544653_3.jpg' + ] + }, + { + type: 'tag', + name: 'hardcore punk', + value: 'hardcore-punk', + url: 'http://bandcamp.com/tag/hardcore-punk', + imageUrls: [ + 'https://f4.bcbits.com/img/a360503158_3.jpg', + 'https://f4.bcbits.com/img/a2239018039_3.jpg', + 'https://f4.bcbits.com/img/a2901862961_3.jpg', + 'https://f4.bcbits.com/img/a1281987747_3.jpg' + ] + }, + { + type: 'tag', + name: 'hillbilly', + value: 'hillbilly', + url: 'http://bandcamp.com/tag/hillbilly', + imageUrls: [ + 'https://f4.bcbits.com/img/a990859061_3.jpg', + 'https://f4.bcbits.com/img/a1053103475_3.jpg', + 'https://f4.bcbits.com/img/a2758289864_3.jpg', + 'https://f4.bcbits.com/img/a1616427371_3.jpg' + ] + }, + { + type: 'tag', + name: 'hip-hop/rap', + value: 'hip-hop-rap', + url: 'http://bandcamp.com/tag/hip-hop-rap', + imageUrls: [ + 'https://f4.bcbits.com/img/a1431155242_3.jpg', + 'https://f4.bcbits.com/img/a2461990826_3.jpg', + 'https://f4.bcbits.com/img/a2738953678_3.jpg', + 'https://f4.bcbits.com/img/a4279135532_3.jpg' + ] + }, + { + type: 'tag', + name: 'honky tonk', + value: 'honky-tonk', + url: 'http://bandcamp.com/tag/honky-tonk', + imageUrls: [ + 'https://f4.bcbits.com/img/a359008077_3.jpg', + 'https://f4.bcbits.com/img/a1169969255_3.jpg', + 'https://f4.bcbits.com/img/a1603431406_3.jpg', + 'https://f4.bcbits.com/img/a3299835395_3.jpg' + ] + }, + { + type: 'tag', + name: 'jangle pop', + value: 'jangle-pop', + url: 'http://bandcamp.com/tag/jangle-pop', + imageUrls: [ + 'https://f4.bcbits.com/img/a4007205205_3.jpg', + 'https://f4.bcbits.com/img/a3813877843_3.jpg', + 'https://f4.bcbits.com/img/a3282444573_3.jpg', + 'https://f4.bcbits.com/img/a4250417365_3.jpg' + ] + }, + { + type: 'tag', + name: 'jazz', + value: 'jazz', + url: 'http://bandcamp.com/tag/jazz', + imageUrls: [ + 'https://f4.bcbits.com/img/a3082595015_3.jpg', + 'https://f4.bcbits.com/img/a3992958598_3.jpg', + 'https://f4.bcbits.com/img/a3843612070_3.jpg', + 'https://f4.bcbits.com/img/a1485592402_3.jpg' + ] + }, + { + type: 'tag', + name: 'latin', + value: 'latin', + url: 'http://bandcamp.com/tag/latin', + imageUrls: [ + 'https://f4.bcbits.com/img/a2558489987_3.jpg', + 'https://f4.bcbits.com/img/a602153856_3.jpg', + 'https://f4.bcbits.com/img/a1505469867_3.jpg', + 'https://f4.bcbits.com/img/a3210602209_3.jpg' + ] + }, + { + type: 'tag', + name: 'math rock', + value: 'math-rock', + url: 'http://bandcamp.com/tag/math-rock', + imageUrls: [ + 'https://f4.bcbits.com/img/a2239335029_3.jpg', + 'https://f4.bcbits.com/img/a361777070_3.jpg', + 'https://f4.bcbits.com/img/a3671783114_3.jpg', + 'https://f4.bcbits.com/img/a1802582472_3.jpg' + ] + }, + { + type: 'tag', + name: 'no wave', + value: 'no-wave', + url: 'http://bandcamp.com/tag/no-wave', + imageUrls: [ + 'https://f4.bcbits.com/img/a1629143589_3.jpg', + 'https://f4.bcbits.com/img/a4286540031_3.jpg', + 'https://f4.bcbits.com/img/a3992958598_3.jpg', + 'https://f4.bcbits.com/img/a2964482710_3.jpg' + ] + }, + { + type: 'tag', + name: 'outlaw', + value: 'outlaw', + url: 'http://bandcamp.com/tag/outlaw', + imageUrls: [ + 'https://f4.bcbits.com/img/a1365344659_3.jpg', + 'https://f4.bcbits.com/img/a1738994864_3.jpg', + 'https://f4.bcbits.com/img/a443791610_3.jpg', + 'https://f4.bcbits.com/img/a3305307372_3.jpg' + ] + }, + { + type: 'tag', + name: 'pop', + value: 'pop', + url: 'http://bandcamp.com/tag/pop', + imageUrls: [ + 'https://f4.bcbits.com/img/a2658112646_3.jpg', + 'https://f4.bcbits.com/img/a2489732443_3.jpg', + 'https://f4.bcbits.com/img/a3211077482_3.jpg', + 'https://f4.bcbits.com/img/a461932149_3.jpg' + ] + }, + { + type: 'tag', + name: 'pop punk', + value: 'pop-punk', + url: 'http://bandcamp.com/tag/pop-punk', + imageUrls: [ + 'https://f4.bcbits.com/img/a113286790_3.jpg', + 'https://f4.bcbits.com/img/a1144344500_3.jpg', + 'https://f4.bcbits.com/img/a3487200283_3.jpg', + 'https://f4.bcbits.com/img/a3823255125_3.jpg' + ] + }, + { + type: 'tag', + name: 'punk', + value: 'punk', + url: 'http://bandcamp.com/tag/punk', + imageUrls: [ + 'https://f4.bcbits.com/img/a1760380718_3.jpg', + 'https://f4.bcbits.com/img/a2926724195_3.jpg', + 'https://f4.bcbits.com/img/a895482160_3.jpg', + 'https://f4.bcbits.com/img/a2402040528_3.jpg' + ] + }, + { + type: 'tag', + name: 'punk rock', + value: 'punk-rock', + url: 'http://bandcamp.com/tag/punk-rock', + imageUrls: [ + 'https://f4.bcbits.com/img/a1705673353_3.jpg', + 'https://f4.bcbits.com/img/a2436204662_3.jpg', + 'https://f4.bcbits.com/img/a3764892689_3.jpg', + 'https://f4.bcbits.com/img/a2901862961_3.jpg' + ] + }, + { + type: 'tag', + name: 'reggaeton', + value: 'reggaeton', + url: 'http://bandcamp.com/tag/reggaeton', + imageUrls: [ + 'https://f4.bcbits.com/img/a1257468602_3.jpg', + 'https://f4.bcbits.com/img/a736673855_3.jpg', + 'https://f4.bcbits.com/img/a3975370626_3.jpg', + 'https://f4.bcbits.com/img/a1656797036_3.jpg' + ] + }, + { + type: 'tag', + name: 'roots', + value: 'roots', + url: 'http://bandcamp.com/tag/roots', + imageUrls: [ + 'https://f4.bcbits.com/img/a580053174_3.jpg', + 'https://f4.bcbits.com/img/a2384940752_3.jpg', + 'https://f4.bcbits.com/img/a2576819959_3.jpg', + 'https://f4.bcbits.com/img/a1998317371_3.jpg' + ] + }, + { + type: 'tag', + name: 'salsa', + value: 'salsa', + url: 'http://bandcamp.com/tag/salsa', + imageUrls: [ + 'https://f4.bcbits.com/img/a3844333998_3.jpg', + 'https://f4.bcbits.com/img/a3210602209_3.jpg', + 'https://f4.bcbits.com/img/a3139930816_3.jpg', + 'https://f4.bcbits.com/img/a362432432_3.jpg' + ] + }, + { + type: 'tag', + name: 'singer-songwriter', + value: 'singer-songwriter', + url: 'http://bandcamp.com/tag/singer-songwriter', + imageUrls: [ + 'https://f4.bcbits.com/img/a3813877843_3.jpg', + 'https://f4.bcbits.com/img/a2298793356_3.jpg', + 'https://f4.bcbits.com/img/a2642176051_3.jpg', + 'https://f4.bcbits.com/img/a1051407307_3.jpg' + ] + }, + { + type: 'tag', + name: 'soundtrack', + value: 'soundtrack', + url: 'http://bandcamp.com/tag/soundtrack', + imageUrls: [ + 'https://f4.bcbits.com/img/a3439861516_3.jpg', + 'https://f4.bcbits.com/img/a1469772142_3.jpg', + 'https://f4.bcbits.com/img/a434097210_3.jpg', + 'https://f4.bcbits.com/img/a2854475053_3.jpg' + ] + } + ], + continuationToken: { fanId: 8563805, token: 'soundtrack' } +} diff --git a/examples/getFanInfo.js b/examples/getFanInfo.js new file mode 100644 index 0000000..5d59a91 --- /dev/null +++ b/examples/getFanInfo.js @@ -0,0 +1,12 @@ +const bcfetch = require('../lib'); +const util = require('util'); + +const username = 'patrickkfkan'; + +const options = { + imageFormat: 'bio_screen' +} + +bcfetch.getFanInfo(username, options).then( results => { + console.log(util.inspect(results, false, null, false)); +}); diff --git a/examples/getFanInfo_output.txt b/examples/getFanInfo_output.txt new file mode 100644 index 0000000..5f19191 --- /dev/null +++ b/examples/getFanInfo_output.txt @@ -0,0 +1,13 @@ +{ + type: 'fan', + name: 'patrickkfkan', + username: 'patrickkfkan', + url: 'https://bandcamp.com/patrickkfkan', + description: 'Hello there!', + location: 'Hong Kong', + websiteUrl: 'http://www.google.com', + imageUrl: 'https://f4.bcbits.com/img/29024471_20.jpg', + followingGenresCount: 45, + followingArtistsAndLabelsCount: 68, + wishlistItemCount: 24 +} diff --git a/examples/getFanWishlist.js b/examples/getFanWishlist.js new file mode 100644 index 0000000..8beca44 --- /dev/null +++ b/examples/getFanWishlist.js @@ -0,0 +1,19 @@ +const bcfetch = require('../lib'); +const util = require('util'); + +const username = 'patrickkfkan'; + +const options = { + imageFormat: 'art_app_large' +} + +bcfetch.getFanWishlist(username, options).then( async (results) => { + console.log(util.inspect(results, false, null, false)); + + if (results.continuationToken) { + console.log('Fetching more with continuation token...'); + + const moreResults = await bcfetch.getFanWishlist(results.continuationToken); + console.log(util.inspect(moreResults, false, null, false)); + } +}); diff --git a/examples/getFanWishlist_output.txt b/examples/getFanWishlist_output.txt new file mode 100644 index 0000000..a3f21fa --- /dev/null +++ b/examples/getFanWishlist_output.txt @@ -0,0 +1,381 @@ +{ + items: [ + { + type: 'album', + name: 'Silver Sash', + url: 'https://glitterhouserecords.bandcamp.com/album/silver-sash', + imageUrl: 'https://f4.bcbits.com/img/a1068361756_16.jpg', + featuredTrack: { + position: 3, + name: 'Duat Hawk', + artist: 'Wovenhand', + duration: 205.24, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1665518602&ts=1655983089&t=57118d83b2b2dd9686c99239b1c82922e59ff80b' + }, + artist: { + name: 'Wovenhand', + url: 'https://glitterhouserecords.bandcamp.com' + } + }, + { + type: 'album', + name: 'Sylvie (2022)', + url: 'https://sylvie-music.bandcamp.com/album/sylvie-2022-2', + imageUrl: 'https://f4.bcbits.com/img/a2220580996_16.jpg', + featuredTrack: { + position: 1, + name: 'Falls On Me', + artist: 'Sylvie', + duration: 299.453, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1840839102&ts=1655983089&t=8b4ca0f5fe87a9b4b4ed2bed51dd01e9c5b10486' + }, + artist: { name: 'Sylvie', url: 'https://sylvie-music.bandcamp.com' } + }, + { + type: 'album', + name: 'Songs From My Bucket', + url: 'https://margocilker.bandcamp.com/album/songs-from-my-bucket', + imageUrl: 'https://f4.bcbits.com/img/a3875199563_16.jpg', + featuredTrack: { + position: 3, + name: 'Skateboard Song', + artist: 'Margo Cilker', + duration: 239.799, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=2599779818&ts=1655983089&t=90eabed1822b2426cc36e5ffc91860279fa53fec' + }, + artist: { name: 'Margo Cilker', url: 'https://margocilker.bandcamp.com' } + }, + { + type: 'album', + name: 'Chicamacomico', + url: 'https://americanaquarium.bandcamp.com/album/chicamacomico', + imageUrl: 'https://f4.bcbits.com/img/a3942062080_16.jpg', + featuredTrack: { + position: 4, + name: 'The First Year', + artist: 'American Aquarium', + duration: 192.013, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1867095022&ts=1655983089&t=73d76a25d7e2bafe05a33ed55838cd8fc76a791e' + }, + artist: { + name: 'American Aquarium', + url: 'https://americanaquarium.bandcamp.com' + } + }, + { + type: 'album', + name: 'Heavy Denim', + url: 'https://nickdittmeier.bandcamp.com/album/heavy-denim', + imageUrl: 'https://f4.bcbits.com/img/a392062975_16.jpg', + featuredTrack: { + position: 1, + name: 'I Suppose', + artist: 'Nick Dittmeier & the Sawdusters', + duration: 173.977, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=291756392&ts=1655983089&t=dac9452150ffb9bc156fe9c146c2109844ecc889' + }, + artist: { + name: 'Nick Dittmeier & the Sawdusters', + url: 'https://nickdittmeier.bandcamp.com' + } + }, + { + type: 'album', + name: 'Highway Sounds', + url: 'https://kassivalazza.bandcamp.com/album/highway-sounds', + imageUrl: 'https://f4.bcbits.com/img/a3803986383_16.jpg', + featuredTrack: { + position: 1, + name: 'Little Flowers', + artist: 'Kassi Valazza', + duration: 306.92, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3662293078&ts=1655983089&t=5bf4a3c8c68149f8a86049a52d141f195b051dfc' + }, + artist: { + name: 'Kassi Valazza', + url: 'https://kassivalazza.bandcamp.com' + } + }, + { + type: 'album', + name: 'LIVE with LOOPER No.1', + url: 'https://markus-k.bandcamp.com/album/live-with-looper-no-1', + imageUrl: 'https://f4.bcbits.com/img/a3261738963_16.jpg', + featuredTrack: { + position: 1, + name: 'We Merge', + artist: 'Markus K', + duration: 228.87, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3271952930&ts=1655983089&t=d04ddb62fd86b5e2a6884ccb39107f4effd0e5f6' + }, + artist: { name: 'Markus K', url: 'https://markus-k.bandcamp.com' } + }, + { + type: 'album', + name: 'Visions of the Country', + url: 'https://gnomelife.bandcamp.com/album/visions-of-the-country', + imageUrl: 'https://f4.bcbits.com/img/a1359892761_16.jpg', + featuredTrack: { + position: 5, + name: 'Blue Crystal Fire', + artist: 'Robbie Basho', + duration: 287.546, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=2254598059&ts=1655983089&t=f94b64f12878f766d9a14e8a964002a43b46a123' + }, + artist: { name: 'Robbie Basho', url: 'https://gnomelife.bandcamp.com' } + }, + { + type: 'album', + name: 'Pohorylle', + url: 'https://margocilker.bandcamp.com/album/pohorylle', + imageUrl: 'https://f4.bcbits.com/img/a880076195_16.jpg', + featuredTrack: { + position: 1, + name: 'That River', + artist: 'Margo Cilker', + duration: 189.187, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=895429532&ts=1655983089&t=951036b766857e03317d8e720c81df576b79d27a' + }, + artist: { name: 'Margo Cilker', url: 'https://margocilker.bandcamp.com' } + }, + { + type: 'album', + name: 'Cruel Country', + url: 'https://wilcohq.bandcamp.com/album/cruel-country', + imageUrl: 'https://f4.bcbits.com/img/a452127673_16.jpg', + featuredTrack: { + position: 14, + name: 'Falling Apart (Right Now)', + artist: 'Wilco', + duration: 198.52, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3385090483&ts=1655983089&t=cbd6fbd2900a87a85c73d7d9f316d7826711f917' + }, + artist: { name: 'Wilco', url: 'https://wilcohq.bandcamp.com' } + }, + { + type: 'album', + name: 'Live at Red Rocks - Morrison, CO - 8/​1​/​2021', + url: 'https://jasonisbell.bandcamp.com/album/live-at-red-rocks-morrison-co-8-1-2021', + imageUrl: 'https://f4.bcbits.com/img/a863538884_16.jpg', + featuredTrack: { + position: 21, + name: 'Tour Of Duty', + artist: 'Jason Isbell and the 400 Unit', + duration: 228.867, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=2971595025&ts=1655983089&t=59596727b495c213825254c1437ea123892dc09f' + }, + artist: { + name: 'Jason Isbell and the 400 Unit', + url: 'https://jasonisbell.bandcamp.com' + } + }, + { + type: 'album', + name: 'Wild Creatures', + url: 'https://nekocaseofficial.bandcamp.com/album/wild-creatures', + imageUrl: 'https://f4.bcbits.com/img/a3745965981_16.jpg', + featuredTrack: { + position: 1, + name: "I'm An Animal", + artist: 'Neko Case', + duration: 136.987, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=535802350&ts=1655983089&t=b52861815f82e98e6674696b6d0d4575b728f44c' + }, + artist: { + name: 'Neko Case', + url: 'https://nekocaseofficial.bandcamp.com' + } + }, + { + type: 'album', + name: "Cover Charge: NC Artists Go Under Cover to Benefit Cat's Cradle", + url: 'https://covercharge.bandcamp.com/album/cover-charge-nc-artists-go-under-cover-to-benefit-cats-cradle', + imageUrl: 'https://f4.bcbits.com/img/a2768267036_16.jpg', + featuredTrack: { + position: 1, + name: "Can't Stop the World", + artist: 'Cover Charge', + duration: 205.213, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=206849315&ts=1655983089&t=0628dd04ff2f75ced8233be6ad48571225c915e3' + }, + artist: { name: 'Cover Charge', url: 'https://covercharge.bandcamp.com' } + }, + { + type: 'album', + name: 'Anything But Country', + url: 'https://legendsofcountry.bandcamp.com/album/anything-but-country', + imageUrl: 'https://f4.bcbits.com/img/a1262555298_16.jpg', + featuredTrack: { + position: 4, + name: "Everything's Going South", + artist: 'Legends Of Country', + duration: 180.156, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=2932389502&ts=1655983089&t=cbb49d079d9202ff991fc30e1785377e22109a94' + }, + artist: { + name: 'Legends Of Country', + url: 'https://legendsofcountry.bandcamp.com' + } + }, + { + type: 'album', + name: 'Outsiders', + url: 'https://annativel.bandcamp.com/album/outsiders', + imageUrl: 'https://f4.bcbits.com/img/a420888783_16.jpg', + featuredTrack: { + position: 1, + name: 'Outsiders', + artist: 'Anna Tivel', + duration: 234.747, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=157662870&ts=1655983089&t=a2b53536e3f8c926f5228319fa9ff66b83cffea0' + }, + artist: { name: 'Anna Tivel', url: 'https://annativel.bandcamp.com' } + }, + { + type: 'album', + name: 'Conversations with My Other Voice', + url: 'https://buickaudra.bandcamp.com/album/conversations-with-my-other-voice', + imageUrl: 'https://f4.bcbits.com/img/a4196155980_16.jpg', + featuredTrack: { + position: 3, + name: 'Afraid of Flying', + artist: 'Buick Audra', + duration: 216.757, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=425192694&ts=1655983089&t=f60ec11c0658b91e46b866872a4efe2b04fbd0bb' + }, + artist: { name: 'Buick Audra', url: 'https://buickaudra.bandcamp.com' } + }, + { + type: 'album', + name: 'The New Faith', + url: 'https://jakeblountmusic.bandcamp.com/album/the-new-faith', + imageUrl: 'https://f4.bcbits.com/img/a1096304773_16.jpg', + featuredTrack: { + position: 3, + name: 'Didn’t It Rain', + artist: 'Jake Blount', + duration: 185.987, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=4237361807&ts=1655983089&t=cdaf63e67d215ccbe8558c267a2fa05f7c564522' + }, + artist: { + name: 'Jake Blount', + url: 'https://jakeblountmusic.bandcamp.com' + } + }, + { + type: 'album', + name: "Crow's Pine", + url: 'https://lobbyartrecs.bandcamp.com/album/crows-pine-2', + imageUrl: 'https://f4.bcbits.com/img/a2270982070_16.jpg', + featuredTrack: { + position: 5, + name: 'Ivory Tower', + artist: 'Bob Driftwood', + duration: 322.479, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=4191030128&ts=1655983089&t=ba54bc1473e2631c332b031bd44671d4d52dc08f' + }, + artist: { + name: 'Bob Driftwood', + url: 'https://lobbyartrecs.bandcamp.com' + } + }, + { + type: 'album', + name: 'Family', + url: 'https://thesilosofficial.bandcamp.com/album/family', + imageUrl: 'https://f4.bcbits.com/img/a877614212_16.jpg', + featuredTrack: { + position: 1, + name: 'My Favorite Animal', + artist: 'The Silos', + duration: 300.595, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=918323024&ts=1655983089&t=69296988b86c76ced9a3fe8dc3b35f7e386f90d1' + }, + artist: { + name: 'The Silos', + url: 'https://thesilosofficial.bandcamp.com' + } + }, + { + type: 'album', + name: 'Raging in the Dark', + url: 'https://jrcarroll.bandcamp.com/album/raging-in-the-dark', + imageUrl: 'https://f4.bcbits.com/img/a1293600_16.jpg', + featuredTrack: { + position: 4, + name: 'Red Fern', + artist: 'J.R. Carroll', + duration: 225.533, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=85757439&ts=1655983089&t=63cd45959e796be77027a6998e3d8e2754f09b20' + }, + artist: { name: 'J.R. Carroll', url: 'https://jrcarroll.bandcamp.com' } + } + ], + total: 24, + continuationToken: { fanId: 8563805, token: '1655929842:735158920:a::' } +} +Fetching more with continuation token... +{ + items: [ + { + type: 'album', + name: 'Live from Boston', + url: 'https://davehause.bandcamp.com/album/live-from-boston', + imageUrl: 'https://f4.bcbits.com/img/a2436594456_9.jpg', + featuredTrack: { + position: 4, + name: 'Saboteurs (Live from Boston 4.08.2022)', + artist: 'Dave Hause', + duration: 294.458, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3843751680&ts=1655983090&t=68a6dfeb68dc6aa77968464352cecf6c755aaa81' + }, + artist: { name: 'Dave Hause', url: 'https://davehause.bandcamp.com' } + }, + { + type: 'track', + name: "T'es belle", + url: 'https://musique.coeurdepirate.com/track/tes-belle', + imageUrl: 'https://f4.bcbits.com/img/a774650359_9.jpg', + featuredTrack: { + position: null, + name: "T'es belle", + artist: 'Cœur de pirate', + duration: 176.373, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3387079907&ts=1655983090&t=2102a4d867d367db18b79f1b24b30abcc0ab4074' + }, + artist: { + name: 'Cœur de pirate', + url: 'https://coeurdepirate.bandcamp.com' + } + }, + { + type: 'album', + name: '痛みの永遠', + url: 'https://macroblank.bandcamp.com/album/--21', + imageUrl: 'https://f4.bcbits.com/img/a1957926096_9.jpg', + featuredTrack: { + position: 1, + name: 'あなたを許すのは難しい', + artist: 'Macroblank', + duration: 358.142, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=4273244916&ts=1655983090&t=fd50e91ebbd038b88e7adc98bab963470f95c859' + }, + artist: { name: 'Macroblank', url: 'https://macroblank.bandcamp.com' } + }, + { + type: 'album', + name: 'False Light', + url: 'https://whiteward.bandcamp.com/album/false-light', + imageUrl: 'https://f4.bcbits.com/img/a2447867317_9.jpg', + featuredTrack: { + position: 1, + name: 'Leviathan', + artist: 'White Ward', + duration: 797.115, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=2599650567&ts=1655983090&t=90ee7e742cb369de99d948af4594ac0585a010e7' + }, + artist: { name: 'White Ward', url: 'https://whiteward.bandcamp.com' } + } + ], + continuationToken: null +} diff --git a/lib/index.js b/lib/index.js index aeea3c8..d2b9088 100644 --- a/lib/index.js +++ b/lib/index.js @@ -432,6 +432,85 @@ async function searchLocation(params) { .then( json => parser.parseSearchLocationResults(json)); } +async function getFanInfo(username, options = {}) { + const imageConstants = await _getImageConstants(); + const fanPageUrl = utils.getFanPageUrl(username); + const opts = { + imageBaseUrl: imageConstants.baseUrl, + imageFormat: await _parseImageFormatArg(options.imageFormat, 20), + }; + return _fetchPage(fanPageUrl) + .then( html => parser.parseFanInfo(html, opts) ); +} + +async function _commonGetFanItems(usernameOrContinuationToken, options, getOpts) { + const {defaultImageFormat, continuationUrl, parsePageFn, parseContinuationFn} = getOpts; + const imageConstants = await _getImageConstants(); + const opts = { + imageBaseUrl: imageConstants.baseUrl, + imageFormat: await _parseImageFormatArg(options.imageFormat, defaultImageFormat), + }; + + if (typeof usernameOrContinuationToken === 'string') { + const fanPageUrl = utils.getFanPageUrl(usernameOrContinuationToken); + + return _fetchPage(fanPageUrl) + .then( html => parsePageFn(html, opts) ); + } + else if (typeof usernameOrContinuationToken === 'object' && usernameOrContinuationToken.fanId && usernameOrContinuationToken.token) { + const postData = { + fan_id: usernameOrContinuationToken.fanId, + older_than_token: usernameOrContinuationToken.token, + count: 20 + }; + + return _fetchPage(continuationUrl, true, _getPostFetchOptions(postData)) + .then( json => parseContinuationFn(json, usernameOrContinuationToken, opts)); + } + else { + throw new Error('Invalid argument'); + } +} + +async function getFanWishlist(usernameOrContinuationToken, options = {}) { + try { + return await _commonGetFanItems(usernameOrContinuationToken, options, { + defaultImageFormat: 9, + continuationUrl: utils.getFanWishlistContinuationUrl(), + parsePageFn: parser.parseFanWishlistFromPage, + parseContinuationFn: parser.parseFanWishlistFromContinuation + }); + } catch (error) { + throw new Error('getFanWishlist(): ' + error.message); + } +} + +async function getFanFollowingArtistsAndLabels(usernameOrContinuationToken, options = {}) { + try { + return await _commonGetFanItems(usernameOrContinuationToken, options, { + defaultImageFormat: 21, + continuationUrl: utils.getFanFollowingBandsContinuationUrl(), + parsePageFn: parser.parseFanFollowingBandsFromPage, + parseContinuationFn: parser.parseFanFollowingBandsFromContinuation + }); + } catch (error) { + throw new Error('getFanFollowingArtistsAndLabels(): ' + error.message); + } +} + +async function getFanFollowingGenres(usernameOrContinuationToken, options = {}) { + try { + return await _commonGetFanItems(usernameOrContinuationToken, options, { + defaultImageFormat: 3, + continuationUrl: utils.getFanFollowingGenresContinuationUrl(), + parsePageFn: parser.parseFanFollowingGenresFromPage, + parseContinuationFn: parser.parseFanFollowingGenresFromContinuation + }); + } catch (error) { + throw new Error('getFanFollowingGenres(): ' + error.message); + } +} + async function _fetchPage(url, json = false, fetchOptions = null) { return _cache.getOrSet('page', url + (json ? ':json' : ':html') + (fetchOptions ? ':' + JSON.stringify(fetchOptions) : ''), () => { const doFetch = fetchOptions ? fetch(url, fetchOptions) : fetch(url); @@ -489,7 +568,11 @@ const _exportFn = { getReleasesByTagFilterOptions, getReleasesByTag, searchTag, - searchLocation + searchLocation, + getFanInfo, + getFanWishlist, + getFanFollowingArtistsAndLabels, + getFanFollowingGenres }; // Bottleneck limiter diff --git a/lib/parser.js b/lib/parser.js index df51e47..e00a0fc 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -1254,6 +1254,250 @@ function parseSearchLocationResults(json) { } } +function parseFanInfo(html, opts) { + const $ = cheerio.load(html); + const blob = decode($('#pagedata[data-blob]').attr('data-blob')); + const parsed = JSON.parse(blob); + + const fanData = parsed.fan_data || {}; + const fanId = fanData.fan_id; + if (!fanId) { + return null; + } + + const result = { + type: 'fan', + name: fanData.name || null, + username: fanData.username || null, + url: fanData.trackpipe_url, + description: fanData.bio || null, + location: fanData.location || null, + websiteUrl: fanData.website_url || null, + imageUrl: '', + followingGenresCount: fanData.following_genres_count || 0, + followingArtistsAndLabelsCount: fanData.following_bands_count || 0, + wishlistItemCount: 0 + }; + + if (parsed.wishlist_data) { + result.wishlistItemCount = parsed.wishlist_data.item_count || 0; + } + + if (fanData.photo && fanData.photo.image_id) { + result.imageUrl = opts.imageBaseUrl + '/img/' + fanData.photo.image_id + '_' + opts.imageFormat.id + '.jpg'; + } + + return result; +} + +function _commonParseFanPageItems(html, opts, parseOpts) { + + /** + * parseOpts: { + * itemType: 'wishlist' / 'following_genres' / 'following_bands' + * parseFn: + * } + */ + + const _getSequenceOrPending = (o) => { + return Array.isArray(o.sequence) && o.sequence.length > 0 ? o.sequence : + Array.isArray(o.pending_sequence) && o.pending_sequence.length > 0 ? o.pending_sequence : []; + }; + + const $ = cheerio.load(html); + const blob = decode($('#pagedata[data-blob]').attr('data-blob')); + const parsed = JSON.parse(blob); + const result = { + items: [], + total: 0, + continuationToken: null + }; + + const itemListData = parsed[`${parseOpts.itemType}_data`]; + const itemCache = parsed.item_cache ? parsed.item_cache[parseOpts.itemType] : null; + if (itemListData && itemCache) { + const tracklists = parsed.tracklists ? parsed.tracklists[parseOpts.itemType] : null; + const sequence = _getSequenceOrPending(itemListData); + const parseFn = parseOpts.parseFn; + + sequence.forEach(itemKey => { + const parsedItem = parseFn(itemCache[itemKey], opts, tracklists); + if (parsedItem) { + result.items.push(parsedItem); + } + }); + result.total = itemListData.item_count; + + const fanId = parsed.fan_data && parsed.fan_data.fan_id ? parsed.fan_data.fan_id : null; + if (itemListData.item_count > sequence.length && itemListData.last_token && fanId) { + result.continuationToken = { + fanId, + token: itemListData.last_token + }; + } + } + + return result; +} + +function _commonParseFanContinuationItems(json, continuationToken, opts, parseOpts) { + + /** + * parseOpts: { + * listKey: 'items' / 'followeers' + * parseFn: + * } + */ + + const items = json[parseOpts.listKey] || []; + const tracklists = json.tracklists || null; + const parseFn = parseOpts.parseFn; + const result = { + items: [], + continuationToken: null + }; + items.forEach( data => { + const parsedItem = parseFn(data, opts, tracklists); + if (parsedItem) { + result.items.push(parsedItem); + } + }); + if (json.more_available && json.last_token) { + result.continuationToken = { + fanId: continuationToken.fanId, + token: json.last_token + }; + } + return result; +} + +function parseFanWishlistFromPage(html, opts) { + return _commonParseFanPageItems(html, opts, { + itemType: 'wishlist', + parseFn: _parseFanWishlistItem + }); +} + +function parseFanWishlistFromContinuation(json, continuationToken, opts) { + return _commonParseFanContinuationItems(json, continuationToken, opts, { + listKey: 'items', + parseFn: _parseFanWishlistItem + }); +} + +function _parseFanWishlistItem(data, opts, tracklists) { + if (!data) { + return null; + } + + const mediaItem = { + type: 'unknown', + name: data.item_title, + url: data.item_url, + imageUrl: '', + featuredTrack: null, + artist: { + name: data.band_name + } + }; + + if (data.tralbum_type === 'a') { + mediaItem.type = 'album'; + } + else if (data.tralbum_type === 't') { + mediaItem.type = 'track'; + } + if (data.item_art_id) { + mediaItem.imageUrl = opts.imageBaseUrl + '/img/a' + data.item_art_id + '_' + opts.imageFormat.id + '.jpg'; + } + if (data.url_hints && data.url_hints.subdomain) { + mediaItem.artist.url = 'https://' + data.url_hints.subdomain + '.bandcamp.com'; + } + const itemKey = (data.tralbum_type && data.item_id) ? `${data.tralbum_type}${data.item_id}` : null; + if (itemKey && tracklists && Array.isArray(tracklists[itemKey]) && tracklists[itemKey].length > 0) { + const featuredTrackData = tracklists[itemKey][0]; + mediaItem.featuredTrack = { + position: featuredTrackData.track_number, + name: featuredTrackData.title, + artist: featuredTrackData.artist, + duration: featuredTrackData.duration, + streamUrl: featuredTrackData.file ? featuredTrackData.file['mp3-128'] : null + }; + } + return mediaItem; +} + +function parseFanFollowingBandsFromPage(html, opts) { + return _commonParseFanPageItems(html, opts, { + itemType: 'following_bands', + parseFn: _parseFanFollowingBand + }); +} + +function parseFanFollowingBandsFromContinuation(json, continuationToken, opts) { + return _commonParseFanContinuationItems(json, continuationToken, opts, { + listKey: 'followeers', + parseFn: _parseFanFollowingBand + }); +} + +function _parseFanFollowingBand(data, opts) { + if (!data) { + return null; + } + + const band = { + name: data.name || null, + url: null, + location: data.location || '', + imageUrl: '' + } + if (data.url_hints && data.url_hints.subdomain) { + band.url = 'https://' + data.url_hints.subdomain + '.bandcamp.com'; + } + if (data.image_id) { + band.imageUrl = opts.imageBaseUrl + '/img/' + data.image_id + '_' + opts.imageFormat.id + '.jpg'; + } + + return band; +} + + +function parseFanFollowingGenresFromPage(html, opts) { + return _commonParseFanPageItems(html, opts, { + itemType: 'following_genres', + parseFn: _parseFanFollowingGenre + }); +} + +function parseFanFollowingGenresFromContinuation(json, continuationToken, opts) { + return _commonParseFanContinuationItems(json, continuationToken, opts, { + listKey: 'followeers', + parseFn: _parseFanFollowingGenre + }); +} + +function _parseFanFollowingGenre(data, opts) { + if (!data) { + return null; + } + + const genre = { + type: 'tag', + name: data.display_name, + value: data.token, + url: data.tag_page_url, + imageUrls: [] + }; + if (Array.isArray(data.art_ids)) { + data.art_ids.forEach(artId => { + genre.imageUrls.push(opts.imageBaseUrl + '/img/a' + artId + '_' + opts.imageFormat.id + '.jpg') + }) + } + + return genre; +} + function _parseBackToLabelLink($) { let labelLink = $('.back-to-label-link'); if (labelLink.length) { @@ -1318,5 +1562,12 @@ module.exports = { parseReleasesByTagFilterOptions, parseReleasesByTag, parseSearchTagResults, - parseSearchLocationResults -}; \ No newline at end of file + parseSearchLocationResults, + parseFanInfo, + parseFanWishlistFromPage, + parseFanWishlistFromContinuation, + parseFanFollowingBandsFromPage, + parseFanFollowingBandsFromContinuation, + parseFanFollowingGenresFromPage, + parseFanFollowingGenresFromContinuation +}; diff --git a/lib/utils.js b/lib/utils.js index 6551dcf..4d9db4f 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -168,6 +168,22 @@ function getSearchLocationUrl() { return 'https://bandcamp.com/api/location/1/geoname_search'; } +function getFanPageUrl(username) { + return getSiteUrl() + '/' + username; +} + +function getFanWishlistContinuationUrl() { + return 'https://bandcamp.com/api/fancollection/1/wishlist_items'; +} + +function getFanFollowingBandsContinuationUrl() { + return 'https://bandcamp.com/api/fancollection/1/following_bands'; +} + +function getFanFollowingGenresContinuationUrl() { + return 'https://bandcamp.com/api/fancollection/1/following_genres'; +} + module.exports = { getUrl, getSiteUrl, @@ -189,5 +205,9 @@ module.exports = { getReleasesByTagUrl, getDigDeeperUrl, getSearchTagUrl, - getSearchLocationUrl -}; \ No newline at end of file + getSearchLocationUrl, + getFanPageUrl, + getFanWishlistContinuationUrl, + getFanFollowingBandsContinuationUrl, + getFanFollowingGenresContinuationUrl +};