From be7010c217363386fe11aeef86caa1d58309da74 Mon Sep 17 00:00:00 2001 From: patrickkfkan Date: Sat, 10 Jun 2023 22:52:27 +0800 Subject: [PATCH] v1.0.0 --- .eslintignore | 4 + .eslintrc.yml | 84 + .gitignore | 3 +- .npmignore | 4 + README.md | 1091 +++-- docs/api/.nojekyll | 1 + docs/api/README.md | 178 + docs/api/classes/AlbumAPI.md | 45 + docs/api/classes/ArticleAPI.md | 81 + docs/api/classes/AutocompleteAPI.md | 77 + docs/api/classes/BandAPI.md | 87 + docs/api/classes/Cache.md | 82 + docs/api/classes/DiscoveryAPI.md | 81 + docs/api/classes/FanAPI.md | 129 + docs/api/classes/ImageAPI.md | 67 + docs/api/classes/LimiterAlbumAPI.md | 53 + docs/api/classes/LimiterArticleAPI.md | 97 + docs/api/classes/LimiterAutocompleteAPI.md | 93 + docs/api/classes/LimiterBandAPI.md | 103 + docs/api/classes/LimiterDiscoveryAPI.md | 97 + docs/api/classes/LimiterFanAPI.md | 153 + docs/api/classes/LimiterImageAPI.md | 79 + docs/api/classes/LimiterSearchAPI.md | 153 + docs/api/classes/LimiterShowAPI.md | 78 + docs/api/classes/LimiterTagAPI.md | 147 + docs/api/classes/LimiterTrackAPI.md | 53 + docs/api/classes/SearchAPI.md | 129 + docs/api/classes/ShowAPI.md | 66 + docs/api/classes/TagAPI.md | 123 + docs/api/classes/TrackAPI.md | 45 + docs/api/enums/AutocompleteItemType.md | 30 + docs/api/enums/CacheDataType.md | 30 + docs/api/enums/ImageFormatFilter.md | 34 + docs/api/enums/SearchItemType.md | 63 + docs/api/interfaces/Album.md | 240 + docs/api/interfaces/AlbumAPIGetInfoParams.md | 52 + docs/api/interfaces/AlbumHighlightsByTag.md | 41 + docs/api/interfaces/AlbumRelease.md | 63 + docs/api/interfaces/Article.md | 152 + .../interfaces/ArticleAPIGetArticleParams.md | 52 + docs/api/interfaces/ArticleAPIListParams.md | 41 + docs/api/interfaces/ArticleCategory.md | 30 + docs/api/interfaces/ArticleCategorySection.md | 52 + docs/api/interfaces/ArticleList.md | 52 + docs/api/interfaces/ArticleListItem.md | 63 + docs/api/interfaces/ArticleSection.md | 59 + docs/api/interfaces/Artist.md | 122 + docs/api/interfaces/AutoCompleteTag.md | 66 + .../AutocompleteAPIGetSuggestionsParams.md | 41 + docs/api/interfaces/AutocompleteItem.md | 38 + docs/api/interfaces/AutocompleteLocation.md | 66 + .../interfaces/BandAPIGetDiscographyParams.md | 30 + docs/api/interfaces/BandAPIGetInfoParams.md | 41 + .../BandAPIGetLabelArtistsParams.md | 30 + docs/api/interfaces/DiscoverOptions.md | 91 + docs/api/interfaces/DiscoverParams.md | 128 + docs/api/interfaces/DiscoverResult.md | 49 + docs/api/interfaces/Fan.md | 166 + docs/api/interfaces/FanAPIGetInfoParams.md | 30 + docs/api/interfaces/FanAPIGetItemsParams.md | 30 + .../interfaces/FanContinuationItemsResult.md | 36 + docs/api/interfaces/FanItemsContinuation.md | 30 + docs/api/interfaces/FanPageItemsResult.md | 47 + docs/api/interfaces/ImageConstants.md | 30 + docs/api/interfaces/ImageFormat.md | 63 + docs/api/interfaces/Label.md | 111 + docs/api/interfaces/MediaKind.md | 111 + docs/api/interfaces/NameValuePair.md | 42 + docs/api/interfaces/ReleasesByTag-1.md | 41 + docs/api/interfaces/ReleasesByTag.Filter.md | 32 + .../interfaces/ReleasesByTag.FilterOption.md | 68 + .../ReleasesByTag.FilterValueNames.md | 9 + docs/api/interfaces/SearchAPISearchParams.md | 52 + docs/api/interfaces/SearchResultAlbum.md | 129 + docs/api/interfaces/SearchResultArtist.md | 107 + docs/api/interfaces/SearchResultFan.md | 85 + docs/api/interfaces/SearchResultItem.md | 66 + docs/api/interfaces/SearchResultLabel.md | 85 + docs/api/interfaces/SearchResultTrack.md | 118 + docs/api/interfaces/SearchResults.md | 36 + docs/api/interfaces/Show.md | 158 + docs/api/interfaces/ShowAPIGetShowParams.md | 52 + docs/api/interfaces/ShowAPIListParams.md | 19 + docs/api/interfaces/Tag.md | 85 + .../TagAPIGetAlbumHighlightsParams.md | 30 + .../api/interfaces/TagAPIGetReleasesParams.md | 63 + docs/api/interfaces/TagList.md | 30 + docs/api/interfaces/Track.md | 207 + docs/api/interfaces/TrackAPIGetInfoParams.md | 52 + docs/api/interfaces/UserKind.md | 73 + docs/api/modules/ReleasesByTag.md | 11 + examples/album/getInfo.ts | 15 + examples/album/getInfo_output.txt | 139 + examples/article/getArticle.ts | 13 + examples/article/getArticle_output.txt | 531 +++ examples/article/getCategories.ts | 6 + examples/article/getCategories_output.txt | 234 + examples/article/list.ts | 11 + examples/article/list_output.txt | 307 ++ examples/autocomplete/getSuggestions.ts | 30 + .../autocomplete/getSuggestions_output.txt | 101 + examples/band/getDiscography.ts | 27 + examples/band/getDiscography_output.txt | 849 ++++ examples/band/getInfo.ts | 28 + .../getInfo_output.txt} | 37 +- examples/band/getLabelArtists.ts | 13 + examples/band/getLabelArtists_output.txt | 603 +++ examples/discover.js | 15 - examples/discover_output.txt | 730 --- examples/discovery/discover.ts | 12 + examples/discovery/discover_output.txt | 926 ++++ examples/discovery/getAvailableOptions.ts | 6 + .../discovery/getAvailableOptions_output.txt | 410 ++ examples/discovery/sanitizeDiscoverParams.ts | 9 + .../sanitizeDiscoverParams_output.txt | 6 +- examples/fan/getCollection.ts | 23 + .../getCollection_output.txt} | 565 +-- examples/fan/getFollowingArtistsAndLabels.ts | 23 + .../getFollowingArtistsAndLabels_output.txt} | 208 +- examples/fan/getFollowingGenres.ts | 23 + examples/fan/getFollowingGenres_output.txt | 492 ++ examples/fan/getInfo.ts | 13 + .../getInfo_output.txt} | 7 +- examples/fan/getWishlist.ts | 23 + .../getWishlist_output.txt} | 264 +- examples/getAlbumHighlightsByTag.js | 12 - examples/getAlbumHighlightsByTag_output.txt | 565 --- examples/getAlbumInfo.js | 14 - examples/getAlbumInfo_output.txt | 107 - examples/getAllShows.js | 6 - examples/getAllShows_output.txt | 901 ---- examples/getArticle.js | 12 - examples/getArticleCategories.js | 6 - examples/getArticleCategories_output.txt | 126 - examples/getArticleList.js | 11 - examples/getArticleList_output.txt | 95 - examples/getArticle_output.txt | 361 -- examples/getArtistOrLabelInfo.js | 21 - examples/getDiscography.js | 21 - examples/getDiscography_output.txt | 595 --- examples/getDiscoverOptions.js | 6 - examples/getDiscoverOptions_output.txt | 375 -- examples/getFanCollection.js | 19 - examples/getFanFollowingArtistsAndLabels.js | 19 - examples/getFanFollowingGenres.js | 19 - examples/getFanFollowingGenres_output.txt | 492 -- examples/getFanInfo.js | 12 - examples/getFanWishlist.js | 19 - examples/getImageFormats.js | 13 - examples/getLabelArtists.js | 12 - examples/getLabelArtists_output.txt | 401 -- examples/getReleasesByTag.js | 23 - examples/getReleasesByTagFilterOptions.js | 8 - .../getReleasesByTagFilterOptions_output.txt | 58 - examples/getReleasesByTag_output.txt | 217 - examples/getShow.js | 8 - examples/getShow_output.txt | 343 -- examples/getTagInfo.js | 10 - examples/getTagInfo_output.txt | 80 - examples/getTags.js | 6 - examples/getTags_output.txt | 260 - examples/getTrackInfo.js | 14 - examples/getTrackInfo_output.txt | 21 - examples/image/getFormats.ts | 13 + .../getFormats_output.txt} | 87 +- examples/limiter.js | 90 - examples/limiter/limiter.ts | 96 + examples/{ => limiter}/limiter_output.txt | 102 +- examples/sanitizeDiscoverParams.js | 9 - examples/search.js | 28 - examples/search/search.ts | 39 + examples/search/search_output.txt | 769 +++ examples/searchLocation.js | 11 - examples/searchLocation_output.txt | 26 - examples/searchTag.js | 11 - examples/searchTag_output.txt | 8 - examples/search_output.txt | 315 -- examples/show/getShow.ts | 12 + examples/show/getShow_output.txt | 450 ++ examples/show/list.ts | 6 + examples/show/list_output.txt | 1104 +++++ examples/tag/getAlbumHighlights.ts | 13 + examples/tag/getAlbumHighlights_output.txt | 776 +++ examples/tag/getInfo.ts | 8 + examples/tag/getInfo_output.txt | 113 + examples/tag/getReleases.ts | 19 + examples/tag/getReleasesAvailableFilters.ts | 8 + .../getReleasesAvailableFilters_output.txt | 74 + examples/tag/getReleases_output.txt | 301 ++ examples/tag/list.ts | 6 + examples/tag/list_output.txt | 334 ++ examples/track/getInfo.ts | 15 + examples/track/getInfo_output.txt | 16 + fixup.sh | 11 + lib/cache.js | 94 - lib/index.js | 604 --- lib/parser.js | 1597 ------- lib/utils.js | 218 - package-lock.json | 4188 +++++++++++++++++ package.json | 70 +- src/index-cjs.ts | 4 + src/index.ts | 109 + src/lib/album/AlbumAPI.ts | 33 + src/lib/album/AlbumInfoParser.ts | 147 + src/lib/article/ArticleAPI.ts | 66 + src/lib/article/ArticleCategoryParser.ts | 71 + src/lib/article/ArticleListParser.ts | 80 + src/lib/article/ArticleParser.ts | 209 + src/lib/autocomplete/AutocompleteAPI.ts | 61 + .../autocomplete/AutocompleteResultsParser.ts | 31 + src/lib/band/BandAPI.ts | 137 + src/lib/band/BandInfoParser.ts | 77 + src/lib/band/DiscographyParser.ts | 155 + src/lib/band/LabelArtistsParser.ts | 33 + src/lib/discovery/DiscoverOptionsParser.ts | 60 + src/lib/discovery/DiscoverResultParser.ts | 61 + src/lib/discovery/DiscoveryAPI.ts | 142 + src/lib/fan/FanAPI.ts | 151 + src/lib/fan/FanCollectionParser.ts | 26 + src/lib/fan/FanFollowingParser.ts | 76 + src/lib/fan/FanInfoParser.ts | 52 + src/lib/fan/FanItemsBaseParser.ts | 98 + src/lib/fan/FanWishlistParser.ts | 92 + src/lib/image/ImageAPI.ts | 69 + src/lib/image/ImageParser.ts | 34 + src/lib/search/SearchAPI.ts | 106 + src/lib/search/SearchResultsParser.ts | 176 + src/lib/show/ShowAPI.ts | 56 + src/lib/show/ShowListParser.ts | 38 + src/lib/show/ShowParser.ts | 103 + src/lib/tag/AlbumHighlightsByTagParser.ts | 68 + src/lib/tag/ReleasesByTagParser.ts | 189 + src/lib/tag/TagAPI.ts | 162 + src/lib/tag/TagInfoParser.ts | 40 + src/lib/tag/TagListParser.ts | 38 + src/lib/track/TrackAPI.ts | 34 + src/lib/track/TrackInfoParser.ts | 165 + src/lib/types/Album.ts | 24 + src/lib/types/Article.ts | 71 + src/lib/types/Artist.ts | 10 + src/lib/types/Autocomplete.ts | 16 + src/lib/types/Discovery.ts | 60 + src/lib/types/Fan.ts | 18 + src/lib/types/Image.ts | 12 + src/lib/types/Label.ts | 11 + src/lib/types/MediaKind.ts | 19 + src/lib/types/Search.ts | 47 + src/lib/types/Show.ts | 22 + src/lib/types/Tag.ts | 50 + src/lib/types/Track.ts | 13 + src/lib/types/UserKind.ts | 9 + src/lib/utils/Cache.ts | 99 + src/lib/utils/Constants.ts | 21 + src/lib/utils/Fetch.ts | 81 + src/lib/utils/Limiter.ts | 16 + src/lib/utils/NameValuePair.ts | 6 + src/lib/utils/Parse.ts | 168 + tsconfig-base.json | 18 + tsconfig-esm.json | 8 + tsconfig.json | 7 + typedoc.json | 9 + 261 files changed, 25201 insertions(+), 10034 deletions(-) create mode 100644 .eslintignore create mode 100644 .eslintrc.yml create mode 100644 .npmignore create mode 100644 docs/api/.nojekyll create mode 100644 docs/api/README.md create mode 100644 docs/api/classes/AlbumAPI.md create mode 100644 docs/api/classes/ArticleAPI.md create mode 100644 docs/api/classes/AutocompleteAPI.md create mode 100644 docs/api/classes/BandAPI.md create mode 100644 docs/api/classes/Cache.md create mode 100644 docs/api/classes/DiscoveryAPI.md create mode 100644 docs/api/classes/FanAPI.md create mode 100644 docs/api/classes/ImageAPI.md create mode 100644 docs/api/classes/LimiterAlbumAPI.md create mode 100644 docs/api/classes/LimiterArticleAPI.md create mode 100644 docs/api/classes/LimiterAutocompleteAPI.md create mode 100644 docs/api/classes/LimiterBandAPI.md create mode 100644 docs/api/classes/LimiterDiscoveryAPI.md create mode 100644 docs/api/classes/LimiterFanAPI.md create mode 100644 docs/api/classes/LimiterImageAPI.md create mode 100644 docs/api/classes/LimiterSearchAPI.md create mode 100644 docs/api/classes/LimiterShowAPI.md create mode 100644 docs/api/classes/LimiterTagAPI.md create mode 100644 docs/api/classes/LimiterTrackAPI.md create mode 100644 docs/api/classes/SearchAPI.md create mode 100644 docs/api/classes/ShowAPI.md create mode 100644 docs/api/classes/TagAPI.md create mode 100644 docs/api/classes/TrackAPI.md create mode 100644 docs/api/enums/AutocompleteItemType.md create mode 100644 docs/api/enums/CacheDataType.md create mode 100644 docs/api/enums/ImageFormatFilter.md create mode 100644 docs/api/enums/SearchItemType.md create mode 100644 docs/api/interfaces/Album.md create mode 100644 docs/api/interfaces/AlbumAPIGetInfoParams.md create mode 100644 docs/api/interfaces/AlbumHighlightsByTag.md create mode 100644 docs/api/interfaces/AlbumRelease.md create mode 100644 docs/api/interfaces/Article.md create mode 100644 docs/api/interfaces/ArticleAPIGetArticleParams.md create mode 100644 docs/api/interfaces/ArticleAPIListParams.md create mode 100644 docs/api/interfaces/ArticleCategory.md create mode 100644 docs/api/interfaces/ArticleCategorySection.md create mode 100644 docs/api/interfaces/ArticleList.md create mode 100644 docs/api/interfaces/ArticleListItem.md create mode 100644 docs/api/interfaces/ArticleSection.md create mode 100644 docs/api/interfaces/Artist.md create mode 100644 docs/api/interfaces/AutoCompleteTag.md create mode 100644 docs/api/interfaces/AutocompleteAPIGetSuggestionsParams.md create mode 100644 docs/api/interfaces/AutocompleteItem.md create mode 100644 docs/api/interfaces/AutocompleteLocation.md create mode 100644 docs/api/interfaces/BandAPIGetDiscographyParams.md create mode 100644 docs/api/interfaces/BandAPIGetInfoParams.md create mode 100644 docs/api/interfaces/BandAPIGetLabelArtistsParams.md create mode 100644 docs/api/interfaces/DiscoverOptions.md create mode 100644 docs/api/interfaces/DiscoverParams.md create mode 100644 docs/api/interfaces/DiscoverResult.md create mode 100644 docs/api/interfaces/Fan.md create mode 100644 docs/api/interfaces/FanAPIGetInfoParams.md create mode 100644 docs/api/interfaces/FanAPIGetItemsParams.md create mode 100644 docs/api/interfaces/FanContinuationItemsResult.md create mode 100644 docs/api/interfaces/FanItemsContinuation.md create mode 100644 docs/api/interfaces/FanPageItemsResult.md create mode 100644 docs/api/interfaces/ImageConstants.md create mode 100644 docs/api/interfaces/ImageFormat.md create mode 100644 docs/api/interfaces/Label.md create mode 100644 docs/api/interfaces/MediaKind.md create mode 100644 docs/api/interfaces/NameValuePair.md create mode 100644 docs/api/interfaces/ReleasesByTag-1.md create mode 100644 docs/api/interfaces/ReleasesByTag.Filter.md create mode 100644 docs/api/interfaces/ReleasesByTag.FilterOption.md create mode 100644 docs/api/interfaces/ReleasesByTag.FilterValueNames.md create mode 100644 docs/api/interfaces/SearchAPISearchParams.md create mode 100644 docs/api/interfaces/SearchResultAlbum.md create mode 100644 docs/api/interfaces/SearchResultArtist.md create mode 100644 docs/api/interfaces/SearchResultFan.md create mode 100644 docs/api/interfaces/SearchResultItem.md create mode 100644 docs/api/interfaces/SearchResultLabel.md create mode 100644 docs/api/interfaces/SearchResultTrack.md create mode 100644 docs/api/interfaces/SearchResults.md create mode 100644 docs/api/interfaces/Show.md create mode 100644 docs/api/interfaces/ShowAPIGetShowParams.md create mode 100644 docs/api/interfaces/ShowAPIListParams.md create mode 100644 docs/api/interfaces/Tag.md create mode 100644 docs/api/interfaces/TagAPIGetAlbumHighlightsParams.md create mode 100644 docs/api/interfaces/TagAPIGetReleasesParams.md create mode 100644 docs/api/interfaces/TagList.md create mode 100644 docs/api/interfaces/Track.md create mode 100644 docs/api/interfaces/TrackAPIGetInfoParams.md create mode 100644 docs/api/interfaces/UserKind.md create mode 100644 docs/api/modules/ReleasesByTag.md create mode 100644 examples/album/getInfo.ts create mode 100644 examples/album/getInfo_output.txt create mode 100644 examples/article/getArticle.ts create mode 100644 examples/article/getArticle_output.txt create mode 100644 examples/article/getCategories.ts create mode 100644 examples/article/getCategories_output.txt create mode 100644 examples/article/list.ts create mode 100644 examples/article/list_output.txt create mode 100644 examples/autocomplete/getSuggestions.ts create mode 100644 examples/autocomplete/getSuggestions_output.txt create mode 100644 examples/band/getDiscography.ts create mode 100644 examples/band/getDiscography_output.txt create mode 100644 examples/band/getInfo.ts rename examples/{getArtistOrLabelInfo_output.txt => band/getInfo_output.txt} (68%) create mode 100644 examples/band/getLabelArtists.ts create mode 100644 examples/band/getLabelArtists_output.txt delete mode 100644 examples/discover.js delete mode 100644 examples/discover_output.txt create mode 100644 examples/discovery/discover.ts create mode 100644 examples/discovery/discover_output.txt create mode 100644 examples/discovery/getAvailableOptions.ts create mode 100644 examples/discovery/getAvailableOptions_output.txt create mode 100644 examples/discovery/sanitizeDiscoverParams.ts rename examples/{ => discovery}/sanitizeDiscoverParams_output.txt (67%) create mode 100644 examples/fan/getCollection.ts rename examples/{getFanCollection_output.txt => fan/getCollection_output.txt} (55%) create mode 100644 examples/fan/getFollowingArtistsAndLabels.ts rename examples/{getFanFollowingArtistsAndLabels_output.txt => fan/getFollowingArtistsAndLabels_output.txt} (84%) create mode 100644 examples/fan/getFollowingGenres.ts create mode 100644 examples/fan/getFollowingGenres_output.txt create mode 100644 examples/fan/getInfo.ts rename examples/{getFanInfo_output.txt => fan/getInfo_output.txt} (70%) create mode 100644 examples/fan/getWishlist.ts rename examples/{getFanWishlist_output.txt => fan/getWishlist_output.txt} (75%) delete mode 100644 examples/getAlbumHighlightsByTag.js delete mode 100644 examples/getAlbumHighlightsByTag_output.txt delete mode 100644 examples/getAlbumInfo.js delete mode 100644 examples/getAlbumInfo_output.txt delete mode 100644 examples/getAllShows.js delete mode 100644 examples/getAllShows_output.txt delete mode 100644 examples/getArticle.js delete mode 100644 examples/getArticleCategories.js delete mode 100644 examples/getArticleCategories_output.txt delete mode 100644 examples/getArticleList.js delete mode 100644 examples/getArticleList_output.txt delete mode 100644 examples/getArticle_output.txt delete mode 100644 examples/getArtistOrLabelInfo.js delete mode 100644 examples/getDiscography.js delete mode 100644 examples/getDiscography_output.txt delete mode 100644 examples/getDiscoverOptions.js delete mode 100644 examples/getDiscoverOptions_output.txt delete mode 100644 examples/getFanCollection.js delete mode 100644 examples/getFanFollowingArtistsAndLabels.js delete mode 100644 examples/getFanFollowingGenres.js delete mode 100644 examples/getFanFollowingGenres_output.txt delete mode 100644 examples/getFanInfo.js delete mode 100644 examples/getFanWishlist.js delete mode 100644 examples/getImageFormats.js delete mode 100644 examples/getLabelArtists.js delete mode 100644 examples/getLabelArtists_output.txt delete mode 100644 examples/getReleasesByTag.js delete mode 100644 examples/getReleasesByTagFilterOptions.js delete mode 100644 examples/getReleasesByTagFilterOptions_output.txt delete mode 100644 examples/getReleasesByTag_output.txt delete mode 100644 examples/getShow.js delete mode 100644 examples/getShow_output.txt delete mode 100644 examples/getTagInfo.js delete mode 100644 examples/getTagInfo_output.txt delete mode 100644 examples/getTags.js delete mode 100644 examples/getTags_output.txt delete mode 100644 examples/getTrackInfo.js delete mode 100644 examples/getTrackInfo_output.txt create mode 100644 examples/image/getFormats.ts rename examples/{getImageFormats_output.txt => image/getFormats_output.txt} (60%) delete mode 100644 examples/limiter.js create mode 100644 examples/limiter/limiter.ts rename examples/{ => limiter}/limiter_output.txt (53%) delete mode 100644 examples/sanitizeDiscoverParams.js delete mode 100644 examples/search.js create mode 100644 examples/search/search.ts create mode 100644 examples/search/search_output.txt delete mode 100644 examples/searchLocation.js delete mode 100644 examples/searchLocation_output.txt delete mode 100644 examples/searchTag.js delete mode 100644 examples/searchTag_output.txt delete mode 100644 examples/search_output.txt create mode 100644 examples/show/getShow.ts create mode 100644 examples/show/getShow_output.txt create mode 100644 examples/show/list.ts create mode 100644 examples/show/list_output.txt create mode 100644 examples/tag/getAlbumHighlights.ts create mode 100644 examples/tag/getAlbumHighlights_output.txt create mode 100644 examples/tag/getInfo.ts create mode 100644 examples/tag/getInfo_output.txt create mode 100644 examples/tag/getReleases.ts create mode 100644 examples/tag/getReleasesAvailableFilters.ts create mode 100644 examples/tag/getReleasesAvailableFilters_output.txt create mode 100644 examples/tag/getReleases_output.txt create mode 100644 examples/tag/list.ts create mode 100644 examples/tag/list_output.txt create mode 100644 examples/track/getInfo.ts create mode 100644 examples/track/getInfo_output.txt create mode 100644 fixup.sh delete mode 100644 lib/cache.js delete mode 100644 lib/index.js delete mode 100644 lib/parser.js delete mode 100644 lib/utils.js create mode 100644 package-lock.json create mode 100644 src/index-cjs.ts create mode 100644 src/index.ts create mode 100644 src/lib/album/AlbumAPI.ts create mode 100644 src/lib/album/AlbumInfoParser.ts create mode 100644 src/lib/article/ArticleAPI.ts create mode 100644 src/lib/article/ArticleCategoryParser.ts create mode 100644 src/lib/article/ArticleListParser.ts create mode 100644 src/lib/article/ArticleParser.ts create mode 100644 src/lib/autocomplete/AutocompleteAPI.ts create mode 100644 src/lib/autocomplete/AutocompleteResultsParser.ts create mode 100644 src/lib/band/BandAPI.ts create mode 100644 src/lib/band/BandInfoParser.ts create mode 100644 src/lib/band/DiscographyParser.ts create mode 100644 src/lib/band/LabelArtistsParser.ts create mode 100644 src/lib/discovery/DiscoverOptionsParser.ts create mode 100644 src/lib/discovery/DiscoverResultParser.ts create mode 100644 src/lib/discovery/DiscoveryAPI.ts create mode 100644 src/lib/fan/FanAPI.ts create mode 100644 src/lib/fan/FanCollectionParser.ts create mode 100644 src/lib/fan/FanFollowingParser.ts create mode 100644 src/lib/fan/FanInfoParser.ts create mode 100644 src/lib/fan/FanItemsBaseParser.ts create mode 100644 src/lib/fan/FanWishlistParser.ts create mode 100644 src/lib/image/ImageAPI.ts create mode 100644 src/lib/image/ImageParser.ts create mode 100644 src/lib/search/SearchAPI.ts create mode 100644 src/lib/search/SearchResultsParser.ts create mode 100644 src/lib/show/ShowAPI.ts create mode 100644 src/lib/show/ShowListParser.ts create mode 100644 src/lib/show/ShowParser.ts create mode 100644 src/lib/tag/AlbumHighlightsByTagParser.ts create mode 100644 src/lib/tag/ReleasesByTagParser.ts create mode 100644 src/lib/tag/TagAPI.ts create mode 100644 src/lib/tag/TagInfoParser.ts create mode 100644 src/lib/tag/TagListParser.ts create mode 100644 src/lib/track/TrackAPI.ts create mode 100644 src/lib/track/TrackInfoParser.ts create mode 100644 src/lib/types/Album.ts create mode 100644 src/lib/types/Article.ts create mode 100644 src/lib/types/Artist.ts create mode 100644 src/lib/types/Autocomplete.ts create mode 100644 src/lib/types/Discovery.ts create mode 100644 src/lib/types/Fan.ts create mode 100644 src/lib/types/Image.ts create mode 100644 src/lib/types/Label.ts create mode 100644 src/lib/types/MediaKind.ts create mode 100644 src/lib/types/Search.ts create mode 100644 src/lib/types/Show.ts create mode 100644 src/lib/types/Tag.ts create mode 100644 src/lib/types/Track.ts create mode 100644 src/lib/types/UserKind.ts create mode 100644 src/lib/utils/Cache.ts create mode 100644 src/lib/utils/Constants.ts create mode 100644 src/lib/utils/Fetch.ts create mode 100644 src/lib/utils/Limiter.ts create mode 100644 src/lib/utils/NameValuePair.ts create mode 100644 src/lib/utils/Parse.ts create mode 100644 tsconfig-base.json create mode 100644 tsconfig-esm.json create mode 100644 tsconfig.json create mode 100644 typedoc.json diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..e9038f2 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,4 @@ +.git +.github +node_modules/ +dist/ \ No newline at end of file diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 0000000..81915f2 --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,84 @@ +plugins: + [ '@typescript-eslint' ] +env: + commonjs: true + es2021: true + node: true +extends: [ eslint:recommended, 'plugin:@typescript-eslint/recommended' ] +parser: '@typescript-eslint/parser' +parserOptions: + ecmaVersion: latest +overrides: + - + files: + - '**/*.js' +rules: + max-len: + - error + - + code: 200 + ignoreComments: true + ignoreTrailingComments: true + ignoreStrings: true + ignoreTemplateLiterals: true + ignoreRegExpLiterals: true + + quotes: [error, single] + + '@typescript-eslint/ban-types': 'off' + '@typescript-eslint/no-explicit-any': 'off' + + no-template-curly-in-string: error + no-unreachable-loop: error + no-unused-private-class-members: 'off' + no-prototype-builtins: 'off' + no-async-promise-executor: 'off' + no-case-declarations: 'off' + no-return-assign: 'off' + no-floating-decimal: error + no-implied-eval: error + arrow-spacing: error + no-invalid-this: error + no-lone-blocks: 'off' + no-new-func: error + no-new-wrappers: error + no-new: error + no-void: error + no-octal-escape: error + no-self-compare: error + no-sequences: error + no-throw-literal: error + no-unmodified-loop-condition: error + no-useless-call: error + no-useless-concat: error + no-useless-escape: error + no-useless-return: error + no-else-return: error + no-lonely-if: error + no-undef-init: error + no-unneeded-ternary: error + no-var: error + no-multi-spaces: error + no-multiple-empty-lines: ["error", { "max": 2, "maxEOF": 0 }] + no-tabs: error + no-trailing-spaces: error + + brace-style: ["error", "stroustrup"] + new-parens: error + space-infix-ops: error + template-curly-spacing: error + wrap-regex: error + capitalized-comments: error + prefer-template: error + + keyword-spacing: ["error", { "before": true } ] + array-bracket-spacing: ["error", "always"] + arrow-parens: ["error", "always"] + comma-dangle: ["error", "never"] + comma-spacing: ["error", { "before": false, "after": true }] + computed-property-spacing: ["error", "never"] + func-call-spacing: ["error", "never"] + indent: ["error", 2, { "SwitchCase": 1 }] + key-spacing: ["error", { "beforeColon": false }] + semi: ["error", "always"] + operator-assignment: ["error", "always"] \ No newline at end of file diff --git a/.gitignore b/.gitignore index b512c09..b947077 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -node_modules \ No newline at end of file +node_modules/ +dist/ diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..9d5b80c --- /dev/null +++ b/.npmignore @@ -0,0 +1,4 @@ +** + +!dist/** +!README.md diff --git a/README.md b/README.md index 16886b2..caaa7ff 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,19 @@ # bandcamp-fetch -A JS library for scraping Bandcamp content; inspired by [bandcamp-scraper](https://github.com/masterT/bandcamp-scraper). +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 @@ -11,343 +24,805 @@ npm i bandcamp-fetch --save # Usage ``` -const bcfetch = require('bandcamp-fetch'); +import bcfetch from 'bandcamp-fetch'; -bcfetch.discover(...).then( results => { - ... -}); +const results = await bcfetch.discovery.discover(...); ``` # API -Each function returns a Promise which resolves to the fetched data. +## Discovery API -### `discover([params], [options])` +To access the Discovery API: -[**Example**](examples/discover.js) ([output](examples/discover_output.txt)) +``` +import bcfetch from 'bandcamp-fetch'; -Fetches albums through Bandcamp Discover. +const discovery = bcfetch.discovery; -- `params` (optional) - object specifying params to be passed to Bandcamp Discover - - genre - - subgenre: only valid when genre is something other than 'all' - - location - - sortBy - - artistRecommendationType: only valid when sortBy is 'rec' (artist recommended) - - format - - time - - page +const options = await discovery.getAvailableOptions(); +const results = await discovery.discover(...); +``` +**Methods:** +
+discover([params]) +
- All properties are optional. Possible values for each property can be obtained with the `getDiscoverOptions()` function. +[**Example**](examples/discovery/discover.ts) ([output](examples/discovery/discover_output.txt)) - `params` passed to this function will be sanitized with `sanitizeDiscoverParams()`. A copy of the sanitized params can obtained through the `params` property of the returned result. +

Fetches albums through Bandcamp Discover.

-- `options` (optional) - object specifying options to be used when formulating results: - - albumImageFormat: name, Id or object referring to an image format. - - artistImageFormat +**Params** - All properties are optional. Image formats can be obtained with the `getImageFormats()` function. +- `params`: ([DiscoverParams](docs/api/interfaces/DiscoverParams.md)) (*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](docs/api/interfaces/ImageFormat.md)) + - `artistImageFormat`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) -### `getDiscoverOptions()` +To see what values can be set in `params`, call `getAvailableOptions()`. -[**Example**](examples/getDiscoverOptions.js) ([output](examples/getDiscoverOptions_output.txt)) +**Returns** -Fetches Bandcamp Discover options that can be passed back to `discover()`. +Promise resolving to [DiscoverResult](docs/api/interfaces/DiscoverResult.md). -### `sanitizeDiscoverParams(params)` +--- +
-[**Example**](examples/sanitizeDiscoverParams.js) ([output](examples/sanitizeDiscoverParams_output.txt)) +
+getAvailableOptions() +
-Sanitizes `params` by setting default values for omitted params and removing irrelevant ones. +[**Example**](examples/discovery/getAvailableOptions.ts) ([output](examples/discovery/getAvailableOptions_output.txt)) -You don't have to call this function on params passed to `discover()` - they will be sanitized automatically. +

Fetches Bandcamp Discover options that can be used to configure params for passing into discover().

-### `getImageFormats([filter])` +**Returns** -[**Example**](examples/getImageFormats.js) ([output](examples/getImageFormats_output.txt)) +Promise resolving to [DiscoverOptions](docs/api/interfaces/DiscoverOptions.md). -Fetches the list of image formats used in Bandcamp. +--- +
-- `filter` (optional) - 'bio' (for artist / profile-type images)* or 'album'. If specified, narrows down the result to include only formats applicable to the specified value. +
+sanitizeDiscoverParams([params]) +
-> The 'artist' filter value is deprecated. Use 'bio' instead. +[**Example**](examples/discovery/sanitizeDiscoverParams.ts) ([output](examples/discovery/sanitizeDiscoverParams_output.txt)) -### `getImageFormat(idOrName)` +

Sanitizes params by setting default values for omitted properties and removing irrelevant or inapplicable ones.

-Fetches the image format that matches Id or name. If none is found, the result will be `null`. +

+Note that you don't have to call this method on params passed into discover() as they will be sanitized automatically. +

-### `getArtistOrLabelInfo(artistOrLabelUrl, [options])` +**Params** -[**Example**](examples/getArtistOrLabelInfo.js) ([output](examples/getArtistOrLabelInfo_output.txt)) +- `params`: ([DiscoverParams](docs/api/interfaces/DiscoverParams.md)) (*optional*) the discover params to sanitize. -Fetches information about an artist or label. +**Returns** -- `artistOrLabelUrl` -- `options` (optional) - - imageFormat - - labelId +Promise resolving to sanitized [DiscoverParams](docs/api/interfaces/DiscoverParams.md). -This function tries to fetch 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 `artistOrLabelUrl` -2. The 'music' page of the artist or label (`artistOrLabelUrl/music`) +## 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:** + +
+getFormats([filter]) +
+ +[**Example**](examples/image/getFormats.ts) ([output](examples/image/getFormats_output.txt)) + +

Fetches the list of image formats used in Bandcamp.

+ +**Params** + +- `filter`: ([ImageFormatFilter](docs/api/enums/ImageFormatFilter.md)) (*optional*) if specified, narrows down the result to include only formats applicable to the specified value. + +**Returns** + +Promise resolving to Array<[ImageFormat](docs/api/interfaces/ImageFormat.md)>. + +--- +
+ +
+getImageFormat(target, [fallbackId]) +
+ +

Fetches the image format that matches target. If none is found, the result will be null.

+ +**Params** + +- `target`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) + - If target is string or number, the method finds the image format with matching name or Id (as appropriate). + - If target satisfies the [ImageFormat](docs/api/interfaces/ImageFormat.md) 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](docs/api/interfaces/ImageFormat.md), 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:** + +
+getInfo(params) +
+ +[**Example**](examples/band/getInfo.ts) ([output](examples/band/getInfo_output.txt)) + +

Fetches information about an artist or label.

+ +**Params** + +- `params`: ([BandAPIGetInfoParams](docs/api/interfaces/BandAPIGetInfoParams.md)) + - `bandUrl`: (string) + - `imageFormat`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) (*optional*) + - `labelId`: (number) (*optional*) + +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, the label could not be fetched successfully for artists. If you know the `labelId` of the label that the artist belongs to, you can specify it in `options`. 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. +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. -### `getLabelArtists(labelUrl, [options])` +**Returns** -[**Example**](examples/getLabelArtists.js) ([output](examples/getLabelArtists_output.txt)) +Promise resolving to [Artist](docs/api/interfaces/Artist.md) or [Label](docs/api/interfaces/Label.md). -Fetches the list of artists belonging to a label. +--- +
-- `labelUrl` -- `options` (optional) - - imageFormat +
+getLabelArtists(params) +
-### `getDiscography(artistOrLabelUrl, [options])` +[**Example**](examples/band/getLabelArtists.ts) ([output](examples/band/getLabelArtists_output.txt)) -[**Example**](examples/getDiscography.js) ([output](examples/getDiscography_output.txt)) +

Fetches the list of artists belonging to a label.

-Fetches the list of albums and standalone tracks belonging to an artist or label. +**Params** -- `artistOrLabelUrl` -- `options` (optional) - - imageFormat +- `params`: ([BandAPIGetLabelArtistsParams](docs/api/interfaces/BandAPIGetLabelArtistsParams.md)) + - `labelUrl`: (string) + - `imageFormat`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) (*optional*) -### `getAlbumInfo(albumUrl, [options])` +**Returns** -[**Example**](examples/getAlbumInfo.js) ([output](examples/getAlbumInfo_output.txt)) +Promise resolving to Array<[LabelArtist](docs/api/README.md#labelartist)>. -Fetches information about an album. +--- +
-- `albumUrl` -- `options` (optional) - - albumImageFormat - - artistImageFormat - - includeRawData +
+getDiscography(params) +
-Following Bandcamp's removal of description and image URL from artist metadata, `artist.description` and `artist.imageUrl` of the returned object are now set to to the same values as `publisher.description` and `publisher.imageUrl`, respectively. This is for backward comaptibility and the `artist.description` and `artist.imageUrl` properties might be removed in a future release. - -Furthermore, if the artist URL is not found in the scraped data, then `artist.url` will be set to the same value as `publisher.url`. This behavior might be subject to change in the future. +[**Example**](examples/band/getDiscography.ts) ([output](examples/band/getDiscography_output.txt)) -### `getTrackInfo(trackUrl, [options])` +

Fetches the list of albums and standalone tracks belonging to an artist or label.

-[**Example**](examples/getTrackInfo.js) ([output](examples/getTrackInfo_output.txt)) +**Params** -Fetches information about a track. +- `params`: ([BandAPIGetDiscographyParams](docs/api/interfaces/BandAPIGetDiscographyParams.md)) + - `bandUrl`: (string) + - `imageFormat`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) (*optional*) -- `trackUrl` -- `options` (optional) - - albumImageFormat - - artistImageFormat - - includeRawData +**Returns** -Following Bandcamp's removal of description and image URL from artist metadata, `artist.description` and `artist.imageUrl` of the returned object are now set to to the same values as `publisher.description` and `publisher.imageUrl`, respectively. This is for backward comaptibility and the `artist.description` and `artist.imageUrl` properties might be removed in a future release. +Promise resolving to Array<[Album](docs/api/interfaces/Album.md) | [Track](docs/api/interfaces/Track.md)>. -Furthermore, if the artist URL is not found in the scraped data, then `artist.url` will be set to the same value as `publisher.url`. This behavior might be subject to change in the future. +--- +
-### `getAlbumHighlightsByTag(tagUrl, [options])` -[**Example**](examples/getAlbumHighlightsByTag.js) ([output](examples/getAlbumHighlightsByTag_output.txt)) +## Album API -Fetches album highlights for the tag referred to by `tagUrl`. The result is an array of album collections, with each collection corresponding to a highlight category such as 'new and notable' and 'all-time best selling'. - -- `tagUrl` - - Tag URLs can be obtained with the `getTags()` function. - -- `options` (optional) - - imageFormat - -### `getTags()` - -[**Example**](examples/getTags.js) ([output](examples/getTags_output.txt)) - -Fetches Bandcamp tags. The result is an object with the following properties: -- `tags`: non-location tags -- `locations`: location tags - -### `search(params, [options])` - -[**Example**](examples/search.js) ([output](examples/search_output.txt)) - -Searches for `params.query`. - -- `params` - - query: search string - - itemType (optional): 'artistsAndLabels', 'albums' or 'tracks'. Searches all item types if unknown value or omitted. - - page (1 if omitted) -- `options` (optional) - - albumImageFormat - - artistImageFormat - -### `getAllShows([options])` - -[**Example**](examples/getAllShows.js) ([output](examples/getAllShows_output.txt)) - -Fetches all Bandcamp shows. Each entry in the returned array contains basic information about a show. To retrieve details of a show, pass the `url` property of the entry to `getShow()`. - -- `options` (optional) - - showImageFormat - -### `getShow(showUrl, [options])` - -[**Example**](examples/getShow.js) ([output](examples/getShow_output.txt)) - -Get show details for the given `showUrl`. - -- `options` (optional) - - albumImageFormat - - artistImageFormat - - showImageFormat - -### `getArticleCategories()` - -[**Example**](examples/getArticleCategories.js) ([output](examples/getArticleCategories_output.txt)) - -Fetches the list of Bandcamp Daily article categories. Categories are grouped into sections. - -### `getArticleList([params], [options])` - -[**Example**](examples/getArticleList.js) ([output](examples/getArticleList_output.txt)) - -Fetches the list of Bandcamp Daily articles under the category specified by `params.categoryUrl` (or all categories if not specified). - -- `params` (optional) - - categoryUrl -- `options` (optional) - - imageFormat - -### `getArticle(articleUrl, [options])` - -[**Example**](examples/getArticle.js) ([output](examples/getArticle_output.txt)) - -Fetches the contents of the Bandcamp Daily article at `articleUrl`. - -- `articleUrl` -- `options` (optional) - - albumImageFormat - - artistImageFormat - - includeRawData - -### `getTagInfo(tagUrl)` - -[**Example**](examples/getTagInfo.js) ([output](examples/getTagInfo_output.txt)) - -Fetches information about the tag referred to by `tagUrl`. - -### `getReleasesByTag(tagUrl, [params], [options])` - -[**Example**](examples/getReleasesByTag.js) ([output](examples/getReleasesByTag_output.txt)) - -Fetches releases matching the tag referred to by `tagUrl`. - -- `tagUrl` -- `params` (optional) - - filters: - - location - - tags: array of tag values to match, in addition to the one referred to by `tagUrl`. - - sort - - format - - page (1 if omitted) - - All properties are optional. For omitted properties, default values obtained from `tagUrl` will be used. Possible filter values can be obtained by calling `getReleasesByTagFilterOptions()`. For `filters.location` and `filters.tag`, you may look up additional values not returned by `getReleasesByTagFilterOptions()` through `searchLocation()` and `searchTag()`, respectively. - -- `options` (optional) - - imageFormat - - useHardcodedDefaultFilters: 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 `getReleasesByTagFilterOptions()` (extra query means slower performance). - -### `getReleasesByTagFilterOptions(tagUrl)` - -[**Example**](examples/getReleasesByTagFilterOptions.js) ([output](examples/getReleasesByTagFilterOptions_output.txt)) - -Fetches the list of possible filter values for `getReleasesByTag()`. For `location` and `tag` filters, this function does not return a conclusive list of values. You may use `searchLocation()` and `searchTag()` to look up additional values. - -- `tagUrl`: the URL of the tag for which filter values should be returned - -### `searchLocation(params)` - -[**Example**](examples/searchLocation.js) ([output](examples/searchLocation_output.txt)) - -Fetches the list of locations matching `params.q`. Results include both partial and full matches. Each item in the returned array corresponds to a matching location, and its `value` property can be used for setting the `location` filter in `getReleasesByTag()`. - -- `params`: - - q: the string to match - - limit: the maximum number of results to return - -### `searchTag(params)` - -[**Example**](examples/searchTag.js) ([output](examples/searchTag_output.txt)) - -Fetches the list of tags matching `params.q`. Results include both partial and full matches. Each item in the returned array corresponds to a matching tag, and its `value` property can be used for setting the `tags` filter in `getReleasesByTag()`. - -- `params`: - - 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 - -### `getFanCollection(usernameOrContinuationToken, [options])` - -[**Example**](examples/getFanCollection.js) ([output](examples/getFanCollection_output.txt)) - -Fetches the list of albums / tracks in a fan's collection. - -- `usernameOrContinuationToken`: if username is provided, returns the first batch of items in the collection. 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 - -### `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: +To access the Album API: ``` -bcfetch.limiter.getAlbumInfo(...); +import bcfetch from 'bandcamp-fetch'; + +const album = bcfetch.album; + +const info = await album.getInfo(...); ``` -[**Example**](examples/limiter.js) ([output](examples/limiter_output.txt)) +**Methods:** + +
+getInfo(params) +
+ +[**Example**](examples/album/getInfo.ts) ([output](examples/album/getInfo_output.txt)) + +

Fetches info about an album.

+ +**Params** + +- `params`: ([AlbumAPIGetInfoParams](docs/api/interfaces/AlbumAPIGetInfoParams.md)) + - `albumUrl`: (string) + - `albumImageFormat`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) (*optional*) + - `artistImageFormat`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) (*optional*) + - `includeRawData`: (boolean) (*optional) + +**Returns** + +Promise resolving to [Album](docs/api/interfaces/Album.md). + +> 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:** + +
+getInfo(params) +
+ +[**Example**](examples/track/getInfo.ts) ([output](examples/track/getInfo_output.txt)) + +

Fetches info about a track.

+ +**Params** + +- `params`: ([TrackAPIGetInfoParams](docs/api/interfaces/Tra)) + - `trackUrl`: (string) + - `albumImageFormat`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) (*optional*) + - `artistImageFormat`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) (*optional*) + - `includeRawData`: (boolean) (*optional) + +**Returns** + +Promise resolving to [Track](docs/api/interfaces/Track.md). + +> 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:** + +
+getInfo(tagUrl) +
+ +[**Example**](examples/tag/getInfo.ts) ([output](examples/tag/getInfo_output.txt)) + +

Fetches info about a tag.

+ +**Params** + +- `tagUrl`: (string) + +**Returns** + +Promise resolving to [Tag](docs/api/interfaces/Tag.md). + +--- +
+ +
+list() +
+ +[**Example**](examples/tag/list.ts) ([output](examples/tag/list_output.txt)) + +

Fetches the full list of tags.

+ +**Returns** + +Promise resolving to [TagList](docs/api/interfaces/TagList.md), which groups results into `tags`(for non-location tags) and `locations` (for location tags). + +--- +
+ +
+getAlbumHighlights(params) +
+ +[**Example**](examples/tag/getAlbumHighlights.ts) ([output](examples/tag/getAlbumHighlights_output.txt)) + +

Fetches album highlights for the tag referred to by params.tagUrl.

+ +Albums are placed in groups. Each group corresponds to a highlight category such as 'new and notable' and 'all-time best selling'. + +**Params** + +- `params`: ([TagAPIGetAlbumHighlightsParams](docs/api/interfaces/TagAPIGetAlbumHighlightsParams.md)) + - `tagUrl`: (string) + - `imageFormat`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) (*optional*) + +**Returns** + +Promise resolving to Array<[AlbumHighlightsByTag](docs/api/interfaces/AlbumHighlightsByTag.md)>. + +--- +
+ +
+getReleases(params) +
+ +[**Example**](examples/tag/getReleases.ts) ([output](examples/tag/getReleases_output.txt)) + +

Fetches releases matching the tag referred to by params.tagUrl.

+ +**Params** + +- `params`: ([TagAPIGetReleasesParams](docs/api/interfaces/TagAPIGetReleasesParams.md)) + - `tagUrl`: (string) + - `imageFormat`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) (*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}) (*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](#autocomplete-api). + +**Returns** + +Promise resolving to [ReleasesByTag](docs/api/interfaces/ReleasesByTag-1.md). + +--- +
+ +
+getReleasesAvailableFilters(tagUrl) +
+ +[**Example**](examples/tag/getReleasesAvailableFilters.ts) ([output](examples/tag/getReleasesAvailableFilters_output.txt)) + +

Fetches the list of possible filter values for getReleases().

+ +For `location` and `tag` filters, this method does not return an exhaustive list of values. You may use `getSuggestions()` of the [Autocomplete API](#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](docs/api/interfaces/ReleasesByTag.Filter.md)>. + +--- +
+ +## Show API + +To access the Show API: + +``` +import bcfetch from 'bandcamp-fetch'; + +const show = bcfetch.show; + +const list = await show.list(...); +``` + +**Methods:** + +
+list(params) +
+ +[**Example**](examples/show/list.ts) ([output](examples/show/list_output.txt)) + +

Fetches the full list of Bandcamp shows.

+ +Each list entry contains basic info about a show. To obtain full details, pass its `url` to `getShow()`. + +**Params** + +- `params`: ([ShowAPIListParams](docs/api/interfaces/ShowAPIListParams.md)) (*optional*) + - `imageFormat`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) (*optional*) + +**Returns** + +Promise resolving to Array<[Show](docs/api/interfaces/Show.md)>. + +--- +
+ +
+getShow(params) +
+ +[**Example**](examples/show/getShow.ts) ([output](examples/show/getShow_output.txt)) + +

Fetches full details about the Bandcamp show referred to by params.showUrl.

+ +**Params** + +- `params`: ([ShowAPIGetShowParams](docs/api/interfaces/ShowAPIGetShowParams.md)) + - `showUrl`: (string) + - `albumImageFormat`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) (*optional*) + - `artistImageFormat`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) (*optional*) + - `showImageFormat`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) (*optional*) + +**Returns** + +Promise resolving to [Show](docs/api/interfaces/Show.md). + +--- +
+ +## Article API + +To access the Article API: + +``` +import bcfetch from 'bandcamp-fetch'; + +const article = bcfetch.article; + +const list = await article.list(...); +``` + +**Methods:** + +
+getCategories() +
+ +[**Example**](examples/article/getCategories.ts) ([output](examples/article/getCategories_output.txt)) + +

Fetches the list of Bandcamp Daily article categories. Categories are grouped into sections.

+ +**Returns** + +Promise resolving to Array<[ArticleCategorySection](docs/api/interfaces/ArticleCategorySection.md)>. + +--- +
+ +
+list(params) +
+ +[**Example**](examples/article/list.ts) ([output](examples/article/list_output.txt)) + +

Fetches the list of Bandcamp Daily articles under the category specified by params.categoryUrl (or all categories if not specified).

+ +**Params** + +- `params`: ([ArticleAPIListParams](docs/api/interfaces/ArticleAPIListParams.md)) (*optional* and *all properties optional*) + - `categoryUrl`: (string) + - `imageFormat`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) + - `page`: (number) + +**Returns** + +Promise resolving to [ArticleList](docs/api/interfaces/ArticleList.md). + +--- +
+ +
+getArticle(params) +
+ +[**Example**](examples/article/getArticle.ts) ([output](examples/article/getArticle_output.txt)) + +

Fetches the contents of the Bandcamp Daily article at params.articleUrl.

+ +**Params** + +- `params`: ([ArticleAPIGetArticleParams](docs/api/interfaces/ArticleAPIGetArticleParams.md)) + - `articleUrl`: (string) + - `albumImageFormat`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) (*optional*) + - `artistImageFormat`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) (*optional*) + - `includeRawData`: (boolean) (*optional*) + +**Returns** + +Promise resolving to [Article](docs/api/interfaces/Article.md). + +--- +
+ +## 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:** + +
+getInfo(params) +
+ +[**Example**](examples/fan/getInfo.ts) ([output](examples/fan/getInfo_output.txt)) + +

Fetches info about a fan.

+ +**Params** + +- `params`: ([FanAPIGetInfoParams](docs/api/interfaces/FanAPIGetInfoParams.md)) + - `username`: (string) + - `imageFormat`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) (*optional*) + +**Returns** + +Promise resolving to [Fan](docs/api/interfaces/Fan.md). + +--- +
+ +
+getCollection(params) +
+ +[**Example**](examples/fan/getCollection.ts) ([output](examples/fan/getCollection_output.txt)) + +

Fetches the list of albums and tracks in a fan's collection.

+ +**Params** + +- `params`: ([FanAPIGetItemsParams](docs/api/interfaces/FanAPIGetItemsParams.md)) + - `target`: (string | [FanItemsContinuation](docs/api/interfaces/FanItemsContinuation.md)) 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](docs/api/interfaces/ImageFormat.md)) (*optional*) + +**Returns** + +Promise resolving to ([FanPageItemsResult](docs/api/interfaces/FanPageItemsResult.md) | [FanContinuationItemsResult](docs/api/interfaces/FanContinuationItemsResult.md))<[Album](docs/api/interfaces/Album.md) | [Track](docs/api/interfaces/Track.md)>. + +--- +
+ +
+getWishlist(params) +
+ +[**Example**](examples/fan/getWishlist.ts) ([output](examples/fan/getWishlist_output.txt)) + +

Fetches the list of albums and tracks added to a fan's wishlist.

+ +**Params** + +- `params`: ([FanAPIGetItemsParams](docs/api/interfaces/FanAPIGetItemsParams.md)) + - `target`: (string | [FanItemsContinuation](docs/api/interfaces/FanItemsContinuation.md)) 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](docs/api/interfaces/ImageFormat.md)) (*optional*) + +**Returns** + +Promise resolving to ([FanPageItemsResult](docs/api/interfaces/FanPageItemsResult.md) | [FanContinuationItemsResult](docs/api/interfaces/FanContinuationItemsResult.md))<[Album](docs/api/interfaces/Album.md) | [Track](docs/api/interfaces/Track.md)>. + +--- +
+ +
+getFollowingArtistsAndLabels(params) +
+ +[**Example**](examples/fan/getFollowingArtistsAndLabels.ts) ([output](examples/fan/getFollowingArtistsAndLabels_output.txt)) + +

Fetches the list of artists and labels followed by a fan.

+ +**Params** + +- `params`: ([FanAPIGetItemsParams](docs/api/interfaces/FanAPIGetItemsParams.md)) + - `target`: (string | [FanItemsContinuation](docs/api/interfaces/FanItemsContinuation.md)) 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](docs/api/interfaces/ImageFormat.md)) (*optional*) + +**Returns** + +Promise resolving to ([FanPageItemsResult](docs/api/interfaces/FanPageItemsResult.md) | [FanContinuationItemsResult](docs/api/interfaces/FanContinuationItemsResult.md))<[UserKind](docs/api/interfaces/UserKind.md)>. + +--- +
+ +
+getFollowingGenres(params) +
+ +[**Example**](examples/fan/getFollowingGenres.ts) ([output](examples/fan/getFollowingGenres_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` to `getReleases()` of the [Tag API](#tag-api). + +**Params** + +- `params`: ([FanAPIGetItemsParams](docs/api/interfaces/FanAPIGetItemsParams.md)) + - `target`: (string | [FanItemsContinuation](docs/api/interfaces/FanItemsContinuation.md)) 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](docs/api/interfaces/ImageFormat.md)) (*optional*) + +**Returns** + +Promise resolving to ([FanPageItemsResult](docs/api/interfaces/FanPageItemsResult.md) | [FanContinuationItemsResult](docs/api/interfaces/FanContinuationItemsResult.md))<[Tag](docs/api/interfaces/Tag.md)>. + +--- +
+ +## 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:** + +
+all(params) / artistsAndLabels(params) / albums(params) / tracks(params) / fans(params) +
+ +[**Example**](examples/search/search.ts) ([output](examples/search/search_output.txt)) + +- `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** + +- `params`: ([SearchAPISearchParams](docs/api/interfaces/SearchAPISearchParams.md)) + - `query`: (string) + - `page`: (number) (*optional*) 1 if omitted + - `albumImageFormat`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) (*optional*) + - `artistImageFormat`: (string | number | [ImageFormat](docs/api/interfaces/ImageFormat.md)) (*optional*) + +**Returns** + +Promise resolving to [SearchResults](docs/api/interfaces/SearchResults.md)<`T`>, where `T` depends on the item type being searched and can be one of: +- Artist ([SearchResultArtist](docs/api/interfaces/SearchResultArtist.md)) +- Label ([SearchResultLabel](docs/api/interfaces/SearchResultLabel.md)) +- Album ([SearchResultAlbum](docs/api/interfaces/SearchResultAlbum.md)) +- Track ([SearchResultTrack](docs/api/interfaces/SearchResultTrack.md)) +- Fan ([SearchResultFan](docs/api/interfaces/SearchResultFan.md)) + +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:** + +
+getSuggestions(params) +
+ +[**Example**](examples/autocomplete/getSuggestions.ts) ([output](examples/autocomplete/getSuggestions_output.txt)) + +

Fetches autocomplete suggestions for tags and locations, based on partial and full matches against `params.query`.

+ +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](#tag-api). + +**Params** + +- `params`: ([AutocompleteAPIGetSuggestionsParams](docs/api/interfaces/AutocompleteAPIGetSuggestionsParams.md)) + - `query`: (string) + - `itemType`: ([AutocompleteItemType](docs/api/enums/AutocompleteItemType.md)) 'Tag' or 'Location' + - limit: (number) (*optional*) the maximum number of results to return; 5 if omitted. + +**Returns** + +- If `params.itemType` is `AutocompleteItemType.Tag`, a Promise resolving to Array<[AutocompleteTag](docs/api/interfaces/AutoCompleteTag.md)>. +- If `params.itemType` is `AutocompleteItemType.Location`, a Promise resolving to Array<[AutocompleteLocation](docs/api/interfaces/AutocompleteLocation.md)>. + +--- +
+ +# Rate Limiting + +`bandcamp-fetch` 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**](examples/limiter/limiter.ts) ([output](examples/limiter/limiter_output.txt)) + The library uses [Bottleneck](https://www.npmjs.com/package/bottleneck) for rate limiting. You can configure the rate limiter like this: ``` @@ -359,42 +834,74 @@ bcfetch.limiter.updateSettings({ `updateSettings()` is just a passthrough function to Bottleneck. Check the [Bottleneck doc](https://www.npmjs.com/package/bottleneck#docs) for the list of options you can set. -## Caching +# Cache -The library maintains an in-memory cache for two types of resources: -1. `page` - pages fetched during scraping -2. `constant` - image formats and discover options +The library maintains an in-memory cache for two types of data (as defined by [CacheDataType](docs/api/enums/CacheDataType.md)): -Functions related to the cache can be called this way: +1. `CacheDataType.Page` - pages fetched during scraping +2. `CacheDataType.Constants` - image formats and discover options + +To access the cache: ``` -const bcfetch = require('bandcamp-fetch'); +import bcfetch, { CacheDataType } from 'bandcamp-fetch'; -bcfetch.cache.setTTL('page', 500); -bcfetch.cache.setMaxPages(20); -bcfetch.cache.clear('constant'); +const cache = bcfetch.cache; +cache.setTTL(CacheDataType.Page, 500); ``` -### `cache.setTTL(type, TTL)` +**Methods:** -Sets the expiry time, in seconds, of cache entries for the given resource type. +
+setTTL(type, ttl) +
-- `type`: 'page' or 'constant' -- `TTL`: expiry time in seconds (default: `300` for 'page' and `3600` for 'constant') +

Sets the expiry time, in seconds, of cache entries for the given data type.

-### `cache.setMaxPages(maxPages)` +**Params** -Sets the maximum number of pages that can be stored in the cache. A negative value means unlimited. Default: `10`. +- `type`: ([CacheDataType](docs/api/enums/CacheDataType.md)) +- `TTL`: (number) expiry time in seconds (default: 300 for `CacheDataType.Page` and 3600 for `CacheDataType.Constants`) -### `cache.clear([type])` +--- +
-Clears the cache entries for the given resource type. +
+setMaxPages(maxPages) +
+ +

Sets the maximum number of pages that can be stored in the cache. A negative value means unlimited. Default: 10.

+ +**Params** + +- `maxPages`: (number) + +--- +
+ +
+clear([type]) +
+ +

Clears the cache entries for the specified data type (or all entries if no data type specified).

+ +**Params** + +- `type`: ([CacheDataType](docs/api/enums/CacheDataType.md)) (*optional*) + +--- +
-- `type` (optional): 'page' or 'constant'. If unspecified, clears the entire cache. # Changelog +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 diff --git a/docs/api/.nojekyll b/docs/api/.nojekyll new file mode 100644 index 0000000..e2ac661 --- /dev/null +++ b/docs/api/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/api/README.md b/docs/api/README.md new file mode 100644 index 0000000..0ed6e72 --- /dev/null +++ b/docs/api/README.md @@ -0,0 +1,178 @@ +bandcamp-fetch + +# bandcamp-fetch + +## Table of contents + +### Namespaces + +- [ReleasesByTag](modules/ReleasesByTag.md) + +### Enumerations + +- [AutocompleteItemType](enums/AutocompleteItemType.md) +- [CacheDataType](enums/CacheDataType.md) +- [ImageFormatFilter](enums/ImageFormatFilter.md) +- [SearchItemType](enums/SearchItemType.md) + +### Classes + +- [AlbumAPI](classes/AlbumAPI.md) +- [ArticleAPI](classes/ArticleAPI.md) +- [AutocompleteAPI](classes/AutocompleteAPI.md) +- [BandAPI](classes/BandAPI.md) +- [Cache](classes/Cache.md) +- [DiscoveryAPI](classes/DiscoveryAPI.md) +- [FanAPI](classes/FanAPI.md) +- [ImageAPI](classes/ImageAPI.md) +- [LimiterAlbumAPI](classes/LimiterAlbumAPI.md) +- [LimiterArticleAPI](classes/LimiterArticleAPI.md) +- [LimiterAutocompleteAPI](classes/LimiterAutocompleteAPI.md) +- [LimiterBandAPI](classes/LimiterBandAPI.md) +- [LimiterDiscoveryAPI](classes/LimiterDiscoveryAPI.md) +- [LimiterFanAPI](classes/LimiterFanAPI.md) +- [LimiterImageAPI](classes/LimiterImageAPI.md) +- [LimiterSearchAPI](classes/LimiterSearchAPI.md) +- [LimiterShowAPI](classes/LimiterShowAPI.md) +- [LimiterTagAPI](classes/LimiterTagAPI.md) +- [LimiterTrackAPI](classes/LimiterTrackAPI.md) +- [SearchAPI](classes/SearchAPI.md) +- [ShowAPI](classes/ShowAPI.md) +- [TagAPI](classes/TagAPI.md) +- [TrackAPI](classes/TrackAPI.md) + +### Interfaces + +- [Album](interfaces/Album.md) +- [AlbumAPIGetInfoParams](interfaces/AlbumAPIGetInfoParams.md) +- [AlbumHighlightsByTag](interfaces/AlbumHighlightsByTag.md) +- [AlbumRelease](interfaces/AlbumRelease.md) +- [Article](interfaces/Article.md) +- [ArticleAPIGetArticleParams](interfaces/ArticleAPIGetArticleParams.md) +- [ArticleAPIListParams](interfaces/ArticleAPIListParams.md) +- [ArticleCategory](interfaces/ArticleCategory.md) +- [ArticleCategorySection](interfaces/ArticleCategorySection.md) +- [ArticleList](interfaces/ArticleList.md) +- [ArticleListItem](interfaces/ArticleListItem.md) +- [ArticleSection](interfaces/ArticleSection.md) +- [Artist](interfaces/Artist.md) +- [AutoCompleteTag](interfaces/AutoCompleteTag.md) +- [AutocompleteAPIGetSuggestionsParams](interfaces/AutocompleteAPIGetSuggestionsParams.md) +- [AutocompleteItem](interfaces/AutocompleteItem.md) +- [AutocompleteLocation](interfaces/AutocompleteLocation.md) +- [BandAPIGetDiscographyParams](interfaces/BandAPIGetDiscographyParams.md) +- [BandAPIGetInfoParams](interfaces/BandAPIGetInfoParams.md) +- [BandAPIGetLabelArtistsParams](interfaces/BandAPIGetLabelArtistsParams.md) +- [DiscoverOptions](interfaces/DiscoverOptions.md) +- [DiscoverParams](interfaces/DiscoverParams.md) +- [DiscoverResult](interfaces/DiscoverResult.md) +- [Fan](interfaces/Fan.md) +- [FanAPIGetInfoParams](interfaces/FanAPIGetInfoParams.md) +- [FanAPIGetItemsParams](interfaces/FanAPIGetItemsParams.md) +- [FanContinuationItemsResult](interfaces/FanContinuationItemsResult.md) +- [FanItemsContinuation](interfaces/FanItemsContinuation.md) +- [FanPageItemsResult](interfaces/FanPageItemsResult.md) +- [ImageConstants](interfaces/ImageConstants.md) +- [ImageFormat](interfaces/ImageFormat.md) +- [Label](interfaces/Label.md) +- [MediaKind](interfaces/MediaKind.md) +- [NameValuePair](interfaces/NameValuePair.md) +- [ReleasesByTag](interfaces/ReleasesByTag-1.md) +- [SearchAPISearchParams](interfaces/SearchAPISearchParams.md) +- [SearchResultAlbum](interfaces/SearchResultAlbum.md) +- [SearchResultArtist](interfaces/SearchResultArtist.md) +- [SearchResultFan](interfaces/SearchResultFan.md) +- [SearchResultItem](interfaces/SearchResultItem.md) +- [SearchResultLabel](interfaces/SearchResultLabel.md) +- [SearchResultTrack](interfaces/SearchResultTrack.md) +- [SearchResults](interfaces/SearchResults.md) +- [Show](interfaces/Show.md) +- [ShowAPIGetShowParams](interfaces/ShowAPIGetShowParams.md) +- [ShowAPIListParams](interfaces/ShowAPIListParams.md) +- [Tag](interfaces/Tag.md) +- [TagAPIGetAlbumHighlightsParams](interfaces/TagAPIGetAlbumHighlightsParams.md) +- [TagAPIGetReleasesParams](interfaces/TagAPIGetReleasesParams.md) +- [TagList](interfaces/TagList.md) +- [Track](interfaces/Track.md) +- [TrackAPIGetInfoParams](interfaces/TrackAPIGetInfoParams.md) +- [UserKind](interfaces/UserKind.md) + +### Type Aliases + +- [ArticleMediaItem](README.md#articlemediaitem) +- [LabelArtist](README.md#labelartist) +- [SearchResultAny](README.md#searchresultany) + +### Variables + +- [default](README.md#default) + +## Type Aliases + +### ArticleMediaItem + +Ƭ **ArticleMediaItem**: [`Album`](interfaces/Album.md) \| [`Track`](interfaces/Track.md) & { `featuredTrackPosition`: `number` ; `mediaItemRef?`: `string` } + +#### Defined in + +lib/types/Article.ts:51 + +___ + +### LabelArtist + +Ƭ **LabelArtist**: `Omit`<[`Artist`](interfaces/Artist.md), ``"type"``\> + +#### Defined in + +lib/types/Label.ts:9 + +___ + +### SearchResultAny + +Ƭ **SearchResultAny**: [`SearchResultArtist`](interfaces/SearchResultArtist.md) \| [`SearchResultLabel`](interfaces/SearchResultLabel.md) \| [`SearchResultAlbum`](interfaces/SearchResultAlbum.md) \| [`SearchResultTrack`](interfaces/SearchResultTrack.md) \| [`SearchResultFan`](interfaces/SearchResultFan.md) + +#### Defined in + +lib/types/Search.ts:47 + +## Variables + +### default + +• **default**: `Object` + +#### Type declaration + +| Name | Type | +| :------ | :------ | +| `album` | typeof [`AlbumAPI`](classes/AlbumAPI.md) | +| `article` | typeof [`ArticleAPI`](classes/ArticleAPI.md) | +| `autocomplete` | typeof [`AutocompleteAPI`](classes/AutocompleteAPI.md) | +| `band` | typeof [`BandAPI`](classes/BandAPI.md) | +| `cache` | typeof [`Cache`](classes/Cache.md) | +| `discovery` | typeof [`DiscoveryAPI`](classes/DiscoveryAPI.md) | +| `fan` | typeof [`FanAPI`](classes/FanAPI.md) | +| `image` | typeof [`ImageAPI`](classes/ImageAPI.md) | +| `limiter` | { `album`: typeof [`LimiterAlbumAPI`](classes/LimiterAlbumAPI.md) = LimiterAlbumAPI; `article`: typeof [`LimiterArticleAPI`](classes/LimiterArticleAPI.md) = LimiterArticleAPI; `autocomplete`: typeof [`LimiterAutocompleteAPI`](classes/LimiterAutocompleteAPI.md) = LimiterAutocompleteAPI; `band`: typeof [`LimiterBandAPI`](classes/LimiterBandAPI.md) = LimiterBandAPI; `discovery`: typeof [`LimiterDiscoveryAPI`](classes/LimiterDiscoveryAPI.md) = LimiterDiscoveryAPI; `fan`: typeof [`LimiterFanAPI`](classes/LimiterFanAPI.md) = LimiterFanAPI; `image`: typeof [`LimiterImageAPI`](classes/LimiterImageAPI.md) = LimiterImageAPI; `search`: typeof [`LimiterSearchAPI`](classes/LimiterSearchAPI.md) = LimiterSearchAPI; `show`: typeof [`LimiterShowAPI`](classes/LimiterShowAPI.md) = LimiterShowAPI; `tag`: typeof [`LimiterTagAPI`](classes/LimiterTagAPI.md) = LimiterTagAPI; `track`: typeof [`LimiterTrackAPI`](classes/LimiterTrackAPI.md) = LimiterTrackAPI; `updateSettings`: (`options?`: `ConstructorOptions`) => `void` } | +| `limiter.album` | typeof [`LimiterAlbumAPI`](classes/LimiterAlbumAPI.md) | +| `limiter.article` | typeof [`LimiterArticleAPI`](classes/LimiterArticleAPI.md) | +| `limiter.autocomplete` | typeof [`LimiterAutocompleteAPI`](classes/LimiterAutocompleteAPI.md) | +| `limiter.band` | typeof [`LimiterBandAPI`](classes/LimiterBandAPI.md) | +| `limiter.discovery` | typeof [`LimiterDiscoveryAPI`](classes/LimiterDiscoveryAPI.md) | +| `limiter.fan` | typeof [`LimiterFanAPI`](classes/LimiterFanAPI.md) | +| `limiter.image` | typeof [`LimiterImageAPI`](classes/LimiterImageAPI.md) | +| `limiter.search` | typeof [`LimiterSearchAPI`](classes/LimiterSearchAPI.md) | +| `limiter.show` | typeof [`LimiterShowAPI`](classes/LimiterShowAPI.md) | +| `limiter.tag` | typeof [`LimiterTagAPI`](classes/LimiterTagAPI.md) | +| `limiter.track` | typeof [`LimiterTrackAPI`](classes/LimiterTrackAPI.md) | +| `limiter.updateSettings` | (`options?`: `ConstructorOptions`) => `void` | +| `search` | typeof [`SearchAPI`](classes/SearchAPI.md) | +| `show` | typeof [`ShowAPI`](classes/ShowAPI.md) | +| `tag` | typeof [`TagAPI`](classes/TagAPI.md) | +| `track` | typeof [`TrackAPI`](classes/TrackAPI.md) | + +#### Defined in + +index.ts:82 diff --git a/docs/api/classes/AlbumAPI.md b/docs/api/classes/AlbumAPI.md new file mode 100644 index 0000000..545d232 --- /dev/null +++ b/docs/api/classes/AlbumAPI.md @@ -0,0 +1,45 @@ +[bandcamp-fetch](../README.md) / AlbumAPI + +# Class: AlbumAPI + +## Hierarchy + +- **`AlbumAPI`** + + ↳ [`LimiterAlbumAPI`](LimiterAlbumAPI.md) + +## Table of contents + +### Constructors + +- [constructor](AlbumAPI.md#constructor) + +### Methods + +- [getInfo](AlbumAPI.md#getinfo) + +## Constructors + +### constructor + +• **new AlbumAPI**() + +## Methods + +### getInfo + +▸ `Static` **getInfo**(`params`): `Promise`<[`Album`](../interfaces/Album.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`AlbumAPIGetInfoParams`](../interfaces/AlbumAPIGetInfoParams.md) | + +#### Returns + +`Promise`<[`Album`](../interfaces/Album.md)\> + +#### Defined in + +lib/album/AlbumAPI.ts:16 diff --git a/docs/api/classes/ArticleAPI.md b/docs/api/classes/ArticleAPI.md new file mode 100644 index 0000000..f5416ae --- /dev/null +++ b/docs/api/classes/ArticleAPI.md @@ -0,0 +1,81 @@ +[bandcamp-fetch](../README.md) / ArticleAPI + +# Class: ArticleAPI + +## Hierarchy + +- **`ArticleAPI`** + + ↳ [`LimiterArticleAPI`](LimiterArticleAPI.md) + +## Table of contents + +### Constructors + +- [constructor](ArticleAPI.md#constructor) + +### Methods + +- [getArticle](ArticleAPI.md#getarticle) +- [getCategories](ArticleAPI.md#getcategories) +- [list](ArticleAPI.md#list) + +## Constructors + +### constructor + +• **new ArticleAPI**() + +## Methods + +### getArticle + +▸ `Static` **getArticle**(`params`): `Promise`<[`Article`](../interfaces/Article.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`ArticleAPIGetArticleParams`](../interfaces/ArticleAPIGetArticleParams.md) | + +#### Returns + +`Promise`<[`Article`](../interfaces/Article.md)\> + +#### Defined in + +lib/article/ArticleAPI.ts:32 + +___ + +### getCategories + +▸ `Static` **getCategories**(): `Promise`<[`ArticleCategorySection`](../interfaces/ArticleCategorySection.md)[]\> + +#### Returns + +`Promise`<[`ArticleCategorySection`](../interfaces/ArticleCategorySection.md)[]\> + +#### Defined in + +lib/article/ArticleAPI.ts:27 + +___ + +### list + +▸ `Static` **list**(`params?`): `Promise`<[`ArticleList`](../interfaces/ArticleList.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params?` | [`ArticleAPIListParams`](../interfaces/ArticleAPIListParams.md) | + +#### Returns + +`Promise`<[`ArticleList`](../interfaces/ArticleList.md)\> + +#### Defined in + +lib/article/ArticleAPI.ts:44 diff --git a/docs/api/classes/AutocompleteAPI.md b/docs/api/classes/AutocompleteAPI.md new file mode 100644 index 0000000..3c539b4 --- /dev/null +++ b/docs/api/classes/AutocompleteAPI.md @@ -0,0 +1,77 @@ +[bandcamp-fetch](../README.md) / AutocompleteAPI + +# Class: AutocompleteAPI + +## Hierarchy + +- **`AutocompleteAPI`** + + ↳ [`LimiterAutocompleteAPI`](LimiterAutocompleteAPI.md) + +## Table of contents + +### Constructors + +- [constructor](AutocompleteAPI.md#constructor) + +### Methods + +- [getSuggestions](AutocompleteAPI.md#getsuggestions) + +## Constructors + +### constructor + +• **new AutocompleteAPI**() + +## Methods + +### getSuggestions + +▸ `Static` **getSuggestions**(`params`): `Promise`<[`AutocompleteLocation`](../interfaces/AutocompleteLocation.md)[]\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`AutocompleteAPIGetSuggestionsParams`](../interfaces/AutocompleteAPIGetSuggestionsParams.md) & { `itemType`: [`Location`](../enums/AutocompleteItemType.md#location) } | + +#### Returns + +`Promise`<[`AutocompleteLocation`](../interfaces/AutocompleteLocation.md)[]\> + +#### Defined in + +lib/autocomplete/AutocompleteAPI.ts:20 + +▸ `Static` **getSuggestions**(`params`): `Promise`<[`AutoCompleteTag`](../interfaces/AutoCompleteTag.md)[]\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`AutocompleteAPIGetSuggestionsParams`](../interfaces/AutocompleteAPIGetSuggestionsParams.md) & { `itemType`: [`Tag`](../enums/AutocompleteItemType.md#tag) } | + +#### Returns + +`Promise`<[`AutoCompleteTag`](../interfaces/AutoCompleteTag.md)[]\> + +#### Defined in + +lib/autocomplete/AutocompleteAPI.ts:21 + +▸ `Static` **getSuggestions**(`params`): `Promise`<[`AutoCompleteTag`](../interfaces/AutoCompleteTag.md)[] \| [`AutocompleteLocation`](../interfaces/AutocompleteLocation.md)[]\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`AutocompleteAPIGetSuggestionsParams`](../interfaces/AutocompleteAPIGetSuggestionsParams.md) | + +#### Returns + +`Promise`<[`AutoCompleteTag`](../interfaces/AutoCompleteTag.md)[] \| [`AutocompleteLocation`](../interfaces/AutocompleteLocation.md)[]\> + +#### Defined in + +lib/autocomplete/AutocompleteAPI.ts:22 diff --git a/docs/api/classes/BandAPI.md b/docs/api/classes/BandAPI.md new file mode 100644 index 0000000..5887ee1 --- /dev/null +++ b/docs/api/classes/BandAPI.md @@ -0,0 +1,87 @@ +[bandcamp-fetch](../README.md) / BandAPI + +# Class: BandAPI + +## Hierarchy + +- **`BandAPI`** + + ↳ [`LimiterBandAPI`](LimiterBandAPI.md) + +## Table of contents + +### Constructors + +- [constructor](BandAPI.md#constructor) + +### Methods + +- [getDiscography](BandAPI.md#getdiscography) +- [getInfo](BandAPI.md#getinfo) +- [getLabelArtists](BandAPI.md#getlabelartists) + +## Constructors + +### constructor + +• **new BandAPI**() + +## Methods + +### getDiscography + +▸ `Static` **getDiscography**(`params`): `Promise`<([`Track`](../interfaces/Track.md) \| [`Album`](../interfaces/Album.md))[]\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`BandAPIGetDiscographyParams`](../interfaces/BandAPIGetDiscographyParams.md) | + +#### Returns + +`Promise`<([`Track`](../interfaces/Track.md) \| [`Album`](../interfaces/Album.md))[]\> + +#### Defined in + +lib/band/BandAPI.ts:32 + +___ + +### getInfo + +▸ `Static` **getInfo**(`params`): `Promise`<[`Label`](../interfaces/Label.md) \| [`Artist`](../interfaces/Artist.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`BandAPIGetInfoParams`](../interfaces/BandAPIGetInfoParams.md) | + +#### Returns + +`Promise`<[`Label`](../interfaces/Label.md) \| [`Artist`](../interfaces/Artist.md)\> + +#### Defined in + +lib/band/BandAPI.ts:43 + +___ + +### getLabelArtists + +▸ `Static` **getLabelArtists**(`params`): `Promise`<[`LabelArtist`](../README.md#labelartist)[]\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`BandAPIGetLabelArtistsParams`](../interfaces/BandAPIGetLabelArtistsParams.md) | + +#### Returns + +`Promise`<[`LabelArtist`](../README.md#labelartist)[]\> + +#### Defined in + +lib/band/BandAPI.ts:86 diff --git a/docs/api/classes/Cache.md b/docs/api/classes/Cache.md new file mode 100644 index 0000000..79c7dbc --- /dev/null +++ b/docs/api/classes/Cache.md @@ -0,0 +1,82 @@ +[bandcamp-fetch](../README.md) / Cache + +# Class: Cache + +## Table of contents + +### Constructors + +- [constructor](Cache.md#constructor) + +### Methods + +- [clear](Cache.md#clear) +- [setMaxPages](Cache.md#setmaxpages) +- [setTTL](Cache.md#setttl) + +## Constructors + +### constructor + +• **new Cache**() + +## Methods + +### clear + +▸ `Static` **clear**(`type?`): `void` + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `type?` | [`CacheDataType`](../enums/CacheDataType.md) | + +#### Returns + +`void` + +#### Defined in + +index.ts:69 + +___ + +### setMaxPages + +▸ `Static` **setMaxPages**(`maxPages`): `void` + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `maxPages` | `number` | + +#### Returns + +`void` + +#### Defined in + +index.ts:77 + +___ + +### setTTL + +▸ `Static` **setTTL**(`type`, `ttl`): `void` + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `type` | [`CacheDataType`](../enums/CacheDataType.md) | +| `ttl` | `number` | + +#### Returns + +`void` + +#### Defined in + +index.ts:73 diff --git a/docs/api/classes/DiscoveryAPI.md b/docs/api/classes/DiscoveryAPI.md new file mode 100644 index 0000000..711dbcb --- /dev/null +++ b/docs/api/classes/DiscoveryAPI.md @@ -0,0 +1,81 @@ +[bandcamp-fetch](../README.md) / DiscoveryAPI + +# Class: DiscoveryAPI + +## Hierarchy + +- **`DiscoveryAPI`** + + ↳ [`LimiterDiscoveryAPI`](LimiterDiscoveryAPI.md) + +## Table of contents + +### Constructors + +- [constructor](DiscoveryAPI.md#constructor) + +### Methods + +- [discover](DiscoveryAPI.md#discover) +- [getAvailableOptions](DiscoveryAPI.md#getavailableoptions) +- [sanitizeDiscoverParams](DiscoveryAPI.md#sanitizediscoverparams) + +## Constructors + +### constructor + +• **new DiscoveryAPI**() + +## Methods + +### discover + +▸ `Static` **discover**(`params?`): `Promise`<[`DiscoverResult`](../interfaces/DiscoverResult.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params?` | [`DiscoverParams`](../interfaces/DiscoverParams.md) | + +#### Returns + +`Promise`<[`DiscoverResult`](../interfaces/DiscoverResult.md)\> + +#### Defined in + +lib/discovery/DiscoveryAPI.ts:76 + +___ + +### getAvailableOptions + +▸ `Static` **getAvailableOptions**(): `Promise`<[`DiscoverOptions`](../interfaces/DiscoverOptions.md)\> + +#### Returns + +`Promise`<[`DiscoverOptions`](../interfaces/DiscoverOptions.md)\> + +#### Defined in + +lib/discovery/DiscoveryAPI.ts:24 + +___ + +### sanitizeDiscoverParams + +▸ `Static` **sanitizeDiscoverParams**(`params?`): `Promise`<[`DiscoverParams`](../interfaces/DiscoverParams.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params?` | [`DiscoverParams`](../interfaces/DiscoverParams.md) | + +#### Returns + +`Promise`<[`DiscoverParams`](../interfaces/DiscoverParams.md)\> + +#### Defined in + +lib/discovery/DiscoveryAPI.ts:31 diff --git a/docs/api/classes/FanAPI.md b/docs/api/classes/FanAPI.md new file mode 100644 index 0000000..694d837 --- /dev/null +++ b/docs/api/classes/FanAPI.md @@ -0,0 +1,129 @@ +[bandcamp-fetch](../README.md) / FanAPI + +# Class: FanAPI + +## Hierarchy + +- **`FanAPI`** + + ↳ [`LimiterFanAPI`](LimiterFanAPI.md) + +## Table of contents + +### Constructors + +- [constructor](FanAPI.md#constructor) + +### Methods + +- [getCollection](FanAPI.md#getcollection) +- [getFollowingArtistsAndLabels](FanAPI.md#getfollowingartistsandlabels) +- [getFollowingGenres](FanAPI.md#getfollowinggenres) +- [getInfo](FanAPI.md#getinfo) +- [getWishlist](FanAPI.md#getwishlist) + +## Constructors + +### constructor + +• **new FanAPI**() + +## Methods + +### getCollection + +▸ `Static` **getCollection**(`params`): `Promise`<[`FanPageItemsResult`](../interfaces/FanPageItemsResult.md)<`NonNullable`<``null`` \| [`Track`](../interfaces/Track.md) \| [`Album`](../interfaces/Album.md)\>\> \| [`FanContinuationItemsResult`](../interfaces/FanContinuationItemsResult.md)<`NonNullable`<``null`` \| [`Track`](../interfaces/Track.md) \| [`Album`](../interfaces/Album.md)\>\>\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`FanAPIGetItemsParams`](../interfaces/FanAPIGetItemsParams.md) | + +#### Returns + +`Promise`<[`FanPageItemsResult`](../interfaces/FanPageItemsResult.md)<`NonNullable`<``null`` \| [`Track`](../interfaces/Track.md) \| [`Album`](../interfaces/Album.md)\>\> \| [`FanContinuationItemsResult`](../interfaces/FanContinuationItemsResult.md)<`NonNullable`<``null`` \| [`Track`](../interfaces/Track.md) \| [`Album`](../interfaces/Album.md)\>\>\> + +#### Defined in + +lib/fan/FanAPI.ts:52 + +___ + +### getFollowingArtistsAndLabels + +▸ `Static` **getFollowingArtistsAndLabels**(`params`): `Promise`<[`FanPageItemsResult`](../interfaces/FanPageItemsResult.md)<[`UserKind`](../interfaces/UserKind.md)\> \| [`FanContinuationItemsResult`](../interfaces/FanContinuationItemsResult.md)<[`UserKind`](../interfaces/UserKind.md)\>\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`FanAPIGetItemsParams`](../interfaces/FanAPIGetItemsParams.md) | + +#### Returns + +`Promise`<[`FanPageItemsResult`](../interfaces/FanPageItemsResult.md)<[`UserKind`](../interfaces/UserKind.md)\> \| [`FanContinuationItemsResult`](../interfaces/FanContinuationItemsResult.md)<[`UserKind`](../interfaces/UserKind.md)\>\> + +#### Defined in + +lib/fan/FanAPI.ts:72 + +___ + +### getFollowingGenres + +▸ `Static` **getFollowingGenres**(`params`): `Promise`<[`FanPageItemsResult`](../interfaces/FanPageItemsResult.md)<[`Tag`](../interfaces/Tag.md)\> \| [`FanContinuationItemsResult`](../interfaces/FanContinuationItemsResult.md)<[`Tag`](../interfaces/Tag.md)\>\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`FanAPIGetItemsParams`](../interfaces/FanAPIGetItemsParams.md) | + +#### Returns + +`Promise`<[`FanPageItemsResult`](../interfaces/FanPageItemsResult.md)<[`Tag`](../interfaces/Tag.md)\> \| [`FanContinuationItemsResult`](../interfaces/FanContinuationItemsResult.md)<[`Tag`](../interfaces/Tag.md)\>\> + +#### Defined in + +lib/fan/FanAPI.ts:82 + +___ + +### getInfo + +▸ `Static` **getInfo**(`params`): `Promise`<[`Fan`](../interfaces/Fan.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`FanAPIGetInfoParams`](../interfaces/FanAPIGetInfoParams.md) | + +#### Returns + +`Promise`<[`Fan`](../interfaces/Fan.md)\> + +#### Defined in + +lib/fan/FanAPI.ts:41 + +___ + +### getWishlist + +▸ `Static` **getWishlist**(`params`): `Promise`<[`FanPageItemsResult`](../interfaces/FanPageItemsResult.md)<`NonNullable`<``null`` \| [`Track`](../interfaces/Track.md) \| [`Album`](../interfaces/Album.md)\>\> \| [`FanContinuationItemsResult`](../interfaces/FanContinuationItemsResult.md)<`NonNullable`<``null`` \| [`Track`](../interfaces/Track.md) \| [`Album`](../interfaces/Album.md)\>\>\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`FanAPIGetItemsParams`](../interfaces/FanAPIGetItemsParams.md) | + +#### Returns + +`Promise`<[`FanPageItemsResult`](../interfaces/FanPageItemsResult.md)<`NonNullable`<``null`` \| [`Track`](../interfaces/Track.md) \| [`Album`](../interfaces/Album.md)\>\> \| [`FanContinuationItemsResult`](../interfaces/FanContinuationItemsResult.md)<`NonNullable`<``null`` \| [`Track`](../interfaces/Track.md) \| [`Album`](../interfaces/Album.md)\>\>\> + +#### Defined in + +lib/fan/FanAPI.ts:62 diff --git a/docs/api/classes/ImageAPI.md b/docs/api/classes/ImageAPI.md new file mode 100644 index 0000000..b8439f5 --- /dev/null +++ b/docs/api/classes/ImageAPI.md @@ -0,0 +1,67 @@ +[bandcamp-fetch](../README.md) / ImageAPI + +# Class: ImageAPI + +## Hierarchy + +- **`ImageAPI`** + + ↳ [`LimiterImageAPI`](LimiterImageAPI.md) + +## Table of contents + +### Constructors + +- [constructor](ImageAPI.md#constructor) + +### Methods + +- [getFormat](ImageAPI.md#getformat) +- [getFormats](ImageAPI.md#getformats) + +## Constructors + +### constructor + +• **new ImageAPI**() + +## Methods + +### getFormat + +▸ `Static` **getFormat**(`target?`, `fallbackId?`): `Promise`<``null`` \| [`ImageFormat`](../interfaces/ImageFormat.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `target?` | `string` \| `number` \| [`ImageFormat`](../interfaces/ImageFormat.md) | +| `fallbackId?` | `number` | + +#### Returns + +`Promise`<``null`` \| [`ImageFormat`](../interfaces/ImageFormat.md)\> + +#### Defined in + +lib/image/ImageAPI.ts:27 + +___ + +### getFormats + +▸ `Static` **getFormats**(`filter?`): `Promise`<[`ImageFormat`](../interfaces/ImageFormat.md)[]\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `filter?` | [`ImageFormatFilter`](../enums/ImageFormatFilter.md) | + +#### Returns + +`Promise`<[`ImageFormat`](../interfaces/ImageFormat.md)[]\> + +#### Defined in + +lib/image/ImageAPI.ts:47 diff --git a/docs/api/classes/LimiterAlbumAPI.md b/docs/api/classes/LimiterAlbumAPI.md new file mode 100644 index 0000000..6c366df --- /dev/null +++ b/docs/api/classes/LimiterAlbumAPI.md @@ -0,0 +1,53 @@ +[bandcamp-fetch](../README.md) / LimiterAlbumAPI + +# Class: LimiterAlbumAPI + +## Hierarchy + +- [`AlbumAPI`](AlbumAPI.md) + + ↳ **`LimiterAlbumAPI`** + +## Table of contents + +### Constructors + +- [constructor](LimiterAlbumAPI.md#constructor) + +### Methods + +- [getInfo](LimiterAlbumAPI.md#getinfo) + +## Constructors + +### constructor + +• **new LimiterAlbumAPI**() + +#### Inherited from + +[AlbumAPI](AlbumAPI.md).[constructor](AlbumAPI.md#constructor) + +## Methods + +### getInfo + +▸ `Static` **getInfo**(`params`): `Promise`<[`Album`](../interfaces/Album.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`AlbumAPIGetInfoParams`](../interfaces/AlbumAPIGetInfoParams.md) | + +#### Returns + +`Promise`<[`Album`](../interfaces/Album.md)\> + +#### Overrides + +[AlbumAPI](AlbumAPI.md).[getInfo](AlbumAPI.md#getinfo) + +#### Defined in + +lib/album/AlbumAPI.ts:30 diff --git a/docs/api/classes/LimiterArticleAPI.md b/docs/api/classes/LimiterArticleAPI.md new file mode 100644 index 0000000..cd62181 --- /dev/null +++ b/docs/api/classes/LimiterArticleAPI.md @@ -0,0 +1,97 @@ +[bandcamp-fetch](../README.md) / LimiterArticleAPI + +# Class: LimiterArticleAPI + +## Hierarchy + +- [`ArticleAPI`](ArticleAPI.md) + + ↳ **`LimiterArticleAPI`** + +## Table of contents + +### Constructors + +- [constructor](LimiterArticleAPI.md#constructor) + +### Methods + +- [getArticle](LimiterArticleAPI.md#getarticle) +- [getCategories](LimiterArticleAPI.md#getcategories) +- [list](LimiterArticleAPI.md#list) + +## Constructors + +### constructor + +• **new LimiterArticleAPI**() + +#### Inherited from + +[ArticleAPI](ArticleAPI.md).[constructor](ArticleAPI.md#constructor) + +## Methods + +### getArticle + +▸ `Static` **getArticle**(`params`): `Promise`<[`Article`](../interfaces/Article.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`ArticleAPIGetArticleParams`](../interfaces/ArticleAPIGetArticleParams.md) | + +#### Returns + +`Promise`<[`Article`](../interfaces/Article.md)\> + +#### Overrides + +[ArticleAPI](ArticleAPI.md).[getArticle](ArticleAPI.md#getarticle) + +#### Defined in + +lib/article/ArticleAPI.ts:63 + +___ + +### getCategories + +▸ `Static` **getCategories**(): `Promise`<[`ArticleCategorySection`](../interfaces/ArticleCategorySection.md)[]\> + +#### Returns + +`Promise`<[`ArticleCategorySection`](../interfaces/ArticleCategorySection.md)[]\> + +#### Overrides + +[ArticleAPI](ArticleAPI.md).[getCategories](ArticleAPI.md#getcategories) + +#### Defined in + +lib/article/ArticleAPI.ts:59 + +___ + +### list + +▸ `Static` **list**(`params?`): `Promise`<[`ArticleList`](../interfaces/ArticleList.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params?` | [`ArticleAPIListParams`](../interfaces/ArticleAPIListParams.md) | + +#### Returns + +`Promise`<[`ArticleList`](../interfaces/ArticleList.md)\> + +#### Inherited from + +[ArticleAPI](ArticleAPI.md).[list](ArticleAPI.md#list) + +#### Defined in + +lib/article/ArticleAPI.ts:44 diff --git a/docs/api/classes/LimiterAutocompleteAPI.md b/docs/api/classes/LimiterAutocompleteAPI.md new file mode 100644 index 0000000..aaed63d --- /dev/null +++ b/docs/api/classes/LimiterAutocompleteAPI.md @@ -0,0 +1,93 @@ +[bandcamp-fetch](../README.md) / LimiterAutocompleteAPI + +# Class: LimiterAutocompleteAPI + +## Hierarchy + +- [`AutocompleteAPI`](AutocompleteAPI.md) + + ↳ **`LimiterAutocompleteAPI`** + +## Table of contents + +### Constructors + +- [constructor](LimiterAutocompleteAPI.md#constructor) + +### Methods + +- [getSuggestions](LimiterAutocompleteAPI.md#getsuggestions) + +## Constructors + +### constructor + +• **new LimiterAutocompleteAPI**() + +#### Inherited from + +[AutocompleteAPI](AutocompleteAPI.md).[constructor](AutocompleteAPI.md#constructor) + +## Methods + +### getSuggestions + +▸ `Static` **getSuggestions**(`params`): `Promise`<[`AutocompleteLocation`](../interfaces/AutocompleteLocation.md)[]\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`AutocompleteAPIGetSuggestionsParams`](../interfaces/AutocompleteAPIGetSuggestionsParams.md) & { `itemType`: [`Location`](../enums/AutocompleteItemType.md#location) } | + +#### Returns + +`Promise`<[`AutocompleteLocation`](../interfaces/AutocompleteLocation.md)[]\> + +#### Overrides + +[AutocompleteAPI](AutocompleteAPI.md).[getSuggestions](AutocompleteAPI.md#getsuggestions) + +#### Defined in + +lib/autocomplete/AutocompleteAPI.ts:55 + +▸ `Static` **getSuggestions**(`params`): `Promise`<[`AutoCompleteTag`](../interfaces/AutoCompleteTag.md)[]\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`AutocompleteAPIGetSuggestionsParams`](../interfaces/AutocompleteAPIGetSuggestionsParams.md) & { `itemType`: [`Tag`](../enums/AutocompleteItemType.md#tag) } | + +#### Returns + +`Promise`<[`AutoCompleteTag`](../interfaces/AutoCompleteTag.md)[]\> + +#### Overrides + +[AutocompleteAPI](AutocompleteAPI.md).[getSuggestions](AutocompleteAPI.md#getsuggestions) + +#### Defined in + +lib/autocomplete/AutocompleteAPI.ts:56 + +▸ `Static` **getSuggestions**(`params`): `Promise`<[`AutoCompleteTag`](../interfaces/AutoCompleteTag.md)[] \| [`AutocompleteLocation`](../interfaces/AutocompleteLocation.md)[]\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`AutocompleteAPIGetSuggestionsParams`](../interfaces/AutocompleteAPIGetSuggestionsParams.md) | + +#### Returns + +`Promise`<[`AutoCompleteTag`](../interfaces/AutoCompleteTag.md)[] \| [`AutocompleteLocation`](../interfaces/AutocompleteLocation.md)[]\> + +#### Overrides + +[AutocompleteAPI](AutocompleteAPI.md).[getSuggestions](AutocompleteAPI.md#getsuggestions) + +#### Defined in + +lib/autocomplete/AutocompleteAPI.ts:57 diff --git a/docs/api/classes/LimiterBandAPI.md b/docs/api/classes/LimiterBandAPI.md new file mode 100644 index 0000000..0897a78 --- /dev/null +++ b/docs/api/classes/LimiterBandAPI.md @@ -0,0 +1,103 @@ +[bandcamp-fetch](../README.md) / LimiterBandAPI + +# Class: LimiterBandAPI + +## Hierarchy + +- [`BandAPI`](BandAPI.md) + + ↳ **`LimiterBandAPI`** + +## Table of contents + +### Constructors + +- [constructor](LimiterBandAPI.md#constructor) + +### Methods + +- [getDiscography](LimiterBandAPI.md#getdiscography) +- [getInfo](LimiterBandAPI.md#getinfo) +- [getLabelArtists](LimiterBandAPI.md#getlabelartists) + +## Constructors + +### constructor + +• **new LimiterBandAPI**() + +#### Inherited from + +[BandAPI](BandAPI.md).[constructor](BandAPI.md#constructor) + +## Methods + +### getDiscography + +▸ `Static` **getDiscography**(`params`): `Promise`<([`Track`](../interfaces/Track.md) \| [`Album`](../interfaces/Album.md))[]\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`BandAPIGetDiscographyParams`](../interfaces/BandAPIGetDiscographyParams.md) | + +#### Returns + +`Promise`<([`Track`](../interfaces/Track.md) \| [`Album`](../interfaces/Album.md))[]\> + +#### Overrides + +[BandAPI](BandAPI.md).[getDiscography](BandAPI.md#getdiscography) + +#### Defined in + +lib/band/BandAPI.ts:126 + +___ + +### getInfo + +▸ `Static` **getInfo**(`params`): `Promise`<[`Label`](../interfaces/Label.md) \| [`Artist`](../interfaces/Artist.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`BandAPIGetInfoParams`](../interfaces/BandAPIGetInfoParams.md) | + +#### Returns + +`Promise`<[`Label`](../interfaces/Label.md) \| [`Artist`](../interfaces/Artist.md)\> + +#### Overrides + +[BandAPI](BandAPI.md).[getInfo](BandAPI.md#getinfo) + +#### Defined in + +lib/band/BandAPI.ts:130 + +___ + +### getLabelArtists + +▸ `Static` **getLabelArtists**(`params`): `Promise`<[`LabelArtist`](../README.md#labelartist)[]\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`BandAPIGetLabelArtistsParams`](../interfaces/BandAPIGetLabelArtistsParams.md) | + +#### Returns + +`Promise`<[`LabelArtist`](../README.md#labelartist)[]\> + +#### Overrides + +[BandAPI](BandAPI.md).[getLabelArtists](BandAPI.md#getlabelartists) + +#### Defined in + +lib/band/BandAPI.ts:134 diff --git a/docs/api/classes/LimiterDiscoveryAPI.md b/docs/api/classes/LimiterDiscoveryAPI.md new file mode 100644 index 0000000..11e3d41 --- /dev/null +++ b/docs/api/classes/LimiterDiscoveryAPI.md @@ -0,0 +1,97 @@ +[bandcamp-fetch](../README.md) / LimiterDiscoveryAPI + +# Class: LimiterDiscoveryAPI + +## Hierarchy + +- [`DiscoveryAPI`](DiscoveryAPI.md) + + ↳ **`LimiterDiscoveryAPI`** + +## Table of contents + +### Constructors + +- [constructor](LimiterDiscoveryAPI.md#constructor) + +### Methods + +- [discover](LimiterDiscoveryAPI.md#discover) +- [getAvailableOptions](LimiterDiscoveryAPI.md#getavailableoptions) +- [sanitizeDiscoverParams](LimiterDiscoveryAPI.md#sanitizediscoverparams) + +## Constructors + +### constructor + +• **new LimiterDiscoveryAPI**() + +#### Inherited from + +[DiscoveryAPI](DiscoveryAPI.md).[constructor](DiscoveryAPI.md#constructor) + +## Methods + +### discover + +▸ `Static` **discover**(`params`): `Promise`<[`DiscoverResult`](../interfaces/DiscoverResult.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`DiscoverParams`](../interfaces/DiscoverParams.md) | + +#### Returns + +`Promise`<[`DiscoverResult`](../interfaces/DiscoverResult.md)\> + +#### Overrides + +[DiscoveryAPI](DiscoveryAPI.md).[discover](DiscoveryAPI.md#discover) + +#### Defined in + +lib/discovery/DiscoveryAPI.ts:139 + +___ + +### getAvailableOptions + +▸ `Static` **getAvailableOptions**(): `Promise`<[`DiscoverOptions`](../interfaces/DiscoverOptions.md)\> + +#### Returns + +`Promise`<[`DiscoverOptions`](../interfaces/DiscoverOptions.md)\> + +#### Overrides + +[DiscoveryAPI](DiscoveryAPI.md).[getAvailableOptions](DiscoveryAPI.md#getavailableoptions) + +#### Defined in + +lib/discovery/DiscoveryAPI.ts:131 + +___ + +### sanitizeDiscoverParams + +▸ `Static` **sanitizeDiscoverParams**(`params`): `Promise`<[`DiscoverParams`](../interfaces/DiscoverParams.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`DiscoverParams`](../interfaces/DiscoverParams.md) | + +#### Returns + +`Promise`<[`DiscoverParams`](../interfaces/DiscoverParams.md)\> + +#### Overrides + +[DiscoveryAPI](DiscoveryAPI.md).[sanitizeDiscoverParams](DiscoveryAPI.md#sanitizediscoverparams) + +#### Defined in + +lib/discovery/DiscoveryAPI.ts:135 diff --git a/docs/api/classes/LimiterFanAPI.md b/docs/api/classes/LimiterFanAPI.md new file mode 100644 index 0000000..0866b23 --- /dev/null +++ b/docs/api/classes/LimiterFanAPI.md @@ -0,0 +1,153 @@ +[bandcamp-fetch](../README.md) / LimiterFanAPI + +# Class: LimiterFanAPI + +## Hierarchy + +- [`FanAPI`](FanAPI.md) + + ↳ **`LimiterFanAPI`** + +## Table of contents + +### Constructors + +- [constructor](LimiterFanAPI.md#constructor) + +### Methods + +- [getCollection](LimiterFanAPI.md#getcollection) +- [getFollowingArtistsAndLabels](LimiterFanAPI.md#getfollowingartistsandlabels) +- [getFollowingGenres](LimiterFanAPI.md#getfollowinggenres) +- [getInfo](LimiterFanAPI.md#getinfo) +- [getWishlist](LimiterFanAPI.md#getwishlist) + +## Constructors + +### constructor + +• **new LimiterFanAPI**() + +#### Inherited from + +[FanAPI](FanAPI.md).[constructor](FanAPI.md#constructor) + +## Methods + +### getCollection + +▸ `Static` **getCollection**(`params`): `Promise`<[`FanPageItemsResult`](../interfaces/FanPageItemsResult.md)<`NonNullable`<``null`` \| [`Track`](../interfaces/Track.md) \| [`Album`](../interfaces/Album.md)\>\> \| [`FanContinuationItemsResult`](../interfaces/FanContinuationItemsResult.md)<`NonNullable`<``null`` \| [`Track`](../interfaces/Track.md) \| [`Album`](../interfaces/Album.md)\>\>\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`FanAPIGetItemsParams`](../interfaces/FanAPIGetItemsParams.md) | + +#### Returns + +`Promise`<[`FanPageItemsResult`](../interfaces/FanPageItemsResult.md)<`NonNullable`<``null`` \| [`Track`](../interfaces/Track.md) \| [`Album`](../interfaces/Album.md)\>\> \| [`FanContinuationItemsResult`](../interfaces/FanContinuationItemsResult.md)<`NonNullable`<``null`` \| [`Track`](../interfaces/Track.md) \| [`Album`](../interfaces/Album.md)\>\>\> + +#### Overrides + +[FanAPI](FanAPI.md).[getCollection](FanAPI.md#getcollection) + +#### Defined in + +lib/fan/FanAPI.ts:136 + +___ + +### getFollowingArtistsAndLabels + +▸ `Static` **getFollowingArtistsAndLabels**(`params`): `Promise`<[`FanPageItemsResult`](../interfaces/FanPageItemsResult.md)<[`UserKind`](../interfaces/UserKind.md)\> \| [`FanContinuationItemsResult`](../interfaces/FanContinuationItemsResult.md)<[`UserKind`](../interfaces/UserKind.md)\>\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`FanAPIGetItemsParams`](../interfaces/FanAPIGetItemsParams.md) | + +#### Returns + +`Promise`<[`FanPageItemsResult`](../interfaces/FanPageItemsResult.md)<[`UserKind`](../interfaces/UserKind.md)\> \| [`FanContinuationItemsResult`](../interfaces/FanContinuationItemsResult.md)<[`UserKind`](../interfaces/UserKind.md)\>\> + +#### Overrides + +[FanAPI](FanAPI.md).[getFollowingArtistsAndLabels](FanAPI.md#getfollowingartistsandlabels) + +#### Defined in + +lib/fan/FanAPI.ts:144 + +___ + +### getFollowingGenres + +▸ `Static` **getFollowingGenres**(`params`): `Promise`<[`FanPageItemsResult`](../interfaces/FanPageItemsResult.md)<[`Tag`](../interfaces/Tag.md)\> \| [`FanContinuationItemsResult`](../interfaces/FanContinuationItemsResult.md)<[`Tag`](../interfaces/Tag.md)\>\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`FanAPIGetItemsParams`](../interfaces/FanAPIGetItemsParams.md) | + +#### Returns + +`Promise`<[`FanPageItemsResult`](../interfaces/FanPageItemsResult.md)<[`Tag`](../interfaces/Tag.md)\> \| [`FanContinuationItemsResult`](../interfaces/FanContinuationItemsResult.md)<[`Tag`](../interfaces/Tag.md)\>\> + +#### Overrides + +[FanAPI](FanAPI.md).[getFollowingGenres](FanAPI.md#getfollowinggenres) + +#### Defined in + +lib/fan/FanAPI.ts:148 + +___ + +### getInfo + +▸ `Static` **getInfo**(`params`): `Promise`<[`Fan`](../interfaces/Fan.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`FanAPIGetInfoParams`](../interfaces/FanAPIGetInfoParams.md) | + +#### Returns + +`Promise`<[`Fan`](../interfaces/Fan.md)\> + +#### Overrides + +[FanAPI](FanAPI.md).[getInfo](FanAPI.md#getinfo) + +#### Defined in + +lib/fan/FanAPI.ts:132 + +___ + +### getWishlist + +▸ `Static` **getWishlist**(`params`): `Promise`<[`FanPageItemsResult`](../interfaces/FanPageItemsResult.md)<`NonNullable`<``null`` \| [`Track`](../interfaces/Track.md) \| [`Album`](../interfaces/Album.md)\>\> \| [`FanContinuationItemsResult`](../interfaces/FanContinuationItemsResult.md)<`NonNullable`<``null`` \| [`Track`](../interfaces/Track.md) \| [`Album`](../interfaces/Album.md)\>\>\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`FanAPIGetItemsParams`](../interfaces/FanAPIGetItemsParams.md) | + +#### Returns + +`Promise`<[`FanPageItemsResult`](../interfaces/FanPageItemsResult.md)<`NonNullable`<``null`` \| [`Track`](../interfaces/Track.md) \| [`Album`](../interfaces/Album.md)\>\> \| [`FanContinuationItemsResult`](../interfaces/FanContinuationItemsResult.md)<`NonNullable`<``null`` \| [`Track`](../interfaces/Track.md) \| [`Album`](../interfaces/Album.md)\>\>\> + +#### Overrides + +[FanAPI](FanAPI.md).[getWishlist](FanAPI.md#getwishlist) + +#### Defined in + +lib/fan/FanAPI.ts:140 diff --git a/docs/api/classes/LimiterImageAPI.md b/docs/api/classes/LimiterImageAPI.md new file mode 100644 index 0000000..04c4aed --- /dev/null +++ b/docs/api/classes/LimiterImageAPI.md @@ -0,0 +1,79 @@ +[bandcamp-fetch](../README.md) / LimiterImageAPI + +# Class: LimiterImageAPI + +## Hierarchy + +- [`ImageAPI`](ImageAPI.md) + + ↳ **`LimiterImageAPI`** + +## Table of contents + +### Constructors + +- [constructor](LimiterImageAPI.md#constructor) + +### Methods + +- [getFormat](LimiterImageAPI.md#getformat) +- [getFormats](LimiterImageAPI.md#getformats) + +## Constructors + +### constructor + +• **new LimiterImageAPI**() + +#### Inherited from + +[ImageAPI](ImageAPI.md).[constructor](ImageAPI.md#constructor) + +## Methods + +### getFormat + +▸ `Static` **getFormat**(`target?`, `fallbackId?`): `Promise`<``null`` \| [`ImageFormat`](../interfaces/ImageFormat.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `target?` | `string` \| `number` \| [`ImageFormat`](../interfaces/ImageFormat.md) | +| `fallbackId?` | `number` | + +#### Returns + +`Promise`<``null`` \| [`ImageFormat`](../interfaces/ImageFormat.md)\> + +#### Overrides + +[ImageAPI](ImageAPI.md).[getFormat](ImageAPI.md#getformat) + +#### Defined in + +lib/image/ImageAPI.ts:66 + +___ + +### getFormats + +▸ `Static` **getFormats**(`filter?`): `Promise`<[`ImageFormat`](../interfaces/ImageFormat.md)[]\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `filter?` | [`ImageFormatFilter`](../enums/ImageFormatFilter.md) | + +#### Returns + +`Promise`<[`ImageFormat`](../interfaces/ImageFormat.md)[]\> + +#### Overrides + +[ImageAPI](ImageAPI.md).[getFormats](ImageAPI.md#getformats) + +#### Defined in + +lib/image/ImageAPI.ts:62 diff --git a/docs/api/classes/LimiterSearchAPI.md b/docs/api/classes/LimiterSearchAPI.md new file mode 100644 index 0000000..8493d10 --- /dev/null +++ b/docs/api/classes/LimiterSearchAPI.md @@ -0,0 +1,153 @@ +[bandcamp-fetch](../README.md) / LimiterSearchAPI + +# Class: LimiterSearchAPI + +## Hierarchy + +- [`SearchAPI`](SearchAPI.md) + + ↳ **`LimiterSearchAPI`** + +## Table of contents + +### Constructors + +- [constructor](LimiterSearchAPI.md#constructor) + +### Methods + +- [albums](LimiterSearchAPI.md#albums) +- [all](LimiterSearchAPI.md#all) +- [artistsAndLabels](LimiterSearchAPI.md#artistsandlabels) +- [fans](LimiterSearchAPI.md#fans) +- [tracks](LimiterSearchAPI.md#tracks) + +## Constructors + +### constructor + +• **new LimiterSearchAPI**() + +#### Inherited from + +[SearchAPI](SearchAPI.md).[constructor](SearchAPI.md#constructor) + +## Methods + +### albums + +▸ `Static` **albums**(`params`): `Promise`<[`SearchResults`](../interfaces/SearchResults.md)<[`SearchResultAlbum`](../interfaces/SearchResultAlbum.md)\>\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`SearchAPISearchParams`](../interfaces/SearchAPISearchParams.md) | + +#### Returns + +`Promise`<[`SearchResults`](../interfaces/SearchResults.md)<[`SearchResultAlbum`](../interfaces/SearchResultAlbum.md)\>\> + +#### Overrides + +[SearchAPI](SearchAPI.md).[albums](SearchAPI.md#albums) + +#### Defined in + +lib/search/SearchAPI.ts:95 + +___ + +### all + +▸ `Static` **all**(`params`): `Promise`<[`SearchResults`](../interfaces/SearchResults.md)<[`SearchResultAny`](../README.md#searchresultany)\>\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`SearchAPISearchParams`](../interfaces/SearchAPISearchParams.md) | + +#### Returns + +`Promise`<[`SearchResults`](../interfaces/SearchResults.md)<[`SearchResultAny`](../README.md#searchresultany)\>\> + +#### Overrides + +[SearchAPI](SearchAPI.md).[all](SearchAPI.md#all) + +#### Defined in + +lib/search/SearchAPI.ts:87 + +___ + +### artistsAndLabels + +▸ `Static` **artistsAndLabels**(`params`): `Promise`<[`SearchResults`](../interfaces/SearchResults.md)<[`SearchResultArtist`](../interfaces/SearchResultArtist.md) \| [`SearchResultLabel`](../interfaces/SearchResultLabel.md)\>\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`SearchAPISearchParams`](../interfaces/SearchAPISearchParams.md) | + +#### Returns + +`Promise`<[`SearchResults`](../interfaces/SearchResults.md)<[`SearchResultArtist`](../interfaces/SearchResultArtist.md) \| [`SearchResultLabel`](../interfaces/SearchResultLabel.md)\>\> + +#### Overrides + +[SearchAPI](SearchAPI.md).[artistsAndLabels](SearchAPI.md#artistsandlabels) + +#### Defined in + +lib/search/SearchAPI.ts:91 + +___ + +### fans + +▸ `Static` **fans**(`params`): `Promise`<[`SearchResults`](../interfaces/SearchResults.md)<[`SearchResultFan`](../interfaces/SearchResultFan.md)\>\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`SearchAPISearchParams`](../interfaces/SearchAPISearchParams.md) | + +#### Returns + +`Promise`<[`SearchResults`](../interfaces/SearchResults.md)<[`SearchResultFan`](../interfaces/SearchResultFan.md)\>\> + +#### Overrides + +[SearchAPI](SearchAPI.md).[fans](SearchAPI.md#fans) + +#### Defined in + +lib/search/SearchAPI.ts:103 + +___ + +### tracks + +▸ `Static` **tracks**(`params`): `Promise`<[`SearchResults`](../interfaces/SearchResults.md)<[`SearchResultTrack`](../interfaces/SearchResultTrack.md)\>\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`SearchAPISearchParams`](../interfaces/SearchAPISearchParams.md) | + +#### Returns + +`Promise`<[`SearchResults`](../interfaces/SearchResults.md)<[`SearchResultTrack`](../interfaces/SearchResultTrack.md)\>\> + +#### Overrides + +[SearchAPI](SearchAPI.md).[tracks](SearchAPI.md#tracks) + +#### Defined in + +lib/search/SearchAPI.ts:99 diff --git a/docs/api/classes/LimiterShowAPI.md b/docs/api/classes/LimiterShowAPI.md new file mode 100644 index 0000000..a702250 --- /dev/null +++ b/docs/api/classes/LimiterShowAPI.md @@ -0,0 +1,78 @@ +[bandcamp-fetch](../README.md) / LimiterShowAPI + +# Class: LimiterShowAPI + +## Hierarchy + +- [`ShowAPI`](ShowAPI.md) + + ↳ **`LimiterShowAPI`** + +## Table of contents + +### Constructors + +- [constructor](LimiterShowAPI.md#constructor) + +### Methods + +- [getShow](LimiterShowAPI.md#getshow) +- [list](LimiterShowAPI.md#list) + +## Constructors + +### constructor + +• **new LimiterShowAPI**() + +#### Inherited from + +[ShowAPI](ShowAPI.md).[constructor](ShowAPI.md#constructor) + +## Methods + +### getShow + +▸ `Static` **getShow**(`params`): `Promise`<[`Show`](../interfaces/Show.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`ShowAPIGetShowParams`](../interfaces/ShowAPIGetShowParams.md) | + +#### Returns + +`Promise`<[`Show`](../interfaces/Show.md)\> + +#### Overrides + +[ShowAPI](ShowAPI.md).[getShow](ShowAPI.md#getshow) + +#### Defined in + +lib/show/ShowAPI.ts:49 + +___ + +### list + +▸ `Static` **list**(`params?`): `Promise`<[`Show`](../interfaces/Show.md)[]\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params?` | [`ShowAPIListParams`](../interfaces/ShowAPIListParams.md) | + +#### Returns + +`Promise`<[`Show`](../interfaces/Show.md)[]\> + +#### Overrides + +[ShowAPI](ShowAPI.md).[list](ShowAPI.md#list) + +#### Defined in + +lib/show/ShowAPI.ts:53 diff --git a/docs/api/classes/LimiterTagAPI.md b/docs/api/classes/LimiterTagAPI.md new file mode 100644 index 0000000..ddc3b8a --- /dev/null +++ b/docs/api/classes/LimiterTagAPI.md @@ -0,0 +1,147 @@ +[bandcamp-fetch](../README.md) / LimiterTagAPI + +# Class: LimiterTagAPI + +## Hierarchy + +- [`TagAPI`](TagAPI.md) + + ↳ **`LimiterTagAPI`** + +## Table of contents + +### Constructors + +- [constructor](LimiterTagAPI.md#constructor) + +### Methods + +- [getAlbumHighlights](LimiterTagAPI.md#getalbumhighlights) +- [getInfo](LimiterTagAPI.md#getinfo) +- [getReleases](LimiterTagAPI.md#getreleases) +- [getReleasesAvailableFilters](LimiterTagAPI.md#getreleasesavailablefilters) +- [list](LimiterTagAPI.md#list) + +## Constructors + +### constructor + +• **new LimiterTagAPI**() + +#### Inherited from + +[TagAPI](TagAPI.md).[constructor](TagAPI.md#constructor) + +## Methods + +### getAlbumHighlights + +▸ `Static` **getAlbumHighlights**(`params`): `Promise`<[`AlbumHighlightsByTag`](../interfaces/AlbumHighlightsByTag.md)[]\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`TagAPIGetAlbumHighlightsParams`](../interfaces/TagAPIGetAlbumHighlightsParams.md) | + +#### Returns + +`Promise`<[`AlbumHighlightsByTag`](../interfaces/AlbumHighlightsByTag.md)[]\> + +#### Overrides + +[TagAPI](TagAPI.md).[getAlbumHighlights](TagAPI.md#getalbumhighlights) + +#### Defined in + +lib/tag/TagAPI.ts:151 + +___ + +### getInfo + +▸ `Static` **getInfo**(`tagUrl`): `Promise`<[`Tag`](../interfaces/Tag.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `tagUrl` | `string` | + +#### Returns + +`Promise`<[`Tag`](../interfaces/Tag.md)\> + +#### Overrides + +[TagAPI](TagAPI.md).[getInfo](TagAPI.md#getinfo) + +#### Defined in + +lib/tag/TagAPI.ts:147 + +___ + +### getReleases + +▸ `Static` **getReleases**(`params`): `Promise`<[`ReleasesByTag`](../interfaces/ReleasesByTag-1.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`TagAPIGetReleasesParams`](../interfaces/TagAPIGetReleasesParams.md) | + +#### Returns + +`Promise`<[`ReleasesByTag`](../interfaces/ReleasesByTag-1.md)\> + +#### Overrides + +[TagAPI](TagAPI.md).[getReleases](TagAPI.md#getreleases) + +#### Defined in + +lib/tag/TagAPI.ts:159 + +___ + +### getReleasesAvailableFilters + +▸ `Static` **getReleasesAvailableFilters**(`tagUrl`): `Promise`<[`Filter`](../interfaces/ReleasesByTag.Filter.md)[]\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `tagUrl` | `string` | + +#### Returns + +`Promise`<[`Filter`](../interfaces/ReleasesByTag.Filter.md)[]\> + +#### Overrides + +[TagAPI](TagAPI.md).[getReleasesAvailableFilters](TagAPI.md#getreleasesavailablefilters) + +#### Defined in + +lib/tag/TagAPI.ts:155 + +___ + +### list + +▸ `Static` **list**(): `Promise`<[`TagList`](../interfaces/TagList.md)\> + +#### Returns + +`Promise`<[`TagList`](../interfaces/TagList.md)\> + +#### Overrides + +[TagAPI](TagAPI.md).[list](TagAPI.md#list) + +#### Defined in + +lib/tag/TagAPI.ts:143 diff --git a/docs/api/classes/LimiterTrackAPI.md b/docs/api/classes/LimiterTrackAPI.md new file mode 100644 index 0000000..c84e192 --- /dev/null +++ b/docs/api/classes/LimiterTrackAPI.md @@ -0,0 +1,53 @@ +[bandcamp-fetch](../README.md) / LimiterTrackAPI + +# Class: LimiterTrackAPI + +## Hierarchy + +- [`TrackAPI`](TrackAPI.md) + + ↳ **`LimiterTrackAPI`** + +## Table of contents + +### Constructors + +- [constructor](LimiterTrackAPI.md#constructor) + +### Methods + +- [getInfo](LimiterTrackAPI.md#getinfo) + +## Constructors + +### constructor + +• **new LimiterTrackAPI**() + +#### Inherited from + +[TrackAPI](TrackAPI.md).[constructor](TrackAPI.md#constructor) + +## Methods + +### getInfo + +▸ `Static` **getInfo**(`params`): `Promise`<[`Track`](../interfaces/Track.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`TrackAPIGetInfoParams`](../interfaces/TrackAPIGetInfoParams.md) | + +#### Returns + +`Promise`<[`Track`](../interfaces/Track.md)\> + +#### Overrides + +[TrackAPI](TrackAPI.md).[getInfo](TrackAPI.md#getinfo) + +#### Defined in + +lib/track/TrackAPI.ts:31 diff --git a/docs/api/classes/SearchAPI.md b/docs/api/classes/SearchAPI.md new file mode 100644 index 0000000..8bc03b2 --- /dev/null +++ b/docs/api/classes/SearchAPI.md @@ -0,0 +1,129 @@ +[bandcamp-fetch](../README.md) / SearchAPI + +# Class: SearchAPI + +## Hierarchy + +- **`SearchAPI`** + + ↳ [`LimiterSearchAPI`](LimiterSearchAPI.md) + +## Table of contents + +### Constructors + +- [constructor](SearchAPI.md#constructor) + +### Methods + +- [albums](SearchAPI.md#albums) +- [all](SearchAPI.md#all) +- [artistsAndLabels](SearchAPI.md#artistsandlabels) +- [fans](SearchAPI.md#fans) +- [tracks](SearchAPI.md#tracks) + +## Constructors + +### constructor + +• **new SearchAPI**() + +## Methods + +### albums + +▸ `Static` **albums**(`params`): `Promise`<[`SearchResults`](../interfaces/SearchResults.md)<[`SearchResultAlbum`](../interfaces/SearchResultAlbum.md)\>\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`SearchAPISearchParams`](../interfaces/SearchAPISearchParams.md) | + +#### Returns + +`Promise`<[`SearchResults`](../interfaces/SearchResults.md)<[`SearchResultAlbum`](../interfaces/SearchResultAlbum.md)\>\> + +#### Defined in + +lib/search/SearchAPI.ts:35 + +___ + +### all + +▸ `Static` **all**(`params`): `Promise`<[`SearchResults`](../interfaces/SearchResults.md)<[`SearchResultAny`](../README.md#searchresultany)\>\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`SearchAPISearchParams`](../interfaces/SearchAPISearchParams.md) | + +#### Returns + +`Promise`<[`SearchResults`](../interfaces/SearchResults.md)<[`SearchResultAny`](../README.md#searchresultany)\>\> + +#### Defined in + +lib/search/SearchAPI.ts:27 + +___ + +### artistsAndLabels + +▸ `Static` **artistsAndLabels**(`params`): `Promise`<[`SearchResults`](../interfaces/SearchResults.md)<[`SearchResultArtist`](../interfaces/SearchResultArtist.md) \| [`SearchResultLabel`](../interfaces/SearchResultLabel.md)\>\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`SearchAPISearchParams`](../interfaces/SearchAPISearchParams.md) | + +#### Returns + +`Promise`<[`SearchResults`](../interfaces/SearchResults.md)<[`SearchResultArtist`](../interfaces/SearchResultArtist.md) \| [`SearchResultLabel`](../interfaces/SearchResultLabel.md)\>\> + +#### Defined in + +lib/search/SearchAPI.ts:31 + +___ + +### fans + +▸ `Static` **fans**(`params`): `Promise`<[`SearchResults`](../interfaces/SearchResults.md)<[`SearchResultFan`](../interfaces/SearchResultFan.md)\>\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`SearchAPISearchParams`](../interfaces/SearchAPISearchParams.md) | + +#### Returns + +`Promise`<[`SearchResults`](../interfaces/SearchResults.md)<[`SearchResultFan`](../interfaces/SearchResultFan.md)\>\> + +#### Defined in + +lib/search/SearchAPI.ts:43 + +___ + +### tracks + +▸ `Static` **tracks**(`params`): `Promise`<[`SearchResults`](../interfaces/SearchResults.md)<[`SearchResultTrack`](../interfaces/SearchResultTrack.md)\>\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`SearchAPISearchParams`](../interfaces/SearchAPISearchParams.md) | + +#### Returns + +`Promise`<[`SearchResults`](../interfaces/SearchResults.md)<[`SearchResultTrack`](../interfaces/SearchResultTrack.md)\>\> + +#### Defined in + +lib/search/SearchAPI.ts:39 diff --git a/docs/api/classes/ShowAPI.md b/docs/api/classes/ShowAPI.md new file mode 100644 index 0000000..3bc8039 --- /dev/null +++ b/docs/api/classes/ShowAPI.md @@ -0,0 +1,66 @@ +[bandcamp-fetch](../README.md) / ShowAPI + +# Class: ShowAPI + +## Hierarchy + +- **`ShowAPI`** + + ↳ [`LimiterShowAPI`](LimiterShowAPI.md) + +## Table of contents + +### Constructors + +- [constructor](ShowAPI.md#constructor) + +### Methods + +- [getShow](ShowAPI.md#getshow) +- [list](ShowAPI.md#list) + +## Constructors + +### constructor + +• **new ShowAPI**() + +## Methods + +### getShow + +▸ `Static` **getShow**(`params`): `Promise`<[`Show`](../interfaces/Show.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`ShowAPIGetShowParams`](../interfaces/ShowAPIGetShowParams.md) | + +#### Returns + +`Promise`<[`Show`](../interfaces/Show.md)\> + +#### Defined in + +lib/show/ShowAPI.ts:23 + +___ + +### list + +▸ `Static` **list**(`params?`): `Promise`<[`Show`](../interfaces/Show.md)[]\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params?` | [`ShowAPIListParams`](../interfaces/ShowAPIListParams.md) | + +#### Returns + +`Promise`<[`Show`](../interfaces/Show.md)[]\> + +#### Defined in + +lib/show/ShowAPI.ts:36 diff --git a/docs/api/classes/TagAPI.md b/docs/api/classes/TagAPI.md new file mode 100644 index 0000000..f2a65c2 --- /dev/null +++ b/docs/api/classes/TagAPI.md @@ -0,0 +1,123 @@ +[bandcamp-fetch](../README.md) / TagAPI + +# Class: TagAPI + +## Hierarchy + +- **`TagAPI`** + + ↳ [`LimiterTagAPI`](LimiterTagAPI.md) + +## Table of contents + +### Constructors + +- [constructor](TagAPI.md#constructor) + +### Methods + +- [getAlbumHighlights](TagAPI.md#getalbumhighlights) +- [getInfo](TagAPI.md#getinfo) +- [getReleases](TagAPI.md#getreleases) +- [getReleasesAvailableFilters](TagAPI.md#getreleasesavailablefilters) +- [list](TagAPI.md#list) + +## Constructors + +### constructor + +• **new TagAPI**() + +## Methods + +### getAlbumHighlights + +▸ `Static` **getAlbumHighlights**(`params`): `Promise`<[`AlbumHighlightsByTag`](../interfaces/AlbumHighlightsByTag.md)[]\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`TagAPIGetAlbumHighlightsParams`](../interfaces/TagAPIGetAlbumHighlightsParams.md) | + +#### Returns + +`Promise`<[`AlbumHighlightsByTag`](../interfaces/AlbumHighlightsByTag.md)[]\> + +#### Defined in + +lib/tag/TagAPI.ts:38 + +___ + +### getInfo + +▸ `Static` **getInfo**(`tagUrl`): `Promise`<[`Tag`](../interfaces/Tag.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `tagUrl` | `string` | + +#### Returns + +`Promise`<[`Tag`](../interfaces/Tag.md)\> + +#### Defined in + +lib/tag/TagAPI.ts:33 + +___ + +### getReleases + +▸ `Static` **getReleases**(`params`): `Promise`<[`ReleasesByTag`](../interfaces/ReleasesByTag-1.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`TagAPIGetReleasesParams`](../interfaces/TagAPIGetReleasesParams.md) | + +#### Returns + +`Promise`<[`ReleasesByTag`](../interfaces/ReleasesByTag-1.md)\> + +#### Defined in + +lib/tag/TagAPI.ts:55 + +___ + +### getReleasesAvailableFilters + +▸ `Static` **getReleasesAvailableFilters**(`tagUrl`): `Promise`<[`Filter`](../interfaces/ReleasesByTag.Filter.md)[]\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `tagUrl` | `string` | + +#### Returns + +`Promise`<[`Filter`](../interfaces/ReleasesByTag.Filter.md)[]\> + +#### Defined in + +lib/tag/TagAPI.ts:49 + +___ + +### list + +▸ `Static` **list**(): `Promise`<[`TagList`](../interfaces/TagList.md)\> + +#### Returns + +`Promise`<[`TagList`](../interfaces/TagList.md)\> + +#### Defined in + +lib/tag/TagAPI.ts:28 diff --git a/docs/api/classes/TrackAPI.md b/docs/api/classes/TrackAPI.md new file mode 100644 index 0000000..814d2c2 --- /dev/null +++ b/docs/api/classes/TrackAPI.md @@ -0,0 +1,45 @@ +[bandcamp-fetch](../README.md) / TrackAPI + +# Class: TrackAPI + +## Hierarchy + +- **`TrackAPI`** + + ↳ [`LimiterTrackAPI`](LimiterTrackAPI.md) + +## Table of contents + +### Constructors + +- [constructor](TrackAPI.md#constructor) + +### Methods + +- [getInfo](TrackAPI.md#getinfo) + +## Constructors + +### constructor + +• **new TrackAPI**() + +## Methods + +### getInfo + +▸ `Static` **getInfo**(`params`): `Promise`<[`Track`](../interfaces/Track.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `params` | [`TrackAPIGetInfoParams`](../interfaces/TrackAPIGetInfoParams.md) | + +#### Returns + +`Promise`<[`Track`](../interfaces/Track.md)\> + +#### Defined in + +lib/track/TrackAPI.ts:16 diff --git a/docs/api/enums/AutocompleteItemType.md b/docs/api/enums/AutocompleteItemType.md new file mode 100644 index 0000000..d326a21 --- /dev/null +++ b/docs/api/enums/AutocompleteItemType.md @@ -0,0 +1,30 @@ +[bandcamp-fetch](../README.md) / AutocompleteItemType + +# Enumeration: AutocompleteItemType + +## Table of contents + +### Enumeration Members + +- [Location](AutocompleteItemType.md#location) +- [Tag](AutocompleteItemType.md#tag) + +## Enumeration Members + +### Location + +• **Location** = ``"Location"`` + +#### Defined in + +lib/autocomplete/AutocompleteAPI.ts:9 + +___ + +### Tag + +• **Tag** = ``"Tag"`` + +#### Defined in + +lib/autocomplete/AutocompleteAPI.ts:8 diff --git a/docs/api/enums/CacheDataType.md b/docs/api/enums/CacheDataType.md new file mode 100644 index 0000000..1c506b1 --- /dev/null +++ b/docs/api/enums/CacheDataType.md @@ -0,0 +1,30 @@ +[bandcamp-fetch](../README.md) / CacheDataType + +# Enumeration: CacheDataType + +## Table of contents + +### Enumeration Members + +- [Constants](CacheDataType.md#constants) +- [Page](CacheDataType.md#page) + +## Enumeration Members + +### Constants + +• **Constants** = ``"Constants"`` + +#### Defined in + +lib/utils/Cache.ts:5 + +___ + +### Page + +• **Page** = ``"Page"`` + +#### Defined in + +lib/utils/Cache.ts:4 diff --git a/docs/api/enums/ImageFormatFilter.md b/docs/api/enums/ImageFormatFilter.md new file mode 100644 index 0000000..ade7ecd --- /dev/null +++ b/docs/api/enums/ImageFormatFilter.md @@ -0,0 +1,34 @@ +[bandcamp-fetch](../README.md) / ImageFormatFilter + +# Enumeration: ImageFormatFilter + +## Table of contents + +### Enumeration Members + +- [Album](ImageFormatFilter.md#album) +- [Bio](ImageFormatFilter.md#bio) + +## Enumeration Members + +### Album + +• **Album** = ``"album"`` + +Album image formats + +#### Defined in + +lib/image/ImageAPI.ts:10 + +___ + +### Bio + +• **Bio** = ``"bio"`` + +Artist / Profile image formats + +#### Defined in + +lib/image/ImageAPI.ts:12 diff --git a/docs/api/enums/SearchItemType.md b/docs/api/enums/SearchItemType.md new file mode 100644 index 0000000..7a463c4 --- /dev/null +++ b/docs/api/enums/SearchItemType.md @@ -0,0 +1,63 @@ +[bandcamp-fetch](../README.md) / SearchItemType + +# Enumeration: SearchItemType + +## Table of contents + +### Enumeration Members + +- [Albums](SearchItemType.md#albums) +- [All](SearchItemType.md#all) +- [ArtistsAndLabels](SearchItemType.md#artistsandlabels) +- [Fans](SearchItemType.md#fans) +- [Tracks](SearchItemType.md#tracks) + +## Enumeration Members + +### Albums + +• **Albums** = ``"Albums"`` + +#### Defined in + +lib/search/SearchAPI.ts:13 + +___ + +### All + +• **All** = ``"All"`` + +#### Defined in + +lib/search/SearchAPI.ts:11 + +___ + +### ArtistsAndLabels + +• **ArtistsAndLabels** = ``"ArtistsAndLabels"`` + +#### Defined in + +lib/search/SearchAPI.ts:12 + +___ + +### Fans + +• **Fans** = ``"Fans"`` + +#### Defined in + +lib/search/SearchAPI.ts:15 + +___ + +### Tracks + +• **Tracks** = ``"Tracks"`` + +#### Defined in + +lib/search/SearchAPI.ts:14 diff --git a/docs/api/interfaces/Album.md b/docs/api/interfaces/Album.md new file mode 100644 index 0000000..8045354 --- /dev/null +++ b/docs/api/interfaces/Album.md @@ -0,0 +1,240 @@ +[bandcamp-fetch](../README.md) / Album + +# Interface: Album + +## Hierarchy + +- [`MediaKind`](MediaKind.md) + + ↳ **`Album`** + +## Table of contents + +### Properties + +- [artist](Album.md#artist) +- [description](Album.md#description) +- [featuredTrack](Album.md#featuredtrack) +- [genre](Album.md#genre) +- [imageUrl](Album.md#imageurl) +- [keywords](Album.md#keywords) +- [label](Album.md#label) +- [location](Album.md#location) +- [name](Album.md#name) +- [numTracks](Album.md#numtracks) +- [publisher](Album.md#publisher) +- [raw](Album.md#raw) +- [releaseDate](Album.md#releasedate) +- [releases](Album.md#releases) +- [tracks](Album.md#tracks) +- [type](Album.md#type) +- [url](Album.md#url) + +## Properties + +### artist + +• `Optional` **artist**: `Omit`<[`Artist`](Artist.md), ``"type"``\> + +#### Inherited from + +[MediaKind](MediaKind.md).[artist](MediaKind.md#artist) + +#### Defined in + +lib/types/MediaKind.ts:10 + +___ + +### description + +• `Optional` **description**: `string` + +#### Defined in + +lib/types/Album.ts:8 + +___ + +### featuredTrack + +• `Optional` **featuredTrack**: `Omit`<[`Track`](Track.md), ``"type"``\> + +#### Defined in + +lib/types/Album.ts:11 + +___ + +### genre + +• `Optional` **genre**: `string` + +#### Defined in + +lib/types/Album.ts:9 + +___ + +### imageUrl + +• `Optional` **imageUrl**: `string` + +#### Inherited from + +[MediaKind](MediaKind.md).[imageUrl](MediaKind.md#imageurl) + +#### Defined in + +lib/types/MediaKind.ts:8 + +___ + +### keywords + +• `Optional` **keywords**: `string`[] + +#### Defined in + +lib/types/Album.ts:7 + +___ + +### label + +• `Optional` **label**: `Omit`<[`Label`](Label.md), ``"type"``\> + +#### Inherited from + +[MediaKind](MediaKind.md).[label](MediaKind.md#label) + +#### Defined in + +lib/types/MediaKind.ts:11 + +___ + +### location + +• `Optional` **location**: `string` + +#### Defined in + +lib/types/Album.ts:10 + +___ + +### name + +• **name**: `string` + +#### Inherited from + +[MediaKind](MediaKind.md).[name](MediaKind.md#name) + +#### Defined in + +lib/types/MediaKind.ts:6 + +___ + +### numTracks + +• `Optional` **numTracks**: `number` + +#### Defined in + +lib/types/Album.ts:6 + +___ + +### publisher + +• `Optional` **publisher**: [`UserKind`](UserKind.md) + +#### Inherited from + +[MediaKind](MediaKind.md).[publisher](MediaKind.md#publisher) + +#### Defined in + +lib/types/MediaKind.ts:12 + +___ + +### raw + +• `Optional` **raw**: `Object` + +#### Type declaration + +| Name | Type | +| :------ | :------ | +| `basic` | `string` | +| `extra` | `string` | + +#### Inherited from + +[MediaKind](MediaKind.md).[raw](MediaKind.md#raw) + +#### Defined in + +lib/types/MediaKind.ts:13 + +___ + +### releaseDate + +• `Optional` **releaseDate**: `string` + +#### Inherited from + +[MediaKind](MediaKind.md).[releaseDate](MediaKind.md#releasedate) + +#### Defined in + +lib/types/MediaKind.ts:9 + +___ + +### releases + +• `Optional` **releases**: [`AlbumRelease`](AlbumRelease.md)[] + +#### Defined in + +lib/types/Album.ts:12 + +___ + +### tracks + +• `Optional` **tracks**: `Omit`<[`Track`](Track.md), ``"type"``\>[] + +#### Defined in + +lib/types/Album.ts:13 + +___ + +### type + +• **type**: ``"album"`` + +#### Defined in + +lib/types/Album.ts:5 + +___ + +### url + +• `Optional` **url**: `string` + +#### Inherited from + +[MediaKind](MediaKind.md).[url](MediaKind.md#url) + +#### Defined in + +lib/types/MediaKind.ts:7 diff --git a/docs/api/interfaces/AlbumAPIGetInfoParams.md b/docs/api/interfaces/AlbumAPIGetInfoParams.md new file mode 100644 index 0000000..40a5e57 --- /dev/null +++ b/docs/api/interfaces/AlbumAPIGetInfoParams.md @@ -0,0 +1,52 @@ +[bandcamp-fetch](../README.md) / AlbumAPIGetInfoParams + +# Interface: AlbumAPIGetInfoParams + +## Table of contents + +### Properties + +- [albumImageFormat](AlbumAPIGetInfoParams.md#albumimageformat) +- [albumUrl](AlbumAPIGetInfoParams.md#albumurl) +- [artistImageFormat](AlbumAPIGetInfoParams.md#artistimageformat) +- [includeRawData](AlbumAPIGetInfoParams.md#includerawdata) + +## Properties + +### albumImageFormat + +• `Optional` **albumImageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +#### Defined in + +lib/album/AlbumAPI.ts:10 + +___ + +### albumUrl + +• **albumUrl**: `string` + +#### Defined in + +lib/album/AlbumAPI.ts:9 + +___ + +### artistImageFormat + +• `Optional` **artistImageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +#### Defined in + +lib/album/AlbumAPI.ts:11 + +___ + +### includeRawData + +• `Optional` **includeRawData**: `boolean` + +#### Defined in + +lib/album/AlbumAPI.ts:12 diff --git a/docs/api/interfaces/AlbumHighlightsByTag.md b/docs/api/interfaces/AlbumHighlightsByTag.md new file mode 100644 index 0000000..c5d748d --- /dev/null +++ b/docs/api/interfaces/AlbumHighlightsByTag.md @@ -0,0 +1,41 @@ +[bandcamp-fetch](../README.md) / AlbumHighlightsByTag + +# Interface: AlbumHighlightsByTag + +## Table of contents + +### Properties + +- [items](AlbumHighlightsByTag.md#items) +- [name](AlbumHighlightsByTag.md#name) +- [title](AlbumHighlightsByTag.md#title) + +## Properties + +### items + +• **items**: [`Album`](Album.md)[] + +#### Defined in + +lib/types/Tag.ts:23 + +___ + +### name + +• **name**: `string` + +#### Defined in + +lib/types/Tag.ts:21 + +___ + +### title + +• **title**: `string` + +#### Defined in + +lib/types/Tag.ts:22 diff --git a/docs/api/interfaces/AlbumRelease.md b/docs/api/interfaces/AlbumRelease.md new file mode 100644 index 0000000..2b622f9 --- /dev/null +++ b/docs/api/interfaces/AlbumRelease.md @@ -0,0 +1,63 @@ +[bandcamp-fetch](../README.md) / AlbumRelease + +# Interface: AlbumRelease + +## Table of contents + +### Properties + +- [description](AlbumRelease.md#description) +- [format](AlbumRelease.md#format) +- [imageUrl](AlbumRelease.md#imageurl) +- [name](AlbumRelease.md#name) +- [url](AlbumRelease.md#url) + +## Properties + +### description + +• `Optional` **description**: `string` + +#### Defined in + +lib/types/Album.ts:21 + +___ + +### format + +• **format**: `string` + +#### Defined in + +lib/types/Album.ts:18 + +___ + +### imageUrl + +• `Optional` **imageUrl**: `string` + +#### Defined in + +lib/types/Album.ts:20 + +___ + +### name + +• **name**: `string` + +#### Defined in + +lib/types/Album.ts:17 + +___ + +### url + +• `Optional` **url**: `string` + +#### Defined in + +lib/types/Album.ts:19 diff --git a/docs/api/interfaces/Article.md b/docs/api/interfaces/Article.md new file mode 100644 index 0000000..e99f22b --- /dev/null +++ b/docs/api/interfaces/Article.md @@ -0,0 +1,152 @@ +[bandcamp-fetch](../README.md) / Article + +# Interface: Article + +## Table of contents + +### Properties + +- [author](Article.md#author) +- [category](Article.md#category) +- [date](Article.md#date) +- [description](Article.md#description) +- [genre](Article.md#genre) +- [imageUrl](Article.md#imageurl) +- [mediaItems](Article.md#mediaitems) +- [raw](Article.md#raw) +- [sections](Article.md#sections) +- [title](Article.md#title) +- [url](Article.md#url) + +## Properties + +### author + +• **author**: `Object` + +#### Type declaration + +| Name | Type | +| :------ | :------ | +| `name` | `string` | +| `url` | `string` | + +#### Defined in + +lib/types/Article.ts:16 + +___ + +### category + +• **category**: [`ArticleCategory`](ArticleCategory.md) + +#### Defined in + +lib/types/Article.ts:10 + +___ + +### date + +• **date**: `string` + +#### Defined in + +lib/types/Article.ts:9 + +___ + +### description + +• **description**: `string` + +#### Defined in + +lib/types/Article.ts:6 + +___ + +### genre + +• `Optional` **genre**: `Object` + +#### Type declaration + +| Name | Type | +| :------ | :------ | +| `name` | `string` | +| `readMoreUrl?` | `string` | +| `url?` | `string` | + +#### Defined in + +lib/types/Article.ts:11 + +___ + +### imageUrl + +• **imageUrl**: `string` + +#### Defined in + +lib/types/Article.ts:8 + +___ + +### mediaItems + +• **mediaItems**: [`ArticleMediaItem`](../README.md#articlemediaitem)[] + +#### Defined in + +lib/types/Article.ts:20 + +___ + +### raw + +• `Optional` **raw**: `Object` + +#### Type declaration + +| Name | Type | +| :------ | :------ | +| `basic` | `any` | +| `body` | `string` | +| `mediaItems` | `any` | + +#### Defined in + +lib/types/Article.ts:22 + +___ + +### sections + +• **sections**: [`ArticleSection`](ArticleSection.md)[] + +#### Defined in + +lib/types/Article.ts:21 + +___ + +### title + +• **title**: `string` + +#### Defined in + +lib/types/Article.ts:5 + +___ + +### url + +• **url**: `string` + +#### Defined in + +lib/types/Article.ts:7 diff --git a/docs/api/interfaces/ArticleAPIGetArticleParams.md b/docs/api/interfaces/ArticleAPIGetArticleParams.md new file mode 100644 index 0000000..18ac0fe --- /dev/null +++ b/docs/api/interfaces/ArticleAPIGetArticleParams.md @@ -0,0 +1,52 @@ +[bandcamp-fetch](../README.md) / ArticleAPIGetArticleParams + +# Interface: ArticleAPIGetArticleParams + +## Table of contents + +### Properties + +- [albumImageFormat](ArticleAPIGetArticleParams.md#albumimageformat) +- [articleUrl](ArticleAPIGetArticleParams.md#articleurl) +- [artistImageFormat](ArticleAPIGetArticleParams.md#artistimageformat) +- [includeRawData](ArticleAPIGetArticleParams.md#includerawdata) + +## Properties + +### albumImageFormat + +• `Optional` **albumImageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +#### Defined in + +lib/article/ArticleAPI.ts:14 + +___ + +### articleUrl + +• **articleUrl**: `string` + +#### Defined in + +lib/article/ArticleAPI.ts:13 + +___ + +### artistImageFormat + +• `Optional` **artistImageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +#### Defined in + +lib/article/ArticleAPI.ts:15 + +___ + +### includeRawData + +• `Optional` **includeRawData**: `boolean` + +#### Defined in + +lib/article/ArticleAPI.ts:16 diff --git a/docs/api/interfaces/ArticleAPIListParams.md b/docs/api/interfaces/ArticleAPIListParams.md new file mode 100644 index 0000000..9646158 --- /dev/null +++ b/docs/api/interfaces/ArticleAPIListParams.md @@ -0,0 +1,41 @@ +[bandcamp-fetch](../README.md) / ArticleAPIListParams + +# Interface: ArticleAPIListParams + +## Table of contents + +### Properties + +- [categoryUrl](ArticleAPIListParams.md#categoryurl) +- [imageFormat](ArticleAPIListParams.md#imageformat) +- [page](ArticleAPIListParams.md#page) + +## Properties + +### categoryUrl + +• `Optional` **categoryUrl**: `string` + +#### Defined in + +lib/article/ArticleAPI.ts:20 + +___ + +### imageFormat + +• `Optional` **imageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +#### Defined in + +lib/article/ArticleAPI.ts:21 + +___ + +### page + +• `Optional` **page**: `number` + +#### Defined in + +lib/article/ArticleAPI.ts:22 diff --git a/docs/api/interfaces/ArticleCategory.md b/docs/api/interfaces/ArticleCategory.md new file mode 100644 index 0000000..650aef7 --- /dev/null +++ b/docs/api/interfaces/ArticleCategory.md @@ -0,0 +1,30 @@ +[bandcamp-fetch](../README.md) / ArticleCategory + +# Interface: ArticleCategory + +## Table of contents + +### Properties + +- [name](ArticleCategory.md#name) +- [url](ArticleCategory.md#url) + +## Properties + +### name + +• **name**: `string` + +#### Defined in + +lib/types/Article.ts:30 + +___ + +### url + +• `Optional` **url**: `string` + +#### Defined in + +lib/types/Article.ts:31 diff --git a/docs/api/interfaces/ArticleCategorySection.md b/docs/api/interfaces/ArticleCategorySection.md new file mode 100644 index 0000000..8662463 --- /dev/null +++ b/docs/api/interfaces/ArticleCategorySection.md @@ -0,0 +1,52 @@ +[bandcamp-fetch](../README.md) / ArticleCategorySection + +# Interface: ArticleCategorySection + +## Table of contents + +### Properties + +- [categories](ArticleCategorySection.md#categories) +- [name](ArticleCategorySection.md#name) +- [sections](ArticleCategorySection.md#sections) +- [title](ArticleCategorySection.md#title) + +## Properties + +### categories + +• `Optional` **categories**: [`ArticleCategory`](ArticleCategory.md)[] + +#### Defined in + +lib/types/Article.ts:38 + +___ + +### name + +• **name**: `string` + +#### Defined in + +lib/types/Article.ts:35 + +___ + +### sections + +• `Optional` **sections**: [`ArticleCategorySection`](ArticleCategorySection.md)[] + +#### Defined in + +lib/types/Article.ts:37 + +___ + +### title + +• `Optional` **title**: `string` + +#### Defined in + +lib/types/Article.ts:36 diff --git a/docs/api/interfaces/ArticleList.md b/docs/api/interfaces/ArticleList.md new file mode 100644 index 0000000..78b51d7 --- /dev/null +++ b/docs/api/interfaces/ArticleList.md @@ -0,0 +1,52 @@ +[bandcamp-fetch](../README.md) / ArticleList + +# Interface: ArticleList + +## Table of contents + +### Properties + +- [articles](ArticleList.md#articles) +- [end](ArticleList.md#end) +- [start](ArticleList.md#start) +- [total](ArticleList.md#total) + +## Properties + +### articles + +• **articles**: [`ArticleListItem`](ArticleListItem.md)[] + +#### Defined in + +lib/types/Article.ts:57 + +___ + +### end + +• **end**: `number` + +#### Defined in + +lib/types/Article.ts:60 + +___ + +### start + +• **start**: `number` + +#### Defined in + +lib/types/Article.ts:59 + +___ + +### total + +• **total**: `number` + +#### Defined in + +lib/types/Article.ts:58 diff --git a/docs/api/interfaces/ArticleListItem.md b/docs/api/interfaces/ArticleListItem.md new file mode 100644 index 0000000..0e6da49 --- /dev/null +++ b/docs/api/interfaces/ArticleListItem.md @@ -0,0 +1,63 @@ +[bandcamp-fetch](../README.md) / ArticleListItem + +# Interface: ArticleListItem + +## Table of contents + +### Properties + +- [category](ArticleListItem.md#category) +- [date](ArticleListItem.md#date) +- [imageUrl](ArticleListItem.md#imageurl) +- [title](ArticleListItem.md#title) +- [url](ArticleListItem.md#url) + +## Properties + +### category + +• `Optional` **category**: [`ArticleCategory`](ArticleCategory.md) + +#### Defined in + +lib/types/Article.ts:67 + +___ + +### date + +• **date**: `string` + +#### Defined in + +lib/types/Article.ts:66 + +___ + +### imageUrl + +• `Optional` **imageUrl**: `string` + +#### Defined in + +lib/types/Article.ts:68 + +___ + +### title + +• **title**: `string` + +#### Defined in + +lib/types/Article.ts:65 + +___ + +### url + +• **url**: `string` + +#### Defined in + +lib/types/Article.ts:64 diff --git a/docs/api/interfaces/ArticleSection.md b/docs/api/interfaces/ArticleSection.md new file mode 100644 index 0000000..0e7ea01 --- /dev/null +++ b/docs/api/interfaces/ArticleSection.md @@ -0,0 +1,59 @@ +[bandcamp-fetch](../README.md) / ArticleSection + +# Interface: ArticleSection + +## Table of contents + +### Properties + +- [heading](ArticleSection.md#heading) +- [html](ArticleSection.md#html) +- [mediaItemRef](ArticleSection.md#mediaitemref) +- [text](ArticleSection.md#text) + +## Properties + +### heading + +• `Optional` **heading**: `Object` + +#### Type declaration + +| Name | Type | +| :------ | :------ | +| `html` | `string` | +| `text` | `string` | + +#### Defined in + +lib/types/Article.ts:42 + +___ + +### html + +• **html**: `string` + +#### Defined in + +lib/types/Article.ts:46 + +___ + +### mediaItemRef + +• `Optional` **mediaItemRef**: `string` + +#### Defined in + +lib/types/Article.ts:48 + +___ + +### text + +• **text**: `string` + +#### Defined in + +lib/types/Article.ts:47 diff --git a/docs/api/interfaces/Artist.md b/docs/api/interfaces/Artist.md new file mode 100644 index 0000000..34a243f --- /dev/null +++ b/docs/api/interfaces/Artist.md @@ -0,0 +1,122 @@ +[bandcamp-fetch](../README.md) / Artist + +# Interface: Artist + +## Hierarchy + +- [`UserKind`](UserKind.md) + + ↳ **`Artist`** + +## Table of contents + +### Properties + +- [description](Artist.md#description) +- [genre](Artist.md#genre) +- [imageUrl](Artist.md#imageurl) +- [label](Artist.md#label) +- [location](Artist.md#location) +- [name](Artist.md#name) +- [type](Artist.md#type) +- [url](Artist.md#url) + +## Properties + +### description + +• `Optional` **description**: `string` + +#### Inherited from + +[UserKind](UserKind.md).[description](UserKind.md#description) + +#### Defined in + +lib/types/UserKind.ts:3 + +___ + +### genre + +• `Optional` **genre**: `string` + +#### Defined in + +lib/types/Artist.ts:7 + +___ + +### imageUrl + +• `Optional` **imageUrl**: `string` + +#### Inherited from + +[UserKind](UserKind.md).[imageUrl](UserKind.md#imageurl) + +#### Defined in + +lib/types/UserKind.ts:5 + +___ + +### label + +• `Optional` **label**: [`Label`](Label.md) + +#### Defined in + +lib/types/Artist.ts:6 + +___ + +### location + +• `Optional` **location**: `string` + +#### Inherited from + +[UserKind](UserKind.md).[location](UserKind.md#location) + +#### Defined in + +lib/types/UserKind.ts:6 + +___ + +### name + +• **name**: `string` + +#### Inherited from + +[UserKind](UserKind.md).[name](UserKind.md#name) + +#### Defined in + +lib/types/UserKind.ts:2 + +___ + +### type + +• **type**: ``"artist"`` + +#### Defined in + +lib/types/Artist.ts:5 + +___ + +### url + +• `Optional` **url**: `string` + +#### Inherited from + +[UserKind](UserKind.md).[url](UserKind.md#url) + +#### Defined in + +lib/types/UserKind.ts:4 diff --git a/docs/api/interfaces/AutoCompleteTag.md b/docs/api/interfaces/AutoCompleteTag.md new file mode 100644 index 0000000..a2217a0 --- /dev/null +++ b/docs/api/interfaces/AutoCompleteTag.md @@ -0,0 +1,66 @@ +[bandcamp-fetch](../README.md) / AutoCompleteTag + +# Interface: AutoCompleteTag + +## Hierarchy + +- [`AutocompleteItem`](AutocompleteItem.md) + + ↳ **`AutoCompleteTag`** + +## Table of contents + +### Properties + +- [count](AutoCompleteTag.md#count) +- [name](AutoCompleteTag.md#name) +- [type](AutoCompleteTag.md#type) +- [value](AutoCompleteTag.md#value) + +## Properties + +### count + +• **count**: `number` + +#### Defined in + +lib/types/Autocomplete.ts:8 + +___ + +### name + +• **name**: `string` + +#### Inherited from + +[AutocompleteItem](AutocompleteItem.md).[name](AutocompleteItem.md#name) + +#### Defined in + +lib/types/Autocomplete.ts:3 + +___ + +### type + +• **type**: ``"tag"`` + +#### Overrides + +[AutocompleteItem](AutocompleteItem.md).[type](AutocompleteItem.md#type) + +#### Defined in + +lib/types/Autocomplete.ts:7 + +___ + +### value + +• **value**: ``"string"`` + +#### Defined in + +lib/types/Autocomplete.ts:9 diff --git a/docs/api/interfaces/AutocompleteAPIGetSuggestionsParams.md b/docs/api/interfaces/AutocompleteAPIGetSuggestionsParams.md new file mode 100644 index 0000000..8fe7751 --- /dev/null +++ b/docs/api/interfaces/AutocompleteAPIGetSuggestionsParams.md @@ -0,0 +1,41 @@ +[bandcamp-fetch](../README.md) / AutocompleteAPIGetSuggestionsParams + +# Interface: AutocompleteAPIGetSuggestionsParams + +## Table of contents + +### Properties + +- [itemType](AutocompleteAPIGetSuggestionsParams.md#itemtype) +- [limit](AutocompleteAPIGetSuggestionsParams.md#limit) +- [query](AutocompleteAPIGetSuggestionsParams.md#query) + +## Properties + +### itemType + +• **itemType**: [`AutocompleteItemType`](../enums/AutocompleteItemType.md) + +#### Defined in + +lib/autocomplete/AutocompleteAPI.ts:14 + +___ + +### limit + +• `Optional` **limit**: `number` + +#### Defined in + +lib/autocomplete/AutocompleteAPI.ts:15 + +___ + +### query + +• **query**: `string` + +#### Defined in + +lib/autocomplete/AutocompleteAPI.ts:13 diff --git a/docs/api/interfaces/AutocompleteItem.md b/docs/api/interfaces/AutocompleteItem.md new file mode 100644 index 0000000..37962cb --- /dev/null +++ b/docs/api/interfaces/AutocompleteItem.md @@ -0,0 +1,38 @@ +[bandcamp-fetch](../README.md) / AutocompleteItem + +# Interface: AutocompleteItem + +## Hierarchy + +- **`AutocompleteItem`** + + ↳ [`AutoCompleteTag`](AutoCompleteTag.md) + + ↳ [`AutocompleteLocation`](AutocompleteLocation.md) + +## Table of contents + +### Properties + +- [name](AutocompleteItem.md#name) +- [type](AutocompleteItem.md#type) + +## Properties + +### name + +• **name**: `string` + +#### Defined in + +lib/types/Autocomplete.ts:3 + +___ + +### type + +• **type**: ``"location"`` \| ``"tag"`` + +#### Defined in + +lib/types/Autocomplete.ts:2 diff --git a/docs/api/interfaces/AutocompleteLocation.md b/docs/api/interfaces/AutocompleteLocation.md new file mode 100644 index 0000000..851fa8b --- /dev/null +++ b/docs/api/interfaces/AutocompleteLocation.md @@ -0,0 +1,66 @@ +[bandcamp-fetch](../README.md) / AutocompleteLocation + +# Interface: AutocompleteLocation + +## Hierarchy + +- [`AutocompleteItem`](AutocompleteItem.md) + + ↳ **`AutocompleteLocation`** + +## Table of contents + +### Properties + +- [fullName](AutocompleteLocation.md#fullname) +- [name](AutocompleteLocation.md#name) +- [type](AutocompleteLocation.md#type) +- [value](AutocompleteLocation.md#value) + +## Properties + +### fullName + +• **fullName**: `string` + +#### Defined in + +lib/types/Autocomplete.ts:14 + +___ + +### name + +• **name**: `string` + +#### Inherited from + +[AutocompleteItem](AutocompleteItem.md).[name](AutocompleteItem.md#name) + +#### Defined in + +lib/types/Autocomplete.ts:3 + +___ + +### type + +• **type**: ``"location"`` + +#### Overrides + +[AutocompleteItem](AutocompleteItem.md).[type](AutocompleteItem.md#type) + +#### Defined in + +lib/types/Autocomplete.ts:13 + +___ + +### value + +• **value**: `number` + +#### Defined in + +lib/types/Autocomplete.ts:15 diff --git a/docs/api/interfaces/BandAPIGetDiscographyParams.md b/docs/api/interfaces/BandAPIGetDiscographyParams.md new file mode 100644 index 0000000..5bc1b2c --- /dev/null +++ b/docs/api/interfaces/BandAPIGetDiscographyParams.md @@ -0,0 +1,30 @@ +[bandcamp-fetch](../README.md) / BandAPIGetDiscographyParams + +# Interface: BandAPIGetDiscographyParams + +## Table of contents + +### Properties + +- [bandUrl](BandAPIGetDiscographyParams.md#bandurl) +- [imageFormat](BandAPIGetDiscographyParams.md#imageformat) + +## Properties + +### bandUrl + +• **bandUrl**: `string` + +#### Defined in + +lib/band/BandAPI.ts:15 + +___ + +### imageFormat + +• `Optional` **imageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +#### Defined in + +lib/band/BandAPI.ts:16 diff --git a/docs/api/interfaces/BandAPIGetInfoParams.md b/docs/api/interfaces/BandAPIGetInfoParams.md new file mode 100644 index 0000000..2f4ff88 --- /dev/null +++ b/docs/api/interfaces/BandAPIGetInfoParams.md @@ -0,0 +1,41 @@ +[bandcamp-fetch](../README.md) / BandAPIGetInfoParams + +# Interface: BandAPIGetInfoParams + +## Table of contents + +### Properties + +- [bandUrl](BandAPIGetInfoParams.md#bandurl) +- [imageFormat](BandAPIGetInfoParams.md#imageformat) +- [labelId](BandAPIGetInfoParams.md#labelid) + +## Properties + +### bandUrl + +• **bandUrl**: `string` + +#### Defined in + +lib/band/BandAPI.ts:20 + +___ + +### imageFormat + +• `Optional` **imageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +#### Defined in + +lib/band/BandAPI.ts:21 + +___ + +### labelId + +• `Optional` **labelId**: `number` + +#### Defined in + +lib/band/BandAPI.ts:22 diff --git a/docs/api/interfaces/BandAPIGetLabelArtistsParams.md b/docs/api/interfaces/BandAPIGetLabelArtistsParams.md new file mode 100644 index 0000000..9cf6b93 --- /dev/null +++ b/docs/api/interfaces/BandAPIGetLabelArtistsParams.md @@ -0,0 +1,30 @@ +[bandcamp-fetch](../README.md) / BandAPIGetLabelArtistsParams + +# Interface: BandAPIGetLabelArtistsParams + +## Table of contents + +### Properties + +- [imageFormat](BandAPIGetLabelArtistsParams.md#imageformat) +- [labelUrl](BandAPIGetLabelArtistsParams.md#labelurl) + +## Properties + +### imageFormat + +• `Optional` **imageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +#### Defined in + +lib/band/BandAPI.ts:27 + +___ + +### labelUrl + +• **labelUrl**: `string` + +#### Defined in + +lib/band/BandAPI.ts:26 diff --git a/docs/api/interfaces/DiscoverOptions.md b/docs/api/interfaces/DiscoverOptions.md new file mode 100644 index 0000000..288c4c4 --- /dev/null +++ b/docs/api/interfaces/DiscoverOptions.md @@ -0,0 +1,91 @@ +[bandcamp-fetch](../README.md) / DiscoverOptions + +# Interface: DiscoverOptions + +Options and list of values for each option that can be used to formulate [DiscoverParams](DiscoverParams.md). + +**`See`** + +DiscoveryAPI.getAvailableOptions + +## Table of contents + +### Properties + +- [artistRecommendationTypes](DiscoverOptions.md#artistrecommendationtypes) +- [formats](DiscoverOptions.md#formats) +- [genres](DiscoverOptions.md#genres) +- [locations](DiscoverOptions.md#locations) +- [sortBys](DiscoverOptions.md#sortbys) +- [subgenres](DiscoverOptions.md#subgenres) +- [times](DiscoverOptions.md#times) + +## Properties + +### artistRecommendationTypes + +• **artistRecommendationTypes**: [`NameValuePair`](NameValuePair.md)<`string`\>[] + +#### Defined in + +lib/types/Discovery.ts:17 + +___ + +### formats + +• **formats**: [`NameValuePair`](NameValuePair.md)<`string`\>[] + +#### Defined in + +lib/types/Discovery.ts:16 + +___ + +### genres + +• **genres**: [`NameValuePair`](NameValuePair.md)<`string`\>[] + +#### Defined in + +lib/types/Discovery.ts:11 + +___ + +### locations + +• **locations**: [`NameValuePair`](NameValuePair.md)<`string`\>[] + +#### Defined in + +lib/types/Discovery.ts:15 + +___ + +### sortBys + +• **sortBys**: [`NameValuePair`](NameValuePair.md)<`string`\>[] + +#### Defined in + +lib/types/Discovery.ts:12 + +___ + +### subgenres + +• **subgenres**: `Record`<`string`, [`NameValuePair`](NameValuePair.md)<`string`\>[]\> + +#### Defined in + +lib/types/Discovery.ts:14 + +___ + +### times + +• **times**: [`NameValuePair`](NameValuePair.md)<`number`\> & { `title`: `string` }[] + +#### Defined in + +lib/types/Discovery.ts:13 diff --git a/docs/api/interfaces/DiscoverParams.md b/docs/api/interfaces/DiscoverParams.md new file mode 100644 index 0000000..9fe7633 --- /dev/null +++ b/docs/api/interfaces/DiscoverParams.md @@ -0,0 +1,128 @@ +[bandcamp-fetch](../README.md) / DiscoverParams + +# Interface: DiscoverParams + +Params used in discovery requests. + +**`See`** + +DiscoveryAPI.discover + +## Table of contents + +### Properties + +- [albumImageFormat](DiscoverParams.md#albumimageformat) +- [artistImageFormat](DiscoverParams.md#artistimageformat) +- [artistRecommendationType](DiscoverParams.md#artistrecommendationtype) +- [format](DiscoverParams.md#format) +- [genre](DiscoverParams.md#genre) +- [location](DiscoverParams.md#location) +- [page](DiscoverParams.md#page) +- [sortBy](DiscoverParams.md#sortby) +- [subgenre](DiscoverParams.md#subgenre) +- [time](DiscoverParams.md#time) + +## Properties + +### albumImageFormat + +• `Optional` **albumImageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +Value indicating the image format to adopt when constructing image URLs of discovered albums. + +#### Defined in + +lib/types/Discovery.ts:37 + +___ + +### artistImageFormat + +• `Optional` **artistImageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +Value indicating the image format to adopt when constructing image URLs of album artists. + +#### Defined in + +lib/types/Discovery.ts:41 + +___ + +### artistRecommendationType + +• `Optional` **artistRecommendationType**: `string` + +#### Defined in + +lib/types/Discovery.ts:33 + +___ + +### format + +• `Optional` **format**: `string` + +#### Defined in + +lib/types/Discovery.ts:32 + +___ + +### genre + +• `Optional` **genre**: `string` + +#### Defined in + +lib/types/Discovery.ts:26 + +___ + +### location + +• `Optional` **location**: `string` + +#### Defined in + +lib/types/Discovery.ts:31 + +___ + +### page + +• `Optional` **page**: `number` + +#### Defined in + +lib/types/Discovery.ts:28 + +___ + +### sortBy + +• `Optional` **sortBy**: `string` + +#### Defined in + +lib/types/Discovery.ts:27 + +___ + +### subgenre + +• `Optional` **subgenre**: `string` + +#### Defined in + +lib/types/Discovery.ts:30 + +___ + +### time + +• `Optional` **time**: `number` + +#### Defined in + +lib/types/Discovery.ts:29 diff --git a/docs/api/interfaces/DiscoverResult.md b/docs/api/interfaces/DiscoverResult.md new file mode 100644 index 0000000..0b7e72d --- /dev/null +++ b/docs/api/interfaces/DiscoverResult.md @@ -0,0 +1,49 @@ +[bandcamp-fetch](../README.md) / DiscoverResult + +# Interface: DiscoverResult + +Results returned by [discover](../classes/DiscoveryAPI.md#discover). + +## Table of contents + +### Properties + +- [items](DiscoverResult.md#items) +- [params](DiscoverResult.md#params) +- [total](DiscoverResult.md#total) + +## Properties + +### items + +• **items**: [`Album`](Album.md)[] + +List of discovered albums. + +#### Defined in + +lib/types/Discovery.ts:51 + +___ + +### params + +• **params**: [`DiscoverParams`](DiscoverParams.md) + +Sanitized params used in the discovery request. + +#### Defined in + +lib/types/Discovery.ts:59 + +___ + +### total + +• **total**: `number` + +Total number of albums discovered. + +#### Defined in + +lib/types/Discovery.ts:55 diff --git a/docs/api/interfaces/Fan.md b/docs/api/interfaces/Fan.md new file mode 100644 index 0000000..3a844cf --- /dev/null +++ b/docs/api/interfaces/Fan.md @@ -0,0 +1,166 @@ +[bandcamp-fetch](../README.md) / Fan + +# Interface: Fan + +## Hierarchy + +- [`UserKind`](UserKind.md) + + ↳ **`Fan`** + +## Table of contents + +### Properties + +- [collectionItemCount](Fan.md#collectionitemcount) +- [description](Fan.md#description) +- [followingArtistsAndLabelsCount](Fan.md#followingartistsandlabelscount) +- [followingGenresCount](Fan.md#followinggenrescount) +- [imageUrl](Fan.md#imageurl) +- [location](Fan.md#location) +- [name](Fan.md#name) +- [type](Fan.md#type) +- [url](Fan.md#url) +- [username](Fan.md#username) +- [websiteUrl](Fan.md#websiteurl) +- [wishlistItemCount](Fan.md#wishlistitemcount) + +## Properties + +### collectionItemCount + +• **collectionItemCount**: `number` + +#### Defined in + +lib/types/Fan.ts:10 + +___ + +### description + +• `Optional` **description**: `string` + +#### Inherited from + +[UserKind](UserKind.md).[description](UserKind.md#description) + +#### Defined in + +lib/types/UserKind.ts:3 + +___ + +### followingArtistsAndLabelsCount + +• **followingArtistsAndLabelsCount**: `number` + +#### Defined in + +lib/types/Fan.ts:8 + +___ + +### followingGenresCount + +• **followingGenresCount**: `number` + +#### Defined in + +lib/types/Fan.ts:7 + +___ + +### imageUrl + +• `Optional` **imageUrl**: `string` + +#### Inherited from + +[UserKind](UserKind.md).[imageUrl](UserKind.md#imageurl) + +#### Defined in + +lib/types/UserKind.ts:5 + +___ + +### location + +• `Optional` **location**: `string` + +#### Inherited from + +[UserKind](UserKind.md).[location](UserKind.md#location) + +#### Defined in + +lib/types/UserKind.ts:6 + +___ + +### name + +• **name**: `string` + +#### Inherited from + +[UserKind](UserKind.md).[name](UserKind.md#name) + +#### Defined in + +lib/types/UserKind.ts:2 + +___ + +### type + +• **type**: ``"fan"`` + +#### Defined in + +lib/types/Fan.ts:4 + +___ + +### url + +• `Optional` **url**: `string` + +#### Inherited from + +[UserKind](UserKind.md).[url](UserKind.md#url) + +#### Defined in + +lib/types/UserKind.ts:4 + +___ + +### username + +• **username**: `string` + +#### Defined in + +lib/types/Fan.ts:5 + +___ + +### websiteUrl + +• **websiteUrl**: `string` + +#### Defined in + +lib/types/Fan.ts:6 + +___ + +### wishlistItemCount + +• **wishlistItemCount**: `number` + +#### Defined in + +lib/types/Fan.ts:9 diff --git a/docs/api/interfaces/FanAPIGetInfoParams.md b/docs/api/interfaces/FanAPIGetInfoParams.md new file mode 100644 index 0000000..5fc51b5 --- /dev/null +++ b/docs/api/interfaces/FanAPIGetInfoParams.md @@ -0,0 +1,30 @@ +[bandcamp-fetch](../README.md) / FanAPIGetInfoParams + +# Interface: FanAPIGetInfoParams + +## Table of contents + +### Properties + +- [imageFormat](FanAPIGetInfoParams.md#imageformat) +- [username](FanAPIGetInfoParams.md#username) + +## Properties + +### imageFormat + +• **imageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +#### Defined in + +lib/fan/FanAPI.ts:21 + +___ + +### username + +• **username**: `string` + +#### Defined in + +lib/fan/FanAPI.ts:20 diff --git a/docs/api/interfaces/FanAPIGetItemsParams.md b/docs/api/interfaces/FanAPIGetItemsParams.md new file mode 100644 index 0000000..40e8445 --- /dev/null +++ b/docs/api/interfaces/FanAPIGetItemsParams.md @@ -0,0 +1,30 @@ +[bandcamp-fetch](../README.md) / FanAPIGetItemsParams + +# Interface: FanAPIGetItemsParams + +## Table of contents + +### Properties + +- [imageFormat](FanAPIGetItemsParams.md#imageformat) +- [target](FanAPIGetItemsParams.md#target) + +## Properties + +### imageFormat + +• `Optional` **imageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +#### Defined in + +lib/fan/FanAPI.ts:26 + +___ + +### target + +• **target**: `string` \| [`FanItemsContinuation`](FanItemsContinuation.md) + +#### Defined in + +lib/fan/FanAPI.ts:25 diff --git a/docs/api/interfaces/FanContinuationItemsResult.md b/docs/api/interfaces/FanContinuationItemsResult.md new file mode 100644 index 0000000..c8ac14d --- /dev/null +++ b/docs/api/interfaces/FanContinuationItemsResult.md @@ -0,0 +1,36 @@ +[bandcamp-fetch](../README.md) / FanContinuationItemsResult + +# Interface: FanContinuationItemsResult + +## Type parameters + +| Name | +| :------ | +| `T` | + +## Table of contents + +### Properties + +- [continuation](FanContinuationItemsResult.md#continuation) +- [items](FanContinuationItemsResult.md#items) + +## Properties + +### continuation + +• **continuation**: ``null`` \| [`FanItemsContinuation`](FanItemsContinuation.md) + +#### Defined in + +lib/fan/FanItemsBaseParser.ts:22 + +___ + +### items + +• **items**: `T`[] + +#### Defined in + +lib/fan/FanItemsBaseParser.ts:21 diff --git a/docs/api/interfaces/FanItemsContinuation.md b/docs/api/interfaces/FanItemsContinuation.md new file mode 100644 index 0000000..9ed973c --- /dev/null +++ b/docs/api/interfaces/FanItemsContinuation.md @@ -0,0 +1,30 @@ +[bandcamp-fetch](../README.md) / FanItemsContinuation + +# Interface: FanItemsContinuation + +## Table of contents + +### Properties + +- [fanId](FanItemsContinuation.md#fanid) +- [token](FanItemsContinuation.md#token) + +## Properties + +### fanId + +• **fanId**: `number` + +#### Defined in + +lib/types/Fan.ts:14 + +___ + +### token + +• **token**: `string` + +#### Defined in + +lib/types/Fan.ts:15 diff --git a/docs/api/interfaces/FanPageItemsResult.md b/docs/api/interfaces/FanPageItemsResult.md new file mode 100644 index 0000000..baacf0d --- /dev/null +++ b/docs/api/interfaces/FanPageItemsResult.md @@ -0,0 +1,47 @@ +[bandcamp-fetch](../README.md) / FanPageItemsResult + +# Interface: FanPageItemsResult + +## Type parameters + +| Name | +| :------ | +| `T` | + +## Table of contents + +### Properties + +- [continuation](FanPageItemsResult.md#continuation) +- [items](FanPageItemsResult.md#items) +- [total](FanPageItemsResult.md#total) + +## Properties + +### continuation + +• **continuation**: ``null`` \| [`FanItemsContinuation`](FanItemsContinuation.md) + +#### Defined in + +lib/fan/FanItemsBaseParser.ts:17 + +___ + +### items + +• **items**: `T`[] + +#### Defined in + +lib/fan/FanItemsBaseParser.ts:15 + +___ + +### total + +• **total**: `number` + +#### Defined in + +lib/fan/FanItemsBaseParser.ts:16 diff --git a/docs/api/interfaces/ImageConstants.md b/docs/api/interfaces/ImageConstants.md new file mode 100644 index 0000000..9856beb --- /dev/null +++ b/docs/api/interfaces/ImageConstants.md @@ -0,0 +1,30 @@ +[bandcamp-fetch](../README.md) / ImageConstants + +# Interface: ImageConstants + +## Table of contents + +### Properties + +- [baseUrl](ImageConstants.md#baseurl) +- [formats](ImageConstants.md#formats) + +## Properties + +### baseUrl + +• **baseUrl**: `string` + +#### Defined in + +lib/types/Image.ts:10 + +___ + +### formats + +• **formats**: [`ImageFormat`](ImageFormat.md)[] + +#### Defined in + +lib/types/Image.ts:11 diff --git a/docs/api/interfaces/ImageFormat.md b/docs/api/interfaces/ImageFormat.md new file mode 100644 index 0000000..2593333 --- /dev/null +++ b/docs/api/interfaces/ImageFormat.md @@ -0,0 +1,63 @@ +[bandcamp-fetch](../README.md) / ImageFormat + +# Interface: ImageFormat + +## Table of contents + +### Properties + +- [fileFormat](ImageFormat.md#fileformat) +- [height](ImageFormat.md#height) +- [id](ImageFormat.md#id) +- [name](ImageFormat.md#name) +- [width](ImageFormat.md#width) + +## Properties + +### fileFormat + +• **fileFormat**: `string` + +#### Defined in + +lib/types/Image.ts:6 + +___ + +### height + +• **height**: `number` + +#### Defined in + +lib/types/Image.ts:5 + +___ + +### id + +• **id**: `number` + +#### Defined in + +lib/types/Image.ts:2 + +___ + +### name + +• **name**: `string` + +#### Defined in + +lib/types/Image.ts:3 + +___ + +### width + +• **width**: `number` + +#### Defined in + +lib/types/Image.ts:4 diff --git a/docs/api/interfaces/Label.md b/docs/api/interfaces/Label.md new file mode 100644 index 0000000..438dced --- /dev/null +++ b/docs/api/interfaces/Label.md @@ -0,0 +1,111 @@ +[bandcamp-fetch](../README.md) / Label + +# Interface: Label + +## Hierarchy + +- [`UserKind`](UserKind.md) + + ↳ **`Label`** + +## Table of contents + +### Properties + +- [description](Label.md#description) +- [imageUrl](Label.md#imageurl) +- [labelId](Label.md#labelid) +- [location](Label.md#location) +- [name](Label.md#name) +- [type](Label.md#type) +- [url](Label.md#url) + +## Properties + +### description + +• `Optional` **description**: `string` + +#### Inherited from + +[UserKind](UserKind.md).[description](UserKind.md#description) + +#### Defined in + +lib/types/UserKind.ts:3 + +___ + +### imageUrl + +• `Optional` **imageUrl**: `string` + +#### Inherited from + +[UserKind](UserKind.md).[imageUrl](UserKind.md#imageurl) + +#### Defined in + +lib/types/UserKind.ts:5 + +___ + +### labelId + +• `Optional` **labelId**: `number` + +#### Defined in + +lib/types/Label.ts:6 + +___ + +### location + +• `Optional` **location**: `string` + +#### Inherited from + +[UserKind](UserKind.md).[location](UserKind.md#location) + +#### Defined in + +lib/types/UserKind.ts:6 + +___ + +### name + +• **name**: `string` + +#### Inherited from + +[UserKind](UserKind.md).[name](UserKind.md#name) + +#### Defined in + +lib/types/UserKind.ts:2 + +___ + +### type + +• **type**: ``"label"`` + +#### Defined in + +lib/types/Label.ts:5 + +___ + +### url + +• `Optional` **url**: `string` + +#### Inherited from + +[UserKind](UserKind.md).[url](UserKind.md#url) + +#### Defined in + +lib/types/UserKind.ts:4 diff --git a/docs/api/interfaces/MediaKind.md b/docs/api/interfaces/MediaKind.md new file mode 100644 index 0000000..96ec498 --- /dev/null +++ b/docs/api/interfaces/MediaKind.md @@ -0,0 +1,111 @@ +[bandcamp-fetch](../README.md) / MediaKind + +# Interface: MediaKind + +## Hierarchy + +- **`MediaKind`** + + ↳ [`Album`](Album.md) + + ↳ [`Track`](Track.md) + +## Table of contents + +### Properties + +- [artist](MediaKind.md#artist) +- [imageUrl](MediaKind.md#imageurl) +- [label](MediaKind.md#label) +- [name](MediaKind.md#name) +- [publisher](MediaKind.md#publisher) +- [raw](MediaKind.md#raw) +- [releaseDate](MediaKind.md#releasedate) +- [url](MediaKind.md#url) + +## Properties + +### artist + +• `Optional` **artist**: `Omit`<[`Artist`](Artist.md), ``"type"``\> + +#### Defined in + +lib/types/MediaKind.ts:10 + +___ + +### imageUrl + +• `Optional` **imageUrl**: `string` + +#### Defined in + +lib/types/MediaKind.ts:8 + +___ + +### label + +• `Optional` **label**: `Omit`<[`Label`](Label.md), ``"type"``\> + +#### Defined in + +lib/types/MediaKind.ts:11 + +___ + +### name + +• **name**: `string` + +#### Defined in + +lib/types/MediaKind.ts:6 + +___ + +### publisher + +• `Optional` **publisher**: [`UserKind`](UserKind.md) + +#### Defined in + +lib/types/MediaKind.ts:12 + +___ + +### raw + +• `Optional` **raw**: `Object` + +#### Type declaration + +| Name | Type | +| :------ | :------ | +| `basic` | `string` | +| `extra` | `string` | + +#### Defined in + +lib/types/MediaKind.ts:13 + +___ + +### releaseDate + +• `Optional` **releaseDate**: `string` + +#### Defined in + +lib/types/MediaKind.ts:9 + +___ + +### url + +• `Optional` **url**: `string` + +#### Defined in + +lib/types/MediaKind.ts:7 diff --git a/docs/api/interfaces/NameValuePair.md b/docs/api/interfaces/NameValuePair.md new file mode 100644 index 0000000..7e7bd97 --- /dev/null +++ b/docs/api/interfaces/NameValuePair.md @@ -0,0 +1,42 @@ +[bandcamp-fetch](../README.md) / NameValuePair + +# Interface: NameValuePair + +## Type parameters + +| Name | +| :------ | +| `T` | + +## Hierarchy + +- **`NameValuePair`** + + ↳ [`FilterOption`](ReleasesByTag.FilterOption.md) + +## Table of contents + +### Properties + +- [name](NameValuePair.md#name) +- [value](NameValuePair.md#value) + +## Properties + +### name + +• **name**: `string` + +#### Defined in + +lib/utils/NameValuePair.ts:2 + +___ + +### value + +• **value**: `T` + +#### Defined in + +lib/utils/NameValuePair.ts:3 diff --git a/docs/api/interfaces/ReleasesByTag-1.md b/docs/api/interfaces/ReleasesByTag-1.md new file mode 100644 index 0000000..a6b5523 --- /dev/null +++ b/docs/api/interfaces/ReleasesByTag-1.md @@ -0,0 +1,41 @@ +[bandcamp-fetch](../README.md) / ReleasesByTag + +# Interface: ReleasesByTag + +## Table of contents + +### Properties + +- [filters](ReleasesByTag-1.md#filters) +- [hasMore](ReleasesByTag-1.md#hasmore) +- [items](ReleasesByTag-1.md#items) + +## Properties + +### filters + +• **filters**: `Record`<`string`, `string` \| `number` \| (`string` \| `number`)[]\> + +#### Defined in + +lib/types/Tag.ts:47 + +___ + +### hasMore + +• **hasMore**: `boolean` + +#### Defined in + +lib/types/Tag.ts:46 + +___ + +### items + +• **items**: ([`Track`](Track.md) \| [`Album`](Album.md))[] + +#### Defined in + +lib/types/Tag.ts:45 diff --git a/docs/api/interfaces/ReleasesByTag.Filter.md b/docs/api/interfaces/ReleasesByTag.Filter.md new file mode 100644 index 0000000..9963377 --- /dev/null +++ b/docs/api/interfaces/ReleasesByTag.Filter.md @@ -0,0 +1,32 @@ +[bandcamp-fetch](../README.md) / [ReleasesByTag](../modules/ReleasesByTag.md) / Filter + +# Interface: Filter + +[ReleasesByTag](../modules/ReleasesByTag.md).Filter + +## Table of contents + +### Properties + +- [name](ReleasesByTag.Filter.md#name) +- [options](ReleasesByTag.Filter.md#options) + +## Properties + +### name + +• **name**: `string` + +#### Defined in + +lib/types/Tag.ts:39 + +___ + +### options + +• **options**: [`FilterOption`](ReleasesByTag.FilterOption.md)[] + +#### Defined in + +lib/types/Tag.ts:40 diff --git a/docs/api/interfaces/ReleasesByTag.FilterOption.md b/docs/api/interfaces/ReleasesByTag.FilterOption.md new file mode 100644 index 0000000..5e6128a --- /dev/null +++ b/docs/api/interfaces/ReleasesByTag.FilterOption.md @@ -0,0 +1,68 @@ +[bandcamp-fetch](../README.md) / [ReleasesByTag](../modules/ReleasesByTag.md) / FilterOption + +# Interface: FilterOption + +[ReleasesByTag](../modules/ReleasesByTag.md).FilterOption + +## Hierarchy + +- [`NameValuePair`](NameValuePair.md)<`string` \| `number`\> + + ↳ **`FilterOption`** + +## Table of contents + +### Properties + +- [default](ReleasesByTag.FilterOption.md#default) +- [name](ReleasesByTag.FilterOption.md#name) +- [selected](ReleasesByTag.FilterOption.md#selected) +- [value](ReleasesByTag.FilterOption.md#value) + +## Properties + +### default + +• `Optional` **default**: `boolean` + +#### Defined in + +lib/types/Tag.ts:34 + +___ + +### name + +• **name**: `string` + +#### Inherited from + +[NameValuePair](NameValuePair.md).[name](NameValuePair.md#name) + +#### Defined in + +lib/utils/NameValuePair.ts:2 + +___ + +### selected + +• `Optional` **selected**: `boolean` + +#### Defined in + +lib/types/Tag.ts:35 + +___ + +### value + +• **value**: `string` \| `number` + +#### Inherited from + +[NameValuePair](NameValuePair.md).[value](NameValuePair.md#value) + +#### Defined in + +lib/utils/NameValuePair.ts:3 diff --git a/docs/api/interfaces/ReleasesByTag.FilterValueNames.md b/docs/api/interfaces/ReleasesByTag.FilterValueNames.md new file mode 100644 index 0000000..4439a69 --- /dev/null +++ b/docs/api/interfaces/ReleasesByTag.FilterValueNames.md @@ -0,0 +1,9 @@ +[bandcamp-fetch](../README.md) / [ReleasesByTag](../modules/ReleasesByTag.md) / FilterValueNames + +# Interface: FilterValueNames + +[ReleasesByTag](../modules/ReleasesByTag.md).FilterValueNames + +## Indexable + +▪ [k: `string`]: `Record`<`string`, `string` \| `number`\> diff --git a/docs/api/interfaces/SearchAPISearchParams.md b/docs/api/interfaces/SearchAPISearchParams.md new file mode 100644 index 0000000..fa0c0b0 --- /dev/null +++ b/docs/api/interfaces/SearchAPISearchParams.md @@ -0,0 +1,52 @@ +[bandcamp-fetch](../README.md) / SearchAPISearchParams + +# Interface: SearchAPISearchParams + +## Table of contents + +### Properties + +- [albumImageFormat](SearchAPISearchParams.md#albumimageformat) +- [artistImageFormat](SearchAPISearchParams.md#artistimageformat) +- [page](SearchAPISearchParams.md#page) +- [query](SearchAPISearchParams.md#query) + +## Properties + +### albumImageFormat + +• `Optional` **albumImageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +#### Defined in + +lib/search/SearchAPI.ts:21 + +___ + +### artistImageFormat + +• `Optional` **artistImageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +#### Defined in + +lib/search/SearchAPI.ts:22 + +___ + +### page + +• `Optional` **page**: `number` + +#### Defined in + +lib/search/SearchAPI.ts:20 + +___ + +### query + +• **query**: `string` + +#### Defined in + +lib/search/SearchAPI.ts:19 diff --git a/docs/api/interfaces/SearchResultAlbum.md b/docs/api/interfaces/SearchResultAlbum.md new file mode 100644 index 0000000..475a46c --- /dev/null +++ b/docs/api/interfaces/SearchResultAlbum.md @@ -0,0 +1,129 @@ +[bandcamp-fetch](../README.md) / SearchResultAlbum + +# Interface: SearchResultAlbum + +## Hierarchy + +- [`SearchResultItem`](SearchResultItem.md) + + ↳ **`SearchResultAlbum`** + +## Table of contents + +### Properties + +- [artist](SearchResultAlbum.md#artist) +- [duration](SearchResultAlbum.md#duration) +- [imageUrl](SearchResultAlbum.md#imageurl) +- [name](SearchResultAlbum.md#name) +- [numTracks](SearchResultAlbum.md#numtracks) +- [releaseDate](SearchResultAlbum.md#releasedate) +- [tags](SearchResultAlbum.md#tags) +- [type](SearchResultAlbum.md#type) +- [url](SearchResultAlbum.md#url) + +## Properties + +### artist + +• `Optional` **artist**: `string` + +#### Defined in + +lib/types/Search.ts:27 + +___ + +### duration + +• `Optional` **duration**: `number` + +#### Defined in + +lib/types/Search.ts:29 + +___ + +### imageUrl + +• `Optional` **imageUrl**: `string` + +#### Inherited from + +[SearchResultItem](SearchResultItem.md).[imageUrl](SearchResultItem.md#imageurl) + +#### Defined in + +lib/types/Search.ts:10 + +___ + +### name + +• **name**: `string` + +#### Inherited from + +[SearchResultItem](SearchResultItem.md).[name](SearchResultItem.md#name) + +#### Defined in + +lib/types/Search.ts:8 + +___ + +### numTracks + +• `Optional` **numTracks**: `number` + +#### Defined in + +lib/types/Search.ts:28 + +___ + +### releaseDate + +• `Optional` **releaseDate**: `string` + +#### Defined in + +lib/types/Search.ts:30 + +___ + +### tags + +• `Optional` **tags**: `string` + +#### Defined in + +lib/types/Search.ts:31 + +___ + +### type + +• **type**: ``"album"`` + +#### Overrides + +[SearchResultItem](SearchResultItem.md).[type](SearchResultItem.md#type) + +#### Defined in + +lib/types/Search.ts:26 + +___ + +### url + +• **url**: `string` + +#### Inherited from + +[SearchResultItem](SearchResultItem.md).[url](SearchResultItem.md#url) + +#### Defined in + +lib/types/Search.ts:9 diff --git a/docs/api/interfaces/SearchResultArtist.md b/docs/api/interfaces/SearchResultArtist.md new file mode 100644 index 0000000..9af9ee4 --- /dev/null +++ b/docs/api/interfaces/SearchResultArtist.md @@ -0,0 +1,107 @@ +[bandcamp-fetch](../README.md) / SearchResultArtist + +# Interface: SearchResultArtist + +## Hierarchy + +- [`SearchResultItem`](SearchResultItem.md) + + ↳ **`SearchResultArtist`** + +## Table of contents + +### Properties + +- [genre](SearchResultArtist.md#genre) +- [imageUrl](SearchResultArtist.md#imageurl) +- [location](SearchResultArtist.md#location) +- [name](SearchResultArtist.md#name) +- [tags](SearchResultArtist.md#tags) +- [type](SearchResultArtist.md#type) +- [url](SearchResultArtist.md#url) + +## Properties + +### genre + +• `Optional` **genre**: `string` + +#### Defined in + +lib/types/Search.ts:16 + +___ + +### imageUrl + +• `Optional` **imageUrl**: `string` + +#### Inherited from + +[SearchResultItem](SearchResultItem.md).[imageUrl](SearchResultItem.md#imageurl) + +#### Defined in + +lib/types/Search.ts:10 + +___ + +### location + +• `Optional` **location**: `string` + +#### Defined in + +lib/types/Search.ts:15 + +___ + +### name + +• **name**: `string` + +#### Inherited from + +[SearchResultItem](SearchResultItem.md).[name](SearchResultItem.md#name) + +#### Defined in + +lib/types/Search.ts:8 + +___ + +### tags + +• `Optional` **tags**: `string` + +#### Defined in + +lib/types/Search.ts:17 + +___ + +### type + +• **type**: ``"artist"`` + +#### Overrides + +[SearchResultItem](SearchResultItem.md).[type](SearchResultItem.md#type) + +#### Defined in + +lib/types/Search.ts:14 + +___ + +### url + +• **url**: `string` + +#### Inherited from + +[SearchResultItem](SearchResultItem.md).[url](SearchResultItem.md#url) + +#### Defined in + +lib/types/Search.ts:9 diff --git a/docs/api/interfaces/SearchResultFan.md b/docs/api/interfaces/SearchResultFan.md new file mode 100644 index 0000000..f8fd2b1 --- /dev/null +++ b/docs/api/interfaces/SearchResultFan.md @@ -0,0 +1,85 @@ +[bandcamp-fetch](../README.md) / SearchResultFan + +# Interface: SearchResultFan + +## Hierarchy + +- [`SearchResultItem`](SearchResultItem.md) + + ↳ **`SearchResultFan`** + +## Table of contents + +### Properties + +- [genre](SearchResultFan.md#genre) +- [imageUrl](SearchResultFan.md#imageurl) +- [name](SearchResultFan.md#name) +- [type](SearchResultFan.md#type) +- [url](SearchResultFan.md#url) + +## Properties + +### genre + +• `Optional` **genre**: `string` + +#### Defined in + +lib/types/Search.ts:44 + +___ + +### imageUrl + +• `Optional` **imageUrl**: `string` + +#### Inherited from + +[SearchResultItem](SearchResultItem.md).[imageUrl](SearchResultItem.md#imageurl) + +#### Defined in + +lib/types/Search.ts:10 + +___ + +### name + +• **name**: `string` + +#### Inherited from + +[SearchResultItem](SearchResultItem.md).[name](SearchResultItem.md#name) + +#### Defined in + +lib/types/Search.ts:8 + +___ + +### type + +• **type**: ``"fan"`` + +#### Overrides + +[SearchResultItem](SearchResultItem.md).[type](SearchResultItem.md#type) + +#### Defined in + +lib/types/Search.ts:43 + +___ + +### url + +• **url**: `string` + +#### Inherited from + +[SearchResultItem](SearchResultItem.md).[url](SearchResultItem.md#url) + +#### Defined in + +lib/types/Search.ts:9 diff --git a/docs/api/interfaces/SearchResultItem.md b/docs/api/interfaces/SearchResultItem.md new file mode 100644 index 0000000..8a116ac --- /dev/null +++ b/docs/api/interfaces/SearchResultItem.md @@ -0,0 +1,66 @@ +[bandcamp-fetch](../README.md) / SearchResultItem + +# Interface: SearchResultItem + +## Hierarchy + +- **`SearchResultItem`** + + ↳ [`SearchResultArtist`](SearchResultArtist.md) + + ↳ [`SearchResultLabel`](SearchResultLabel.md) + + ↳ [`SearchResultAlbum`](SearchResultAlbum.md) + + ↳ [`SearchResultTrack`](SearchResultTrack.md) + + ↳ [`SearchResultFan`](SearchResultFan.md) + +## Table of contents + +### Properties + +- [imageUrl](SearchResultItem.md#imageurl) +- [name](SearchResultItem.md#name) +- [type](SearchResultItem.md#type) +- [url](SearchResultItem.md#url) + +## Properties + +### imageUrl + +• `Optional` **imageUrl**: `string` + +#### Defined in + +lib/types/Search.ts:10 + +___ + +### name + +• **name**: `string` + +#### Defined in + +lib/types/Search.ts:8 + +___ + +### type + +• **type**: ``"track"`` \| ``"label"`` \| ``"artist"`` \| ``"album"`` \| ``"fan"`` + +#### Defined in + +lib/types/Search.ts:7 + +___ + +### url + +• **url**: `string` + +#### Defined in + +lib/types/Search.ts:9 diff --git a/docs/api/interfaces/SearchResultLabel.md b/docs/api/interfaces/SearchResultLabel.md new file mode 100644 index 0000000..7079791 --- /dev/null +++ b/docs/api/interfaces/SearchResultLabel.md @@ -0,0 +1,85 @@ +[bandcamp-fetch](../README.md) / SearchResultLabel + +# Interface: SearchResultLabel + +## Hierarchy + +- [`SearchResultItem`](SearchResultItem.md) + + ↳ **`SearchResultLabel`** + +## Table of contents + +### Properties + +- [imageUrl](SearchResultLabel.md#imageurl) +- [location](SearchResultLabel.md#location) +- [name](SearchResultLabel.md#name) +- [type](SearchResultLabel.md#type) +- [url](SearchResultLabel.md#url) + +## Properties + +### imageUrl + +• `Optional` **imageUrl**: `string` + +#### Inherited from + +[SearchResultItem](SearchResultItem.md).[imageUrl](SearchResultItem.md#imageurl) + +#### Defined in + +lib/types/Search.ts:10 + +___ + +### location + +• `Optional` **location**: `string` + +#### Defined in + +lib/types/Search.ts:22 + +___ + +### name + +• **name**: `string` + +#### Inherited from + +[SearchResultItem](SearchResultItem.md).[name](SearchResultItem.md#name) + +#### Defined in + +lib/types/Search.ts:8 + +___ + +### type + +• **type**: ``"label"`` + +#### Overrides + +[SearchResultItem](SearchResultItem.md).[type](SearchResultItem.md#type) + +#### Defined in + +lib/types/Search.ts:21 + +___ + +### url + +• **url**: `string` + +#### Inherited from + +[SearchResultItem](SearchResultItem.md).[url](SearchResultItem.md#url) + +#### Defined in + +lib/types/Search.ts:9 diff --git a/docs/api/interfaces/SearchResultTrack.md b/docs/api/interfaces/SearchResultTrack.md new file mode 100644 index 0000000..3ec017f --- /dev/null +++ b/docs/api/interfaces/SearchResultTrack.md @@ -0,0 +1,118 @@ +[bandcamp-fetch](../README.md) / SearchResultTrack + +# Interface: SearchResultTrack + +## Hierarchy + +- [`SearchResultItem`](SearchResultItem.md) + + ↳ **`SearchResultTrack`** + +## Table of contents + +### Properties + +- [album](SearchResultTrack.md#album) +- [artist](SearchResultTrack.md#artist) +- [imageUrl](SearchResultTrack.md#imageurl) +- [name](SearchResultTrack.md#name) +- [releaseDate](SearchResultTrack.md#releasedate) +- [tags](SearchResultTrack.md#tags) +- [type](SearchResultTrack.md#type) +- [url](SearchResultTrack.md#url) + +## Properties + +### album + +• `Optional` **album**: `string` + +#### Defined in + +lib/types/Search.ts:37 + +___ + +### artist + +• `Optional` **artist**: `string` + +#### Defined in + +lib/types/Search.ts:36 + +___ + +### imageUrl + +• `Optional` **imageUrl**: `string` + +#### Inherited from + +[SearchResultItem](SearchResultItem.md).[imageUrl](SearchResultItem.md#imageurl) + +#### Defined in + +lib/types/Search.ts:10 + +___ + +### name + +• **name**: `string` + +#### Inherited from + +[SearchResultItem](SearchResultItem.md).[name](SearchResultItem.md#name) + +#### Defined in + +lib/types/Search.ts:8 + +___ + +### releaseDate + +• `Optional` **releaseDate**: `string` + +#### Defined in + +lib/types/Search.ts:38 + +___ + +### tags + +• `Optional` **tags**: `string` + +#### Defined in + +lib/types/Search.ts:39 + +___ + +### type + +• **type**: ``"track"`` + +#### Overrides + +[SearchResultItem](SearchResultItem.md).[type](SearchResultItem.md#type) + +#### Defined in + +lib/types/Search.ts:35 + +___ + +### url + +• **url**: `string` + +#### Inherited from + +[SearchResultItem](SearchResultItem.md).[url](SearchResultItem.md#url) + +#### Defined in + +lib/types/Search.ts:9 diff --git a/docs/api/interfaces/SearchResults.md b/docs/api/interfaces/SearchResults.md new file mode 100644 index 0000000..f9ee87e --- /dev/null +++ b/docs/api/interfaces/SearchResults.md @@ -0,0 +1,36 @@ +[bandcamp-fetch](../README.md) / SearchResults + +# Interface: SearchResults + +## Type parameters + +| Name | Type | +| :------ | :------ | +| `T` | extends [`SearchResultItem`](SearchResultItem.md) | + +## Table of contents + +### Properties + +- [items](SearchResults.md#items) +- [totalPages](SearchResults.md#totalpages) + +## Properties + +### items + +• **items**: `T`[] + +#### Defined in + +lib/types/Search.ts:2 + +___ + +### totalPages + +• **totalPages**: `number` + +#### Defined in + +lib/types/Search.ts:3 diff --git a/docs/api/interfaces/Show.md b/docs/api/interfaces/Show.md new file mode 100644 index 0000000..615dfb1 --- /dev/null +++ b/docs/api/interfaces/Show.md @@ -0,0 +1,158 @@ +[bandcamp-fetch](../README.md) / Show + +# Interface: Show + +## Table of contents + +### Properties + +- [description](Show.md#description) +- [duration](Show.md#duration) +- [imageCaption](Show.md#imagecaption) +- [imageUrl](Show.md#imageurl) +- [name](Show.md#name) +- [publishedDate](Show.md#publisheddate) +- [screenImageUrl](Show.md#screenimageurl) +- [shortDescription](Show.md#shortdescription) +- [streamUrl](Show.md#streamurl) +- [subtitle](Show.md#subtitle) +- [tracks](Show.md#tracks) +- [type](Show.md#type) +- [url](Show.md#url) + +## Properties + +### description + +• **description**: `string` + +#### Defined in + +lib/types/Show.ts:8 + +___ + +### duration + +• `Optional` **duration**: `number` + +#### Defined in + +lib/types/Show.ts:10 + +___ + +### imageCaption + +• **imageCaption**: `string` + +#### Defined in + +lib/types/Show.ts:16 + +___ + +### imageUrl + +• `Optional` **imageUrl**: `string` + +#### Defined in + +lib/types/Show.ts:18 + +___ + +### name + +• **name**: `string` + +#### Defined in + +lib/types/Show.ts:5 + +___ + +### publishedDate + +• **publishedDate**: `string` + +#### Defined in + +lib/types/Show.ts:7 + +___ + +### screenImageUrl + +• **screenImageUrl**: `string` + +#### Defined in + +lib/types/Show.ts:19 + +___ + +### shortDescription + +• `Optional` **shortDescription**: `string` + +#### Defined in + +lib/types/Show.ts:9 + +___ + +### streamUrl + +• `Optional` **streamUrl**: `Object` + +#### Type declaration + +| Name | Type | +| :------ | :------ | +| `mp3-128?` | `string` | +| `opus-lo?` | `string` | + +#### Defined in + +lib/types/Show.ts:11 + +___ + +### subtitle + +• **subtitle**: `string` + +#### Defined in + +lib/types/Show.ts:17 + +___ + +### tracks + +• `Optional` **tracks**: `Omit`<[`Track`](Track.md), ``"type"``\>[] + +#### Defined in + +lib/types/Show.ts:15 + +___ + +### type + +• **type**: ``"show"`` + +#### Defined in + +lib/types/Show.ts:4 + +___ + +### url + +• **url**: `string` + +#### Defined in + +lib/types/Show.ts:6 diff --git a/docs/api/interfaces/ShowAPIGetShowParams.md b/docs/api/interfaces/ShowAPIGetShowParams.md new file mode 100644 index 0000000..c6d4c93 --- /dev/null +++ b/docs/api/interfaces/ShowAPIGetShowParams.md @@ -0,0 +1,52 @@ +[bandcamp-fetch](../README.md) / ShowAPIGetShowParams + +# Interface: ShowAPIGetShowParams + +## Table of contents + +### Properties + +- [albumImageFormat](ShowAPIGetShowParams.md#albumimageformat) +- [artistImageFormat](ShowAPIGetShowParams.md#artistimageformat) +- [showImageFormat](ShowAPIGetShowParams.md#showimageformat) +- [showUrl](ShowAPIGetShowParams.md#showurl) + +## Properties + +### albumImageFormat + +• `Optional` **albumImageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +#### Defined in + +lib/show/ShowAPI.ts:12 + +___ + +### artistImageFormat + +• `Optional` **artistImageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +#### Defined in + +lib/show/ShowAPI.ts:13 + +___ + +### showImageFormat + +• `Optional` **showImageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +#### Defined in + +lib/show/ShowAPI.ts:14 + +___ + +### showUrl + +• **showUrl**: `string` + +#### Defined in + +lib/show/ShowAPI.ts:11 diff --git a/docs/api/interfaces/ShowAPIListParams.md b/docs/api/interfaces/ShowAPIListParams.md new file mode 100644 index 0000000..d762bcd --- /dev/null +++ b/docs/api/interfaces/ShowAPIListParams.md @@ -0,0 +1,19 @@ +[bandcamp-fetch](../README.md) / ShowAPIListParams + +# Interface: ShowAPIListParams + +## Table of contents + +### Properties + +- [imageFormat](ShowAPIListParams.md#imageformat) + +## Properties + +### imageFormat + +• `Optional` **imageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +#### Defined in + +lib/show/ShowAPI.ts:18 diff --git a/docs/api/interfaces/Tag.md b/docs/api/interfaces/Tag.md new file mode 100644 index 0000000..5997761 --- /dev/null +++ b/docs/api/interfaces/Tag.md @@ -0,0 +1,85 @@ +[bandcamp-fetch](../README.md) / Tag + +# Interface: Tag + +## Table of contents + +### Properties + +- [imageUrls](Tag.md#imageurls) +- [isLocation](Tag.md#islocation) +- [name](Tag.md#name) +- [related](Tag.md#related) +- [type](Tag.md#type) +- [url](Tag.md#url) +- [value](Tag.md#value) + +## Properties + +### imageUrls + +• `Optional` **imageUrls**: `string`[] + +#### Defined in + +lib/types/Tag.ts:12 + +___ + +### isLocation + +• `Optional` **isLocation**: `boolean` + +#### Defined in + +lib/types/Tag.ts:11 + +___ + +### name + +• **name**: `string` + +#### Defined in + +lib/types/Tag.ts:7 + +___ + +### related + +• `Optional` **related**: [`Tag`](Tag.md)[] + +#### Defined in + +lib/types/Tag.ts:10 + +___ + +### type + +• **type**: `string` + +#### Defined in + +lib/types/Tag.ts:6 + +___ + +### url + +• **url**: `string` + +#### Defined in + +lib/types/Tag.ts:8 + +___ + +### value + +• `Optional` **value**: `string` + +#### Defined in + +lib/types/Tag.ts:9 diff --git a/docs/api/interfaces/TagAPIGetAlbumHighlightsParams.md b/docs/api/interfaces/TagAPIGetAlbumHighlightsParams.md new file mode 100644 index 0000000..0e3ee20 --- /dev/null +++ b/docs/api/interfaces/TagAPIGetAlbumHighlightsParams.md @@ -0,0 +1,30 @@ +[bandcamp-fetch](../README.md) / TagAPIGetAlbumHighlightsParams + +# Interface: TagAPIGetAlbumHighlightsParams + +## Table of contents + +### Properties + +- [imageFormat](TagAPIGetAlbumHighlightsParams.md#imageformat) +- [tagUrl](TagAPIGetAlbumHighlightsParams.md#tagurl) + +## Properties + +### imageFormat + +• `Optional` **imageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +#### Defined in + +lib/tag/TagAPI.ts:15 + +___ + +### tagUrl + +• **tagUrl**: `string` + +#### Defined in + +lib/tag/TagAPI.ts:14 diff --git a/docs/api/interfaces/TagAPIGetReleasesParams.md b/docs/api/interfaces/TagAPIGetReleasesParams.md new file mode 100644 index 0000000..74615f3 --- /dev/null +++ b/docs/api/interfaces/TagAPIGetReleasesParams.md @@ -0,0 +1,63 @@ +[bandcamp-fetch](../README.md) / TagAPIGetReleasesParams + +# Interface: TagAPIGetReleasesParams + +## Table of contents + +### Properties + +- [filters](TagAPIGetReleasesParams.md#filters) +- [imageFormat](TagAPIGetReleasesParams.md#imageformat) +- [page](TagAPIGetReleasesParams.md#page) +- [tagUrl](TagAPIGetReleasesParams.md#tagurl) +- [useHardcodedDefaultFilters](TagAPIGetReleasesParams.md#usehardcodeddefaultfilters) + +## Properties + +### filters + +• `Optional` **filters**: `Record`<`string`, `string` \| `number` \| (`string` \| `number`)[]\> + +#### Defined in + +lib/tag/TagAPI.ts:22 + +___ + +### imageFormat + +• `Optional` **imageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +#### Defined in + +lib/tag/TagAPI.ts:20 + +___ + +### page + +• `Optional` **page**: `number` + +#### Defined in + +lib/tag/TagAPI.ts:23 + +___ + +### tagUrl + +• **tagUrl**: `string` + +#### Defined in + +lib/tag/TagAPI.ts:19 + +___ + +### useHardcodedDefaultFilters + +• `Optional` **useHardcodedDefaultFilters**: `boolean` + +#### Defined in + +lib/tag/TagAPI.ts:21 diff --git a/docs/api/interfaces/TagList.md b/docs/api/interfaces/TagList.md new file mode 100644 index 0000000..fb4763d --- /dev/null +++ b/docs/api/interfaces/TagList.md @@ -0,0 +1,30 @@ +[bandcamp-fetch](../README.md) / TagList + +# Interface: TagList + +## Table of contents + +### Properties + +- [locations](TagList.md#locations) +- [tags](TagList.md#tags) + +## Properties + +### locations + +• **locations**: `Omit`<[`Tag`](Tag.md), ``"type"``\>[] + +#### Defined in + +lib/types/Tag.ts:17 + +___ + +### tags + +• **tags**: `Omit`<[`Tag`](Tag.md), ``"type"``\>[] + +#### Defined in + +lib/types/Tag.ts:16 diff --git a/docs/api/interfaces/Track.md b/docs/api/interfaces/Track.md new file mode 100644 index 0000000..7b2bb49 --- /dev/null +++ b/docs/api/interfaces/Track.md @@ -0,0 +1,207 @@ +[bandcamp-fetch](../README.md) / Track + +# Interface: Track + +## Hierarchy + +- [`MediaKind`](MediaKind.md) + + ↳ **`Track`** + +## Table of contents + +### Properties + +- [album](Track.md#album) +- [artist](Track.md#artist) +- [duration](Track.md#duration) +- [imageUrl](Track.md#imageurl) +- [label](Track.md#label) +- [name](Track.md#name) +- [position](Track.md#position) +- [publisher](Track.md#publisher) +- [raw](Track.md#raw) +- [releaseDate](Track.md#releasedate) +- [seekPosition](Track.md#seekposition) +- [streamUrl](Track.md#streamurl) +- [type](Track.md#type) +- [url](Track.md#url) + +## Properties + +### album + +• `Optional` **album**: `Omit`<[`Album`](Album.md), ``"type"``\> + +#### Defined in + +lib/types/Track.ts:9 + +___ + +### artist + +• `Optional` **artist**: `Omit`<[`Artist`](Artist.md), ``"type"``\> + +#### Inherited from + +[MediaKind](MediaKind.md).[artist](MediaKind.md#artist) + +#### Defined in + +lib/types/MediaKind.ts:10 + +___ + +### duration + +• `Optional` **duration**: `number` + +#### Defined in + +lib/types/Track.ts:6 + +___ + +### imageUrl + +• `Optional` **imageUrl**: `string` + +#### Inherited from + +[MediaKind](MediaKind.md).[imageUrl](MediaKind.md#imageurl) + +#### Defined in + +lib/types/MediaKind.ts:8 + +___ + +### label + +• `Optional` **label**: `Omit`<[`Label`](Label.md), ``"type"``\> + +#### Inherited from + +[MediaKind](MediaKind.md).[label](MediaKind.md#label) + +#### Defined in + +lib/types/MediaKind.ts:11 + +___ + +### name + +• **name**: `string` + +#### Inherited from + +[MediaKind](MediaKind.md).[name](MediaKind.md#name) + +#### Defined in + +lib/types/MediaKind.ts:6 + +___ + +### position + +• `Optional` **position**: `number` + +#### Defined in + +lib/types/Track.ts:10 + +___ + +### publisher + +• `Optional` **publisher**: [`UserKind`](UserKind.md) + +#### Inherited from + +[MediaKind](MediaKind.md).[publisher](MediaKind.md#publisher) + +#### Defined in + +lib/types/MediaKind.ts:12 + +___ + +### raw + +• `Optional` **raw**: `Object` + +#### Type declaration + +| Name | Type | +| :------ | :------ | +| `basic` | `string` | +| `extra` | `string` | + +#### Inherited from + +[MediaKind](MediaKind.md).[raw](MediaKind.md#raw) + +#### Defined in + +lib/types/MediaKind.ts:13 + +___ + +### releaseDate + +• `Optional` **releaseDate**: `string` + +#### Inherited from + +[MediaKind](MediaKind.md).[releaseDate](MediaKind.md#releasedate) + +#### Defined in + +lib/types/MediaKind.ts:9 + +___ + +### seekPosition + +• `Optional` **seekPosition**: `number` + +#### Defined in + +lib/types/Track.ts:7 + +___ + +### streamUrl + +• `Optional` **streamUrl**: `string` + +#### Defined in + +lib/types/Track.ts:8 + +___ + +### type + +• **type**: ``"track"`` + +#### Defined in + +lib/types/Track.ts:5 + +___ + +### url + +• `Optional` **url**: `string` + +#### Inherited from + +[MediaKind](MediaKind.md).[url](MediaKind.md#url) + +#### Defined in + +lib/types/MediaKind.ts:7 diff --git a/docs/api/interfaces/TrackAPIGetInfoParams.md b/docs/api/interfaces/TrackAPIGetInfoParams.md new file mode 100644 index 0000000..9d66db0 --- /dev/null +++ b/docs/api/interfaces/TrackAPIGetInfoParams.md @@ -0,0 +1,52 @@ +[bandcamp-fetch](../README.md) / TrackAPIGetInfoParams + +# Interface: TrackAPIGetInfoParams + +## Table of contents + +### Properties + +- [albumImageFormat](TrackAPIGetInfoParams.md#albumimageformat) +- [artistImageFormat](TrackAPIGetInfoParams.md#artistimageformat) +- [includeRawData](TrackAPIGetInfoParams.md#includerawdata) +- [trackUrl](TrackAPIGetInfoParams.md#trackurl) + +## Properties + +### albumImageFormat + +• `Optional` **albumImageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +#### Defined in + +lib/track/TrackAPI.ts:10 + +___ + +### artistImageFormat + +• `Optional` **artistImageFormat**: `string` \| `number` \| [`ImageFormat`](ImageFormat.md) + +#### Defined in + +lib/track/TrackAPI.ts:11 + +___ + +### includeRawData + +• `Optional` **includeRawData**: `boolean` + +#### Defined in + +lib/track/TrackAPI.ts:12 + +___ + +### trackUrl + +• **trackUrl**: `string` + +#### Defined in + +lib/track/TrackAPI.ts:9 diff --git a/docs/api/interfaces/UserKind.md b/docs/api/interfaces/UserKind.md new file mode 100644 index 0000000..1f80cfe --- /dev/null +++ b/docs/api/interfaces/UserKind.md @@ -0,0 +1,73 @@ +[bandcamp-fetch](../README.md) / UserKind + +# Interface: UserKind + +## Hierarchy + +- **`UserKind`** + + ↳ [`Artist`](Artist.md) + + ↳ [`Fan`](Fan.md) + + ↳ [`Label`](Label.md) + +## Table of contents + +### Properties + +- [description](UserKind.md#description) +- [imageUrl](UserKind.md#imageurl) +- [location](UserKind.md#location) +- [name](UserKind.md#name) +- [url](UserKind.md#url) + +## Properties + +### description + +• `Optional` **description**: `string` + +#### Defined in + +lib/types/UserKind.ts:3 + +___ + +### imageUrl + +• `Optional` **imageUrl**: `string` + +#### Defined in + +lib/types/UserKind.ts:5 + +___ + +### location + +• `Optional` **location**: `string` + +#### Defined in + +lib/types/UserKind.ts:6 + +___ + +### name + +• **name**: `string` + +#### Defined in + +lib/types/UserKind.ts:2 + +___ + +### url + +• `Optional` **url**: `string` + +#### Defined in + +lib/types/UserKind.ts:4 diff --git a/docs/api/modules/ReleasesByTag.md b/docs/api/modules/ReleasesByTag.md new file mode 100644 index 0000000..fd68728 --- /dev/null +++ b/docs/api/modules/ReleasesByTag.md @@ -0,0 +1,11 @@ +[bandcamp-fetch](../README.md) / ReleasesByTag + +# Namespace: ReleasesByTag + +## Table of contents + +### Interfaces + +- [Filter](../interfaces/ReleasesByTag.Filter.md) +- [FilterOption](../interfaces/ReleasesByTag.FilterOption.md) +- [FilterValueNames](../interfaces/ReleasesByTag.FilterValueNames.md) diff --git a/examples/album/getInfo.ts b/examples/album/getInfo.ts new file mode 100644 index 0000000..eaed14d --- /dev/null +++ b/examples/album/getInfo.ts @@ -0,0 +1,15 @@ +import bcfetch from '../../'; +import util from 'util'; + +const albumUrl = 'https://musique.coeurdepirate.com/album/blonde'; + +const params = { + albumUrl, + albumImageFormat: 'art_app_large', + artistImageFormat: 'bio_featured', + includeRawData: false +}; + +bcfetch.album.getInfo(params).then((results) => { + console.log(util.inspect(results, false, null, false)); +}); diff --git a/examples/album/getInfo_output.txt b/examples/album/getInfo_output.txt new file mode 100644 index 0000000..e7a8285 --- /dev/null +++ b/examples/album/getInfo_output.txt @@ -0,0 +1,139 @@ +{ + type: 'album', + name: 'Blonde', + url: 'https://musique.coeurdepirate.com/album/blonde', + numTracks: 12, + keywords: [ + 'Pop', + 'amour', + 'coeur de pirate', + 'french', + 'french pop', + 'grosse boîte', + 'montreal', + 'piano pop', + 'Montréal' + ], + description: "Après avoir immortalisé tout un pan de son adolescence dans les chansons pop douces-amères d'un premier album homonyme, Coeur de pirate s'attaque aux différentes étapes de la relation amoureuse, d'où le titre de l'album, en référence avant tout, à la copine, à l'amoureuse. L'album a été enregistré à l'été 2011 à l'Hotel2Tango sous la gouverne d'Howard Bilerman, coréalisateur avec Béatrice Martin.", + releaseDate: '07 Nov 2011 00:00:00 GMT', + artist: { name: 'Cœur de pirate', url: 'https://musique.coeurdepirate.com' }, + releases: [ + { + name: 'Blonde', + format: 'DigitalFormat', + description: 'Includes high-quality download in MP3, FLAC and more. Paying supporters also get unlimited streaming via the free Bandcamp app.', + url: 'https://musique.coeurdepirate.com/album/blonde', + imageUrl: 'https://f4.bcbits.com/img/a1328452291_10.jpg' + }, + { + name: 'Blonde - CD', + format: 'CDFormat', + description: "Blonde - Édition régulière comprend le téléchargement gratuit de l'album digital. Veuillez allouer 3 jours ouvrables pour que la version physique vous soit postée.\n" + + 'Pour les commandes hors-Canada, vous devez compter de 2 à 4 semaines pour recevoir votre achat.', + url: 'https://musique.coeurdepirate.com/album/blonde#p3309945537', + imageUrl: 'https://f4.bcbits.com/img/0000167149_10.jpg' + }, + { + name: 'Blonde - Vinyle', + format: 'VinylFormat', + description: "L'édition vinyle de Blonde comprend les 12 pistes de l'édition régulière, ainsi qu'une affichette 12x24 po. L'achat comprend le téléchargement gratuit de l'album en version régulière.\n" + + 'Veuillez allouer 3 jours ouvrables pour que la version physique vous soit postée.', + url: 'https://musique.coeurdepirate.com/album/blonde#p3172983276', + imageUrl: 'https://f4.bcbits.com/img/0012274631_10.jpg' + } + ], + tracks: [ + { + name: 'Lève les voiles', + duration: 72.7333, + streamUrl: 'https://t4.bcbits.com/stream/4ec5267c795d34d71d84b8d9e6373477/mp3-128/4183427253?p=0&ts=1686494439&t=c40a4ae91db5c33dec570cefcb3ee628460fc173&token=1686494439_3862d4be07dc3bd6df2c7bbc017668e590ae471a', + position: 1, + url: 'https://musique.coeurdepirate.com/track/l-ve-les-voiles' + }, + { + name: 'Adieu', + duration: 147.627, + streamUrl: 'https://t4.bcbits.com/stream/bdf4f2f59952f78effcbbf3ff63300db/mp3-128/3647767496?p=0&ts=1686494439&t=6c5367a11a820055e9f97be27b2535567654e837&token=1686494439_d8163e215b039890eda6b11922a36135a31997e9', + position: 2, + url: 'https://musique.coeurdepirate.com/track/adieu' + }, + { + name: 'Danse et danse', + duration: 190.413, + streamUrl: 'https://t4.bcbits.com/stream/8d4d755d9f351caf16529342096c3de6/mp3-128/1276790164?p=0&ts=1686494439&t=76ad61376be3f286c67bd63dfb507534256fbe41&token=1686494439_07b36ea15c7b74034371696f4947da6781f6442b', + position: 3, + url: 'https://musique.coeurdepirate.com/track/danse-et-danse' + }, + { + name: 'Golden Baby', + duration: 187.027, + streamUrl: 'https://t4.bcbits.com/stream/af5533da023515d8c6c28dd834b7872f/mp3-128/365646139?p=0&ts=1686494439&t=fd353706d770e332bcb1b34d12499243cae8f6d7&token=1686494439_57c8461c94d9bc2935b311ffc1767afa480ef7b2', + position: 4, + url: 'https://musique.coeurdepirate.com/track/golden-baby' + }, + { + name: 'Ava', + duration: 196.88, + streamUrl: 'https://t4.bcbits.com/stream/5133f1cab7fa8da200f1392690aca50e/mp3-128/1796143680?p=0&ts=1686494439&t=4661d282acdddb40ed2a445802d556d01e0782a5&token=1686494439_c10e7cfd27302d58958eece4387cebe78e324d9d', + position: 5, + url: 'https://musique.coeurdepirate.com/track/ava' + }, + { + name: "Loin d'ici", + duration: 163.827, + streamUrl: 'https://t4.bcbits.com/stream/8a3166238fe996999f457459c592f7c7/mp3-128/260996437?p=0&ts=1686494439&t=4cb28deb9a7f6df51f53c8e85270f8d028d58306&token=1686494439_25e96b731cd8c63e0f955b12fa94074aecbacde0', + position: 6, + url: 'https://musique.coeurdepirate.com/track/loin-dici' + }, + { + name: 'Les amours dévouées', + duration: 147.947, + streamUrl: 'https://t4.bcbits.com/stream/a62a0cd6f5b871e7559627b34f996552/mp3-128/4086570201?p=0&ts=1686494439&t=8052657686a94df097cddaa34f1887a958befd6e&token=1686494439_c59b379ffe5096d7f913648f650a179a1b8defa5', + position: 7, + url: 'https://musique.coeurdepirate.com/track/les-amours-d-vou-es' + }, + { + name: 'Place de la République', + duration: 251.227, + streamUrl: 'https://t4.bcbits.com/stream/f7f5df6de9007b9415e120d8083cd973/mp3-128/1239774913?p=0&ts=1686494439&t=48d400f3e678c8aae520cdcc3b15a317435c772c&token=1686494439_a2060a56ed2da98c8595e9cab36280a0cc4d2b35', + position: 8, + url: 'https://musique.coeurdepirate.com/track/place-de-la-r-publique' + }, + { + name: 'Cap Diamant', + duration: 163.293, + streamUrl: 'https://t4.bcbits.com/stream/732fc4dbcd8ca3cc2cc477d773f99206/mp3-128/3259278294?p=0&ts=1686494439&t=ab5c55198ebfffc9ea45d522bb0e2eefef1c6cbb&token=1686494439_8fae510052febbd9ac978da5c63a52d69ecc5d1a', + position: 9, + url: 'https://musique.coeurdepirate.com/track/cap-diamant' + }, + { + name: 'Verseau', + duration: 233.813, + streamUrl: 'https://t4.bcbits.com/stream/a4e97e8b436b8e2ad1a2c598f5cd0a48/mp3-128/496320675?p=0&ts=1686494439&t=fc280ca1b1c88421a2c099bf43c2602adb575d5f&token=1686494439_d783594d927a7687f84879030b37b603731f8bef', + position: 10, + url: 'https://musique.coeurdepirate.com/track/verseau' + }, + { + name: 'Saint-Laurent', + duration: 194.787, + streamUrl: 'https://t4.bcbits.com/stream/5f87e431eaaf515564c006de4c888e91/mp3-128/2437695881?p=0&ts=1686494439&t=09ea0d9aa5ed5bedabf7d5875cb222ef34c95e22&token=1686494439_2b0a5f58681ce104a96f9821374f0dcc058d6f91', + position: 11, + url: 'https://musique.coeurdepirate.com/track/saint-laurent' + }, + { + name: 'La petite mort', + duration: 229.627, + streamUrl: 'https://t4.bcbits.com/stream/b257ccb8aca3ea864cbb2774e9d8d2be/mp3-128/525064765?p=0&ts=1686494439&t=9a65c09b642c93569cc88b445d5cc81f59317d54&token=1686494439_60776d9b721bf9e1f1eb994e3ac01fc5703e11a6', + position: 12, + url: 'https://musique.coeurdepirate.com/track/la-petite-mort' + } + ], + imageUrl: 'https://f4.bcbits.com/img/a1328452291_16.jpg', + label: { name: 'Bravo musique', url: 'https://bravomusique.bandcamp.com' }, + publisher: { + name: 'Cœur de pirate', + url: 'https://musique.coeurdepirate.com', + description: 'Plus d’une décennie s’est écoulée depuis que Béatrice Martin s’est incrustée dans le paysage sous le pseudonyme désormais coutumier de Cœur de pirate. Armée d’un talent digne de l’orfèvrerie, d’une poésie tantôt raffinée, tantôt subversive, et d’une aura insaisissable, elle séduit comme elle surprend, jaillissant là où on ne l’attend pas.', + imageUrl: 'https://f4.bcbits.com/img/0026415167_28.jpg' + } +} diff --git a/examples/article/getArticle.ts b/examples/article/getArticle.ts new file mode 100644 index 0000000..9ddb792 --- /dev/null +++ b/examples/article/getArticle.ts @@ -0,0 +1,13 @@ +import bcfetch from '../../'; +import util from 'util'; + +const articleUrl = 'https://daily.bandcamp.com/best-ambient/best-new-ambient-march-2018'; + +const params = { + articleUrl, + includeRawData: false +}; + +bcfetch.article.getArticle(params).then((results) => { + console.log(util.inspect(results, false, null, false)); +}); diff --git a/examples/article/getArticle_output.txt b/examples/article/getArticle_output.txt new file mode 100644 index 0000000..6637faf --- /dev/null +++ b/examples/article/getArticle_output.txt @@ -0,0 +1,531 @@ +{ + title: 'The Best New Ambient Music on Bandcamp, March 2018', + description: 'Music for snow days, reissued ‘80s Japanese ambient, and themes of loss and emptiness in this month’s roundup.', + url: 'https://daily.bandcamp.com/best-ambient/best-new-ambient-march-2018', + imageUrl: 'https://f4.bcbits.com/img/0017912164_0', + date: '2018-03-27T11:03:30Z', + category: { + name: 'Best Ambient', + url: 'https://daily.bandcamp.com/best-ambient' + }, + author: { + name: 'Aurora Mitchell', + url: 'https://daily.bandcamp.com/contributors/aurora-mitchell' + }, + mediaItems: [ + { + type: 'album', + name: 'Aarde', + url: 'https://somta.bandcamp.com/album/aarde', + imageUrl: 'https://f4.bcbits.com/img/a2731445950_9.jpg', + featuredTrackPosition: 3, + artist: { + name: 'somta', + url: 'https://somta.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/8414332_21.jpg', + location: 'Leeds, UK' + }, + tracks: [ + { + position: 1, + name: 'Aiolus', + duration: 42, + streamUrl: 'https://t4.bcbits.com/stream/bba2dc752e4f02070f3b40b72abfdfda/mp3-128/3588455251?p=0&ts=1686494469&t=4c30836b93e0647a9d41e699ec5d4d0ec21d291f&token=1686494469_77788f76c09c251b1ca1d8226f33da4db5c0d62b' + }, + { + position: 2, + name: 'Sophia', + duration: 343.521, + streamUrl: 'https://t4.bcbits.com/stream/5e904efaf5962cc83f8ed35cc49b6850/mp3-128/2347995441?p=0&ts=1686494469&t=80f5f80cd380ba2dfc3e2c355c4733c22c60285b&token=1686494469_88be89941db3ed862dc22a598e8da8450fb95461' + }, + { + position: 3, + name: 'Khiōn', + duration: 226.396, + streamUrl: 'https://t4.bcbits.com/stream/4dcfdbfcecdccde6260a12fe37bb4277/mp3-128/2272710569?p=0&ts=1686494469&t=ebb8260be7b9f5ca6a4fa33e0c5c3fea088bc6f4&token=1686494469_f2f3a59541e0c435a90730d5daf90e1b226e189c' + }, + { + position: 4, + name: 'Noos', + duration: 388.5, + streamUrl: 'https://t4.bcbits.com/stream/503e4b6e9d762fd172e36c33a0a5de57/mp3-128/3685166023?p=0&ts=1686494469&t=8dfc1dd298e0899efe99b0694028a037777231a3&token=1686494469_18f33172533b36be8ab49007b204826cbad32604' + }, + { + position: 5, + name: 'Trogo', + duration: 410.083, + streamUrl: 'https://t4.bcbits.com/stream/599b32fbe8b66002676cdaf77d3b6b06/mp3-128/717296085?p=0&ts=1686494469&t=3ac93eb20dccbd099c514b09193f62f35289a656&token=1686494469_74a168335a1dba38ca2456bfe96a6fbaa11a0e48' + }, + { + position: 6, + name: 'Tuptō', + duration: 254.029, + streamUrl: 'https://t4.bcbits.com/stream/507d08c0c8edb321660d6305d1b13c5f/mp3-128/3228890418?p=0&ts=1686494469&t=20765b465460baabb7fb5f063a5e2e61411f0891&token=1686494469_194a3741374113c29e5a7c4f688b1f3302039f69' + }, + { + position: 7, + name: 'Onos', + duration: 98.2833, + streamUrl: 'https://t4.bcbits.com/stream/a0b6e9b5d2434bbcd74d6e99ddc2965e/mp3-128/3097234822?p=0&ts=1686494469&t=53f8d1e94b49b70a19f6f0d470d78a665f94b078&token=1686494469_8628ba327d43c9a6432b79d2c88f174c29464dc1' + }, + { + position: 8, + name: 'Skia', + duration: 140.104, + streamUrl: 'https://t4.bcbits.com/stream/f5c6320bf16ef3fb8ed3a54121898866/mp3-128/2952107736?p=0&ts=1686494469&t=eb20c42b9078123b5ea4f7398bf0e16827f578ba&token=1686494469_eada5fc8f44f55debd00feccce7b257c4da78468' + }, + { + position: 9, + name: 'Hulē', + duration: 183.999, + streamUrl: 'https://t4.bcbits.com/stream/a61bb342e975ab95117826eb15dbea76/mp3-128/743360394?p=0&ts=1686494469&t=da576e28e5a2ad0d88f4e186b70491ea953b75c4&token=1686494469_be9ac67c90ec1917b5fe2b783fa100c78988ee8e' + }, + { + position: 10, + name: 'Ganos', + duration: 423, + streamUrl: 'https://t4.bcbits.com/stream/2a200eed7fd2b130b64dcf0bdc6783c5/mp3-128/3511494308?p=0&ts=1686494469&t=517172cefc9896d3f0d87239cb9172f6da680eea&token=1686494469_2ce30398f836684dd5e8904bc1f3094df8eef6b4' + }, + { + position: 11, + name: 'Polos', + duration: 414, + streamUrl: 'https://t4.bcbits.com/stream/87ba803b563488675ee32fd7f281a70a/mp3-128/3723435045?p=0&ts=1686494469&t=ddb36efb550990c965c7ddd3e2923650c8a86419&token=1686494469_83eb7200eff6bc86988448a947fb1340779825d5' + }, + { + position: 12, + name: 'Zophos', + duration: 91.0833, + streamUrl: 'https://t4.bcbits.com/stream/9ae9dc4469ca3b328ef96b7775589eaa/mp3-128/1373210950?p=0&ts=1686494469&t=792f3cf1f2863ebc9d705a55ba08e818cb916300&token=1686494469_0bb5698f995810fa807f94e90d8f90df8a77f20f' + }, + { + position: 13, + name: 'Kruos', + duration: 308.729, + streamUrl: 'https://t4.bcbits.com/stream/85ac0ff689dcd7112a431d096d9d11c0/mp3-128/3200879908?p=0&ts=1686494469&t=1394347027f64bc621373d331f980a05be09130c&token=1686494469_c43330bffd8b3c5691311032239744f88d146e45' + }, + { + position: 14, + name: 'Phōs', + duration: 188.271, + streamUrl: 'https://t4.bcbits.com/stream/bcdde2e8a7d8adb0d140804b9fbef632/mp3-128/575691204?p=0&ts=1686494469&t=f3e9a6a314330c6a48faf35a07c943bc63c238e0&token=1686494469_906a97ed39bbe922bd362d98fabb637b8dece387' + }, + { + position: 15, + name: 'Arktos', + duration: 188, + streamUrl: 'https://t4.bcbits.com/stream/d0016651f3368112bed9367294db5c45/mp3-128/2863705519?p=0&ts=1686494469&t=9bead25252fe5040997b5ea5d65a6df35244113c&token=1686494469_44f899dfb8b868d53dbf72aedd8a798f3a34532a' + } + ], + mediaItemRef: 't2272710569' + }, + { + type: 'album', + name: 'music for snow days', + url: 'https://maevescave.bandcamp.com/album/music-for-snow-days', + imageUrl: 'https://f4.bcbits.com/img/a2627798194_9.jpg', + featuredTrackPosition: 1, + artist: { + name: 'Maeve Clark', + url: 'https://maevescave.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/13825272_21.jpg', + location: 'Toronto, Ontario' + }, + tracks: [ + { + position: 1, + name: 'glacial drift', + duration: 165.517, + streamUrl: 'https://t4.bcbits.com/stream/8fe55af7408165869c29c1dacd3438ef/mp3-128/1986717137?p=0&ts=1686494469&t=c150ddb2c88a744ab803314f0591b900ddf58a80&token=1686494469_0346045914293050a7d03c82687bb6bd874121cb' + }, + { + position: 2, + name: 'frosted panes', + duration: 128, + streamUrl: 'https://t4.bcbits.com/stream/0c79a983698347a100b5927208b7de6b/mp3-128/1139371646?p=0&ts=1686494469&t=114c24fa5caa2d8323d30d25ca874a161ed78631&token=1686494469_6ef3d6745a089262935561558f0ba88f34c64674' + }, + { + position: 3, + name: 'under the glass', + duration: 180, + streamUrl: 'https://t4.bcbits.com/stream/8fbcc9f3bdf66b067fa92f479840289f/mp3-128/2086342140?p=0&ts=1686494469&t=53779590c57e93e5494cdfce3eb9ff013a46f950&token=1686494469_6f210bbd22ebeda088a3e4e37958c2b6fd585df4' + }, + { + position: 4, + name: 'alpine train track', + duration: 188.723, + streamUrl: 'https://t4.bcbits.com/stream/c3fe330ef0d7081bcfdb1861746ce799/mp3-128/3789770489?p=0&ts=1686494469&t=b515cb118bbed7f57ea5b4feb92a1fbbf1897afa&token=1686494469_09276bcdecd18f5360fab6b803e16ddd6cf5df71' + } + ], + mediaItemRef: 'a454040844' + }, + { + type: 'album', + name: 'Music for Nine Post Cards', + url: 'https://hiroshiyoshimura.bandcamp.com/album/music-for-nine-post-cards', + imageUrl: 'https://f4.bcbits.com/img/a1329705853_9.jpg', + featuredTrackPosition: 1, + artist: { + name: 'Hiroshi Yoshimura', + url: 'https://hiroshiyoshimura.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/19000214_21.jpg', + location: 'Japan' + }, + tracks: [ + { + position: 1, + name: 'Water Copy', + duration: 371.867, + streamUrl: 'https://t4.bcbits.com/stream/9ccc3a98463b14603e64163f367e40c5/mp3-128/2862079336?p=0&ts=1686494469&t=247725407c0985176b6ab1d420f58b06485d6849&token=1686494469_d7914006db3be86449698350e257a455cc2ce633' + }, + { + position: 2, + name: 'Clouds', + duration: 354.693, + streamUrl: 'https://t4.bcbits.com/stream/0479a3b6fb0905034221425f3bcada0a/mp3-128/1445700587?p=0&ts=1686494469&t=3462e4949519316b0fc974d0b53beade7d98927e&token=1686494469_60abd162e3259379c049ab927c536e11e3428fc9' + }, + { + position: 3, + name: 'Blink', + duration: 282.013, + streamUrl: 'https://t4.bcbits.com/stream/0dbe234ebedd6ddd469b09a05ef317c0/mp3-128/3323651202?p=0&ts=1686494469&t=03c0d5c852c7f8b876a567fd44fbfb0a820941bb&token=1686494469_ab301249f6c56b2f019296cec689eb687f4cd868' + }, + { + position: 4, + name: 'Dance PM', + duration: 392.32, + streamUrl: 'https://t4.bcbits.com/stream/07aae56284ad785d1bd1a5b8373dae6a/mp3-128/1063072521?p=0&ts=1686494469&t=3e70f5181ea2644cb33179b0f28b847ca3cd8617&token=1686494469_5935030d334069bee032ddd321c6b0ced1a54adb' + }, + { + position: 5, + name: 'Ice Copy', + duration: 175.773, + streamUrl: 'https://t4.bcbits.com/stream/f496d40c5475e90e3ddd5fb07373211c/mp3-128/2066333379?p=0&ts=1686494469&t=2e05a942326035faa5bf50eabc05f4e412569ca5&token=1686494469_dd950cd64561bf84eb73beaa35f1f54ca37aaae3' + }, + { + position: 6, + name: 'Soto Wa Ame - Rain Out Of Window', + duration: 276.707, + streamUrl: 'https://t4.bcbits.com/stream/7279637e86f60e8f24815a41908cfe7b/mp3-128/3414351773?p=0&ts=1686494469&t=c1c1d72dff8389abbde5f411f1217ce926e8532f&token=1686494469_01a8f3ba252a4d879629edf7bfdce2f5822bd7ca' + }, + { + position: 7, + name: 'View From My Window', + duration: 375.6, + streamUrl: 'https://t4.bcbits.com/stream/2caffd8268ef49a7d174ee3f8ce67ae9/mp3-128/546874861?p=0&ts=1686494469&t=f05f09347103a6dffc6b114f515da9884e8ebd06&token=1686494469_b4dcebc70554558118c35627181a717f16f84074' + }, + { + position: 8, + name: 'Urban Snow', + duration: 285.773, + streamUrl: 'https://t4.bcbits.com/stream/9c2fa2599479ac941b7a2cd2a646c458/mp3-128/2047145341?p=0&ts=1686494469&t=b67de3dcb90706c5d992c42a33301e5a16e05a3b&token=1686494469_c8bfcf63410a4582b5af799e431a679834f81a3a' + }, + { + position: 9, + name: 'Dream', + duration: 334.96, + streamUrl: 'https://t4.bcbits.com/stream/59654dbc1a3856016056ecb713de2279/mp3-128/1844238301?p=0&ts=1686494469&t=844e9664d0044c0412d128b1ca45d97fc9070425&token=1686494469_0754f7473ae8598c4d9fe15477faa05790100b41' + } + ], + mediaItemRef: 'a3834281211' + }, + { + type: 'album', + name: 'The Greys', + url: 'https://ilurecords.bandcamp.com/album/the-greys', + imageUrl: 'https://f4.bcbits.com/img/a2451461387_9.jpg', + featuredTrackPosition: 1, + artist: { + name: 'I Low You Records', + url: 'https://ilurecords.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/27921075_21.jpg', + location: 'Tokyo, Japan' + }, + tracks: [ + { + position: 1, + name: "Don't Remember a Thing", + duration: 71.1318, + streamUrl: 'https://t4.bcbits.com/stream/9a1d8da36f4c48850e47ad5766fad27c/mp3-128/1849746451?p=0&ts=1686494469&t=c51b2be9a8c6ff4d71e8580d47640e8580c8333a&token=1686494469_131f825d3dd5502e8696bb3e88433cd1d85e17f7' + }, + { + position: 2, + name: 'Analog Man', + duration: 45.2042, + streamUrl: 'https://t4.bcbits.com/stream/57c460c29443951cd10cd0d908b0a394/mp3-128/1737948375?p=0&ts=1686494469&t=d7f577fd3271931a8b277128a47b4127bf6ecfa6&token=1686494469_d79b412385c0ccfa30d796fb330e123ba0ffe0f9' + }, + { + position: 3, + name: 'Coughing Death', + duration: 136.558, + streamUrl: 'https://t4.bcbits.com/stream/054ff3c71138117e95d9d2f442c7ba2f/mp3-128/3268827724?p=0&ts=1686494469&t=af77f20881b42660862df7834c152af8f3c33cde&token=1686494469_85f5c1600a76430b32ac6635ed805db1be3bdde9' + }, + { + position: 4, + name: "Don't Go in the Woods Alone", + duration: 48.7698, + streamUrl: 'https://t4.bcbits.com/stream/b67e617c74892efde0bd9579d364e473/mp3-128/3557485835?p=0&ts=1686494469&t=3805312a8c17cb9b1659f8c0bf9d7681c22bad19&token=1686494469_b0498f1d085d73feabaada451288dc589b17ce60' + }, + { + position: 5, + name: 'Fairy_Fungi', + duration: 148.588, + streamUrl: 'https://t4.bcbits.com/stream/bb6759be8fe535515d3d1e8a92cd8bcf/mp3-128/1079329015?p=0&ts=1686494469&t=b9c813180eb88d3cb8187041236b6a30b46f4e66&token=1686494469_94b1389dc7ffc7195e8f3428d6d1b1323aad9ba6' + }, + { + position: 6, + name: 'Gray Ghosts', + duration: 56.1875, + streamUrl: 'https://t4.bcbits.com/stream/f949c20042a6d42671f587f761095503/mp3-128/2836999478?p=0&ts=1686494469&t=d60fe5ae1717abd3c0cf12e4d0d60804e6098b3e&token=1686494469_d6bd0423d7f1004ed16d4026533edac1fe438936' + }, + { + position: 7, + name: 'Lost', + duration: 31.1641, + streamUrl: 'https://t4.bcbits.com/stream/4f2bbf6289460c0d1108f79b9dc49c11/mp3-128/1187225203?p=0&ts=1686494469&t=7cd8ee65f00865bf49b55ae0a6799dd9ec12693d&token=1686494469_8f4990594169a7396c95dc91665d754bc780b39b' + }, + { + position: 8, + name: 'Melted Snow', + duration: 116.221, + streamUrl: 'https://t4.bcbits.com/stream/4ec3e010ad2a46e6b8b29f1f3b66f545/mp3-128/2296333718?p=0&ts=1686494469&t=00dd56f218b4e01ea0b7a71dc4d1e4812c4cf481&token=1686494469_d2970813e4227db7576cf00787e78bce1a58530d' + }, + { + position: 9, + name: 'UUmox (vers. II)', + duration: 106.548, + streamUrl: 'https://t4.bcbits.com/stream/41d8070603d9957a54c47aac4b5efdf2/mp3-128/1800262023?p=0&ts=1686494469&t=3bc0a31a7c5e2cd81536e9f4d5f4d2b5ea14b05f&token=1686494469_835a7c5a4e3bea433240f8cf515f1bef87c6effa' + }, + { + position: 10, + name: 'White Ghosts', + duration: 93.9719, + streamUrl: 'https://t4.bcbits.com/stream/b2ae77e2ff74d6bac0873c0acf3622a7/mp3-128/1335292332?p=0&ts=1686494469&t=f51d7010e2027c345cb34c4f728ec6bf478f9268&token=1686494469_f3c2c35634cba7d42278fe2ff223ab077f0cb3d5' + }, + { + position: 11, + name: 'Warp Day', + duration: 33.8125, + streamUrl: 'https://t4.bcbits.com/stream/7d70e3b721d731c859ad39461a8d6351/mp3-128/180645096?p=0&ts=1686494469&t=28a909bd3c67747bba2fe88d3d5219489a99b2d0&token=1686494469_124c813bdb31fcf0782ad1b85c021403bad801af' + }, + { + position: 12, + name: 'Old M.m.', + duration: 152.208, + streamUrl: 'https://t4.bcbits.com/stream/61015621f32f12a4afe23f2619c6d9b0/mp3-128/2286068125?p=0&ts=1686494469&t=f29ad18b5c77681a5cd1ce965e54734459577eae&token=1686494469_629ebbafd491171ea77a436b4229170918c6fdee' + }, + { + position: 13, + name: 'You Can be Lonely', + duration: 101.327, + streamUrl: 'https://t4.bcbits.com/stream/d9d5dd4f031f0a48f97bac056f405253/mp3-128/2325111113?p=0&ts=1686494469&t=663b4e21f6f6ea0667f33ce32bbf6798521ca9af&token=1686494469_32ed355733efff7b55f211c6dc0dfd4171d48633' + }, + { + position: 14, + name: 'Eyelid Smiles', + duration: 57.1423, + streamUrl: 'https://t4.bcbits.com/stream/2268e0c0f289573fb4d9741543f6245f/mp3-128/2054627365?p=0&ts=1686494469&t=0b8a62a754d0a324e40642b0d5712c27318002bf&token=1686494469_8b7619e12043c63f17a42973554fee9d7711c97f' + }, + { + position: 15, + name: 'Saying Goodbye', + duration: 70.6005, + streamUrl: 'https://t4.bcbits.com/stream/088e77b99529ab2048218c3da8bf842b/mp3-128/82283410?p=0&ts=1686494469&t=cde84d30f6576eb5b9e9b6a69c36f2b8bb10faac&token=1686494469_8dcada60aa5a2b253fbd9b89c96263f37a1f03f4' + }, + { + position: 16, + name: 'How is Your Day Going', + duration: 49.0354, + streamUrl: 'https://t4.bcbits.com/stream/18d8fc3015352bd76b5478f36dc9d23b/mp3-128/3760493673?p=0&ts=1686494469&t=e3bb914cbad1c94c4bdeb0be52579532321462dc&token=1686494469_0c5ad65622cbba936454e74d2af9fa9cd93b2fcb' + }, + { + position: 17, + name: 'Granular Loop', + duration: 66.7917, + streamUrl: 'https://t4.bcbits.com/stream/53db5fe0b0f85c17afb2d730c8ad993f/mp3-128/1780688429?p=0&ts=1686494469&t=cddad73eeebda5ad687a661ed1367a6605b643d5&token=1686494469_5b97d1cc7a0b6bb9f9730278504e7fd2a78e26c9' + }, + { + position: 18, + name: 'Shadow Faces', + duration: 90.4234, + streamUrl: 'https://t4.bcbits.com/stream/4fcd1b502498b33194683f099565e754/mp3-128/4275310931?p=0&ts=1686494469&t=8671f6e953dfac8f564327827d49675c83fb50ed&token=1686494469_2988cd8f938534c8efb9479c88cefc4583ab7c6b' + }, + { + position: 19, + name: 'The Greys', + duration: 67.9607, + streamUrl: 'https://t4.bcbits.com/stream/199e8a672421d7429b2fb1673917c0b1/mp3-128/3364736689?p=0&ts=1686494469&t=655629cbbac3fba5cd9679ae64ed6eacc920d3d0&token=1686494469_3c8d96181bbe6e1e7cb49dc27cd0f0074fb59206' + }, + { + position: 20, + name: 'Fragmented Loop', + duration: 24.0135, + streamUrl: 'https://t4.bcbits.com/stream/d0345a94b9fc6ceb4f895fd5fc6fc1b6/mp3-128/3121980470?p=0&ts=1686494469&t=eaa3c0f652c7e0306b62e162ed5a22b2d5d50ec4&token=1686494469_9e2a0b88ef6d1ec4602094dc01ea9b2d47cd5fe5' + }, + { + position: 21, + name: 'Who Goes There', + duration: 33.0828, + streamUrl: 'https://t4.bcbits.com/stream/e65fbe773c8a05cce5fe657dc28b76c8/mp3-128/411868333?p=0&ts=1686494469&t=97e3760eb5e7a8dc94f6d78252627d60211abcee&token=1686494469_92a2a676f64b2cd07823de147337676fb6efba1d' + }, + { + position: 22, + name: 'Two Lost Years', + duration: 568.698, + streamUrl: 'https://t4.bcbits.com/stream/7467ea7fd5fb544c20b50a561928f9da/mp3-128/2821042097?p=0&ts=1686494469&t=e9d8a8357150fac7f785fb81c2005cea12771379&token=1686494469_2477aa0993251a029fc68fea5fc87e56ef3f1dcf' + }, + { + position: 23, + name: 'What is Song', + duration: 21.6526, + streamUrl: 'https://t4.bcbits.com/stream/4772122bb7d1c83daf16300738af3553/mp3-128/617262569?p=0&ts=1686494469&t=87ec12526f8f99605c80110c46ef9f184247c7f0&token=1686494469_bfe5c340e47cd4a975c6f1cfb257bd5f87dffd9c' + } + ], + mediaItemRef: 'a612750820' + }, + { + type: 'album', + name: 'Cold Ubiquity', + url: 'https://lahautdanslocean.bandcamp.com/album/cold-ubiquity', + imageUrl: 'https://f4.bcbits.com/img/a837598593_9.jpg', + featuredTrackPosition: 1, + artist: { + name: "Là-haut Dans L'Océan", + url: 'https://lahautdanslocean.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/12384834_21.jpg', + location: 'Grenoble, France' + }, + tracks: [ + { + position: 1, + name: "Tentation d'Ubiquité", + duration: 523.234, + streamUrl: 'https://t4.bcbits.com/stream/dd36a520ba2cb3a40b9e8e8abe85503a/mp3-128/130351574?p=0&ts=1686494469&t=ba0dfbf29f69e5323a4425a2d0a81972cc1dc470&token=1686494469_377617898452ab4c50c1746b25bd774ae344cdb2' + }, + { + position: 2, + name: 'Ambifidus', + duration: 243.438, + streamUrl: 'https://t4.bcbits.com/stream/86de9fd0403b65068f193ef61188472b/mp3-128/1707109576?p=0&ts=1686494469&t=c3dd92c5b6925f8f14c5fe82165859d5b7eb4b84&token=1686494469_55ad5f7e5d68c09daeabbbb6ddd856519b61992b' + }, + { + position: 3, + name: 'Snow Bird', + duration: 248.527, + streamUrl: 'https://t4.bcbits.com/stream/e2af6f01452e374ddf48e7c46bb41489/mp3-128/136062705?p=0&ts=1686494469&t=962314a2d76dea9e6040bd0e2a48a04034213fe9&token=1686494469_a7b0fa47ba164f904f3768d69202210914ef6dfe' + }, + { + position: 4, + name: 'Fake Seagulls and Fulfillment', + duration: 292.912, + streamUrl: 'https://t4.bcbits.com/stream/3ed2e54d7285d00da313befb72ed4c7d/mp3-128/573634003?p=0&ts=1686494469&t=d0bc4b1129b1dcd736ff40effd09dc6972cc844a&token=1686494469_dbc4ddc1eedd6ee0067c1fa2d4cf6fe324a24b24' + }, + { + position: 5, + name: 'Birds Coalition For My Apartment Cat', + duration: 340, + streamUrl: 'https://t4.bcbits.com/stream/6508dd8760542d30418021b7802ecb9c/mp3-128/1466403206?p=0&ts=1686494469&t=0eba2c47435a732ddb1f31f91f1f3fc0ebaca293&token=1686494469_ceb7e7aa3d18452bbe6f0fe97541e02d596ba384' + }, + { + position: 6, + name: 'My Heart Play On The Lake', + duration: 429.978, + streamUrl: 'https://t4.bcbits.com/stream/6e3eb7b756bdf444930ffa576717fe5f/mp3-128/2764710200?p=0&ts=1686494469&t=539adf2da96047bb007694c64e67c29b7066fb3a&token=1686494469_d8ca058e1e1ba84a38ff5a400e1b28b097f5a1e6' + }, + { + position: 7, + name: 'Riley Net Data', + duration: 232.441, + streamUrl: 'https://t4.bcbits.com/stream/ae9892a7be6390f426f787cf5ef0140b/mp3-128/3158881070?p=0&ts=1686494469&t=2ecff9ec2af57a6e638d6dcb082e5b801bde231c&token=1686494469_64ec175b72c368f2f24d823a851c84d57f9e5086' + }, + { + position: 8, + name: 'Deepor Radiator Swamp', + duration: 327.733, + streamUrl: 'https://t4.bcbits.com/stream/c3a49f2d6b0c8d6bff69367956f0c5f6/mp3-128/3414841372?p=0&ts=1686494469&t=2046c3f04d880ec28ecd7b9ee1e41b752f7ffa90&token=1686494469_5a9c4fa8f4eb18f222897ed068705abc6e94ccd4' + }, + { + position: 9, + name: 'Far Cold Away', + duration: 98.5972, + streamUrl: 'https://t4.bcbits.com/stream/57d74642b683fcd05ba062fc908045b9/mp3-128/1563033969?p=0&ts=1686494469&t=ecf77684b26e12d91b9e9beb438ccf1e72bde97a&token=1686494469_9b9dec99921bd90924f7610589946ce92f73ff2f' + }, + { + position: 10, + name: 'Muages', + duration: 304.83, + streamUrl: 'https://t4.bcbits.com/stream/5108a71efc1eb4aeecbad4a18ea595e4/mp3-128/2851363235?p=0&ts=1686494469&t=9c8bab0f0876e000622e4b59a60a9b51111a1e5f&token=1686494469_8488e69b7b0dd58a53c5456852f55f269f6b1b43' + }, + { + position: 11, + name: 'Thunder The Ocean', + duration: 436.336, + streamUrl: 'https://t4.bcbits.com/stream/aa7c27bf7f052098de0535adf3604aba/mp3-128/1650176642?p=0&ts=1686494469&t=00d2472f17fef9a9732e2a19cdf4ec106e11789a&token=1686494469_e77c2dcfa4f41befc719283f8b8f18c02fcb0b0e' + }, + { + position: 12, + name: 'Mermaids', + duration: 352.662, + streamUrl: 'https://t4.bcbits.com/stream/c4b8764c805124986b4f97e49f96af13/mp3-128/104707931?p=0&ts=1686494469&t=fe4aeaaca4a7401141fb6edbf7ce0eb964b901c8&token=1686494469_83191afa709478ac8c8724398ecd519cf604ef22' + } + ], + mediaItemRef: 'a42342034' + } + ], + sections: [ + { + html: 'There are infinite atmospheric worlds available on Bandcamp. From the dreamy to the nightmarish, and the meditative to the deeply unsettling, these compositions often fall under the ambient umbrella. Albums without words that are deeply thoughtful and thought-provoking in their own way—these releases come from all corners of the globe. Every month, Aurora Mitchell will take you through the best ambient releases. In this edition, there’s music for snow days, reissued ‘80s Japanese ambient, and themes of loss and emptiness.', + text: 'There are infinite atmospheric worlds available on Bandcamp. From the dreamy to the nightmarish, and the meditative to the deeply unsettling, these compositions often fall under the ambient umbrella. Albums without words that are deeply thoughtful and thought-provoking in their own way—these releases come from all corners of the globe. Every month, Aurora Mitchell will take you through the best ambient releases. In this edition, there’s music for snow days, reissued ‘80s Japanese ambient, and themes of loss and emptiness.' + }, + { + html: 'Last year, Leeds-based producer Somta released their first album, Surface to air, a dark and introspective offering. Exactly one year later, they return with their follow-up, Aarde. It starts off in blistering fashion, with bizarre voices echoing through a whipped-up wind: Every star is just a sail in the brain of the universe,they whisper. “Khiōn” is one of the album’s highlights, boasting intergalactic melodies and sliding pads. The gust of noise that blew through Surface to air is present here, but there are more moments of soft elegance, like the pleasant drift of “Ganos” and the shiny, high-pitched waves of “Zophos.” But the album really reaches an apex with “Phōs,” which makes excellent use of space (and cosmic bleeps).', + text: 'Last year, Leeds-based producer Somta released their first album, Surface to air, a dark and introspective offering. Exactly one year later, they return with their follow-up, Aarde. It starts off in blistering fashion, with bizarre voices echoing through a whipped-up wind: “Every star is just a sail in the brain of the universe,” they whisper. “Khiōn” is one of the album’s highlights, boasting intergalactic melodies and sliding pads. The gust of noise that blew through Surface to air is present here, but there are more moments of soft elegance, like the pleasant drift of “Ganos” and the shiny, high-pitched waves of “Zophos.” But the album really reaches an apex with “Phōs,” which makes excellent use of space (and cosmic bleeps).', + heading: { + html: 'Somta
Aarde\n', + text: 'Somta\nAarde' + }, + mediaItemRef: 't2272710569' + }, + { + html: 'We may be in early spring, but snow is still wreaking wintry havoc around the world, leaving people housebound while the ground is covered and transport is halted. Ontario’s Dame Cook’s four-track release is designed to soundtrack these days, titled, appropriately enough, music for snow days. While the track titles reference ice and freezing temperatures, the music is neither frosty nor cold; it has the sparkling, dewy atmosphere of a world slowly thawing. Flutes flutter dreamily across the bare bones of the first track, “glacial drift.” The record alternates between the twinkling instrumentation of that song and “under the glass” to the sluggish, meandering charm of “frosted panes” and “alpine train track.”', + text: 'We may be in early spring, but snow is still wreaking wintry havoc around the world, leaving people housebound while the ground is covered and transport is halted. Ontario’s Dame Cook’s four-track release is designed to soundtrack these days, titled, appropriately enough, music for snow days. While the track titles reference ice and freezing temperatures, the music is neither frosty nor cold; it has the sparkling, dewy atmosphere of a world slowly thawing. Flutes flutter dreamily across the bare bones of the first track, “glacial drift.” The record alternates between the twinkling instrumentation of that song and “under the glass” to the sluggish, meandering charm of “frosted panes” and “alpine train track.”', + heading: { + html: 'Dame Cook
music for snow days\n', + text: 'Dame Cook\nmusic for snow days' + }, + mediaItemRef: 'a454040844' + }, + { + html: 'A few years after Hiroshi Yoshimura’s death in 2003, his music started slowly finding its way onto popular mixes and playlists, and the demand for his music grew so much that many titles were repressed. His first album, Music For Nine Post Cards, was finally reissued last year, and it proves that few composers capture the elegance of space, the balance of hope and sadness, and the healing power of ambient music quite like Yoshimura does. It’s the perfect soundtrack to drift slowly to sleep to, the silence between each note more poignant in the darkness.', + text: 'A few years after Hiroshi Yoshimura’s death in 2003, his music started slowly finding its way onto popular mixes and playlists, and the demand for his music grew so much that many titles were repressed. His first album, Music For Nine Post Cards, was finally reissued last year, and it proves that few composers capture the elegance of space, the balance of hope and sadness, and the healing power of ambient music quite like Yoshimura does. It’s the perfect soundtrack to drift slowly to sleep to, the silence between each note more poignant in the darkness.', + heading: { + html: 'Hiroshi Yoshimura
Music For Nine Post Cards\n', + text: 'Hiroshi Yoshimura\nMusic For Nine Post Cards' + }, + mediaItemRef: 'a3834281211' + }, + { + html: 'This album by Tape Sounds, on Tokyo-based label I Low You Records, has a despondent tone; The Greys is a collection of loops that center around feelings of loss, emptiness, and blankness. Each loop is two minutes or less, giving only brief snapshots of ideas or sequences before swiftly moving on. Despite the theme, there are a lot of magical, bright sounds on The Greys. It’s a largely guitar-based record, taking simple chords and notes and stretching them with the charm of delay.', + text: 'This album by Tape Sounds, on Tokyo-based label I Low You Records, has a despondent tone; The Greys is a collection of loops that center around feelings of loss, emptiness, and blankness. Each loop is two minutes or less, giving only brief snapshots of ideas or sequences before swiftly moving on. Despite the theme, there are a lot of magical, bright sounds on The Greys. It’s a largely guitar-based record, taking simple chords and notes and stretching them with the charm of delay.', + heading: { + html: 'Tape Sounds
The Greys\n', + text: 'Tape Sounds\nThe Greys' + }, + mediaItemRef: 'a612750820' + }, + { + html: 'Denis Morin’s newest offering for French label Là-haut Dans L’Océan is a lush exploration of birds chirping in echoing circles, watery samples, and shimmering pads. Cold Ubiquity plays with the tones and pitches of a large variety of bird calls. The second half of the record is more synth-based, with pixelated melodies fluttering throughout. Some of the tracks were recorded in a 1400m altitude environment, and the whole record was made to be listened at that altitude “while snow melts in a little stream on the path.”', + text: 'Denis Morin’s newest offering for French label Là-haut Dans L’Océan is a lush exploration of birds chirping in echoing circles, watery samples, and shimmering pads. Cold Ubiquity plays with the tones and pitches of a large variety of bird calls. The second half of the record is more synth-based, with pixelated melodies fluttering throughout. Some of the tracks were recorded in a 1400m altitude environment, and the whole record was made to be listened at that altitude “while snow melts in a little stream on the path.”', + heading: { + html: 'Denis Morin
Cold Ubiquity\n', + text: 'Denis Morin\nCold Ubiquity' + }, + mediaItemRef: 'a42342034' + } + ], + genre: { + name: 'Ambient', + url: 'https://bandcamp.com/tag/ambient', + readMoreUrl: 'https://daily.bandcamp.com/genres/ambient' + } +} diff --git a/examples/article/getCategories.ts b/examples/article/getCategories.ts new file mode 100644 index 0000000..edaa7b4 --- /dev/null +++ b/examples/article/getCategories.ts @@ -0,0 +1,6 @@ +import bcfetch from '../../'; +import util from 'util'; + +bcfetch.article.getCategories().then((results) => { + console.log(util.inspect(results, false, null, false)); +}); diff --git a/examples/article/getCategories_output.txt b/examples/article/getCategories_output.txt new file mode 100644 index 0000000..dee0c0c --- /dev/null +++ b/examples/article/getCategories_output.txt @@ -0,0 +1,234 @@ +[ + { + name: 'featured', + title: '', + sections: [ + { + name: 'best-of-year', + title: '', + categories: [ + { + url: 'https://daily.bandcamp.com/best-of-2022', + name: 'Best of 2022' + }, + { + url: 'https://daily.bandcamp.com/best-of-2021', + name: 'Best of 2021' + }, + { + url: 'https://daily.bandcamp.com/best-of-2020', + name: 'Best of 2020' + }, + { + url: 'https://daily.bandcamp.com/best-of-2019', + name: 'Best of 2019' + }, + { + url: 'https://daily.bandcamp.com/best-of-2018', + name: 'Best of 2018' + }, + { + url: 'https://daily.bandcamp.com/best-of-2017', + name: 'Best of 2017' + }, + { + url: 'https://daily.bandcamp.com/best-of-2016', + name: 'Best of 2016' + } + ] + }, + { + name: 'best-of', + title: '', + categories: [ + { + url: 'https://daily.bandcamp.com/best-ambient', + name: 'Best Ambient' + }, + { + url: 'https://daily.bandcamp.com/best-beat-tapes', + name: 'Best Beat Tapes' + }, + { + url: 'https://daily.bandcamp.com/best-dance-12s', + name: 'Best Dance 12”s' + }, + { + url: 'https://daily.bandcamp.com/best-contemporary-classical', + name: 'Best Contemporary Classical' + }, + { + url: 'https://daily.bandcamp.com/best-electronic', + name: 'Best Electronic' + }, + { + url: 'https://daily.bandcamp.com/best-experimental', + name: 'Best Experimental' + }, + { + url: 'https://daily.bandcamp.com/best-hip-hop', + name: 'Best Hip-Hop' + }, + { + url: 'https://daily.bandcamp.com/best-jazz', + name: 'Best Jazz' + }, + { + url: 'https://daily.bandcamp.com/best-metal', + name: 'Best Metal' + }, + { + url: 'https://daily.bandcamp.com/best-punk', + name: 'Best Punk' + }, + { + url: 'https://daily.bandcamp.com/best-reissues', + name: 'Best Reissues' + }, + { + url: 'https://daily.bandcamp.com/best-soul', + name: 'Best Soul' + } + ] + } + ] + }, + { + name: 'franchises', + title: 'Franchises', + categories: [ + { url: 'https://daily.bandcamp.com/lists', name: 'Lists' }, + { url: 'https://daily.bandcamp.com/features', name: 'Features' }, + { + url: 'https://daily.bandcamp.com/album-of-the-day', + name: 'Album of the Day' + }, + { + url: 'https://daily.bandcamp.com/acid-test', + name: 'Acid Test' + }, + { + url: 'https://daily.bandcamp.com/bandcamp-navigator', + name: 'Bandcamp Navigator' + }, + { url: 'https://daily.bandcamp.com/big-ups', name: 'Big Ups' }, + { + url: 'https://daily.bandcamp.com/certified', + name: 'Certified' + }, + { url: 'https://daily.bandcamp.com/gallery', name: 'Gallery' }, + { + url: 'https://daily.bandcamp.com/hidden-gems', + name: 'Hidden Gems' + }, + { + url: 'https://daily.bandcamp.com/high-scores', + name: 'High Scores' + }, + { + url: 'https://daily.bandcamp.com/label-profile', + name: 'Label Profile' + }, + { + url: 'https://daily.bandcamp.com/lifetime-achievement', + name: 'Lifetime Achievement' + }, + { + url: 'https://daily.bandcamp.com/resonance', + name: 'Resonance' + }, + { + url: 'https://daily.bandcamp.com/scene-report', + name: 'Scene Report' + }, + { + url: 'https://daily.bandcamp.com/seven-essential-releases', + name: 'Seven Essential Releases' + }, + { + url: 'https://daily.bandcamp.com/shortlist', + name: 'Shortlist' + }, + { + url: 'https://daily.bandcamp.com/the-merch-table', + name: 'The Merch Table' + } + ] + }, + { + name: 'genres', + title: 'Genres', + categories: [ + { + url: 'https://daily.bandcamp.com/genres/acoustic', + name: 'Acoustic' + }, + { + url: 'https://daily.bandcamp.com/genres/alternative', + name: 'Alternative' + }, + { + url: 'https://daily.bandcamp.com/genres/ambient', + name: 'Ambient' + }, + { url: 'https://daily.bandcamp.com/genres/blues', name: 'Blues' }, + { + url: 'https://daily.bandcamp.com/genres/classical', + name: 'Classical' + }, + { + url: 'https://daily.bandcamp.com/genres/comedy', + name: 'Comedy' + }, + { + url: 'https://daily.bandcamp.com/genres/country', + name: 'Country' + }, + { + url: 'https://daily.bandcamp.com/genres/devotional', + name: 'Devotional' + }, + { + url: 'https://daily.bandcamp.com/genres/electronic', + name: 'Electronic' + }, + { + url: 'https://daily.bandcamp.com/genres/experimental', + name: 'Experimental' + }, + { url: 'https://daily.bandcamp.com/genres/folk', name: 'Folk' }, + { url: 'https://daily.bandcamp.com/genres/funk', name: 'Funk' }, + { + url: 'https://daily.bandcamp.com/genres/hip-hop-rap', + name: 'Hip-Hop/Rap' + }, + { url: 'https://daily.bandcamp.com/genres/jazz', name: 'Jazz' }, + { url: 'https://daily.bandcamp.com/genres/latin', name: 'Latin' }, + { url: 'https://daily.bandcamp.com/genres/metal', name: 'Metal' }, + { url: 'https://daily.bandcamp.com/genres/pop', name: 'Pop' }, + { url: 'https://daily.bandcamp.com/genres/punk', name: 'Punk' }, + { + url: 'https://daily.bandcamp.com/genres/r-b-soul', + name: 'R&B/Soul' + }, + { + url: 'https://daily.bandcamp.com/genres/reggae', + name: 'Reggae' + }, + { url: 'https://daily.bandcamp.com/genres/rock', name: 'Rock' }, + { + url: 'https://daily.bandcamp.com/genres/soundtrack', + name: 'Soundtrack' + }, + { + url: 'https://daily.bandcamp.com/genres/spoken-word', + name: 'Spoken Word' + }, + { url: 'https://daily.bandcamp.com/genres/world', name: 'World' }, + { + url: 'https://daily.bandcamp.com/genres/podcasts', + name: 'Podcasts' + } + ] + } +] diff --git a/examples/article/list.ts b/examples/article/list.ts new file mode 100644 index 0000000..8fb54d5 --- /dev/null +++ b/examples/article/list.ts @@ -0,0 +1,11 @@ +import bcfetch from '../../'; +import util from 'util'; + +const params = { + categoryUrl: 'https://daily.bandcamp.com/best-ambient', + page: 2 +}; + +bcfetch.article.list(params).then((results) => { + console.log(util.inspect(results, false, null, false)); +}); diff --git a/examples/article/list_output.txt b/examples/article/list_output.txt new file mode 100644 index 0000000..a64d10d --- /dev/null +++ b/examples/article/list_output.txt @@ -0,0 +1,307 @@ +{ + articles: [ + { + url: 'https://daily.bandcamp.com/best-ambient/the-best-ambient-music-on-bandcamp-august-2020', + title: 'The Best Ambient Music on Bandcamp: August 2020', + date: 'September 01, 2020', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0021431652_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/the-best-ambient-music-on-bandcamp-july-2020', + title: 'The Best Ambient Music on Bandcamp: July 2020', + date: 'August 06, 2020', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0021091675_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/the-best-ambient-music-on-bandcamp-june-2020', + title: 'The Best Ambient Music on Bandcamp: June 2020', + date: 'July 02, 2020', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0020691450_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/the-best-new-ambient-music-on-bandcamp-april-2020', + title: 'The Best New Ambient Music on Bandcamp: April 2020', + date: 'May 01, 2020', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0019742632_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/the-best-new-ambient-music-on-bandcamp-march-2020', + title: 'The Best New Ambient Music on Bandcamp: March 2020', + date: 'April 03, 2020', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0019297568_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/the-best-new-ambient-music-on-bandcamp-february-2020', + title: 'The Best New Ambient Music on Bandcamp: February 2020', + date: 'March 06, 2020', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0018875275_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/the-best-new-ambient-music-on-bandcamp-january-2020', + title: 'The Best New Ambient Music on Bandcamp: January 2020', + date: 'January 29, 2020', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0018520240_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/the-best-new-ambient-on-bandcamp-november-2019', + title: 'The Best New Ambient on Bandcamp: November 2019', + date: 'December 04, 2019', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0018037661_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/the-best-new-ambient-music-on-bandcamp-october-2019', + title: 'The Best New Ambient Music on Bandcamp: October 2019', + date: 'October 28, 2019', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0017912007_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/the-best-new-ambient-on-bandcamp-september-2019', + title: 'The Best New Ambient on Bandcamp: September 2019', + date: 'October 02, 2019', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0017911937_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/best-new-ambient-on-bandcamp-august-2019', + title: 'The Best New Ambient on Bandcamp: August 2019', + date: 'August 23, 2019', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0017911821_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/best-new-ambient-on-bandcamp-july-2019', + title: 'The Best New Ambient on Bandcamp: July 2019', + date: 'August 02, 2019', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0017911758_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/best-new-ambient-on-bandcamp-june-2019', + title: 'The Best New Ambient on Bandcamp: June 2019', + date: 'July 01, 2019', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0017911671_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/best-new-ambient-may-2019', + title: 'The Best New Ambient on Bandcamp: May 2019', + date: 'May 30, 2019', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0017911570_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/best-new-ambient-on-bandcamp-april-2019', + title: 'The Best New Ambient on Bandcamp: April 2019', + date: 'May 02, 2019', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0017911488_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/best-new-ambient-music-march-2019', + title: 'The Best New Ambient Music on Bandcamp: March 2019', + date: 'April 01, 2019', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0017911390_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/best-new-ambient-music-on-bandcamp-february-2019', + title: 'The Best New Ambient Music on Bandcamp: February 2019', + date: 'February 27, 2019', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0017911294_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/the-best-new-ambient-on-bandcamp-january-2019', + title: 'The Best New Ambient on Bandcamp: January 2019', + date: 'February 05, 2019', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0030523501_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/the-best-new-ambient-music-on-bandcamp-november-2018', + title: 'The Best New Ambient Music on Bandcamp, November 2018', + date: 'November 21, 2018', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0028920513_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/best-ambient-october-2018', + title: 'The Best New Ambient Music on Bandcamp, October 2018', + date: 'October 18, 2018', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0017911002_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/the-best-new-ambient-music-on-bandcamp-september-2018', + title: 'The Best New Ambient Music on Bandcamp, September 2018', + date: 'October 05, 2018', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0017910962_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/the-best-new-ambient-music-on-bandcamp-july-2018', + title: 'The Best New Ambient Music on Bandcamp, July 2018', + date: 'August 01, 2018', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0017910788_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/best-new-ambient-june-2018', + title: 'The Best New Ambient Music on Bandcamp, June 2018', + date: 'June 22, 2018', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0017910685_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/best-new-ambient-may-2018', + title: 'The Best New Ambient Music on Bandcamp, May 2018', + date: 'May 23, 2018', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0017910601_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/best-ambient-april-2018', + title: 'The Best New Ambient Music on Bandcamp, April 2018', + date: 'April 23, 2018', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0017912162_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/best-new-ambient-march-2018', + title: 'The Best New Ambient Music on Bandcamp, March 2018', + date: 'March 27, 2018', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0017912164_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/best-ambient-february-2018', + title: 'The Best New Ambient Music on Bandcamp, February 2018', + date: 'February 27, 2018', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0017910366_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/the-best-new-ambient-january-2018', + title: 'The Best New Ambient Music on Bandcamp, January 2018', + date: 'January 24, 2018', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0017910284_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/best-new-ambient-november-2017', + title: 'The Best New Ambient Music on Bandcamp, October/November 2017', + date: 'November 29, 2017', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0017910161_150.jpg' + }, + { + url: 'https://daily.bandcamp.com/best-ambient/best-new-ambient-september-2017', + title: 'The Best New Ambient Music on Bandcamp, September 2017', + date: 'September 28, 2017', + category: { + name: 'BEST AMBIENT', + url: 'https://daily.bandcamp.com/best-ambient' + }, + imageUrl: 'https://f4.bcbits.com/img/0011401744_150.jpg' + } + ], + total: 61, + start: 31, + end: 60 +} diff --git a/examples/autocomplete/getSuggestions.ts b/examples/autocomplete/getSuggestions.ts new file mode 100644 index 0000000..0fa4ad1 --- /dev/null +++ b/examples/autocomplete/getSuggestions.ts @@ -0,0 +1,30 @@ +import bcfetch, { AutocompleteAPIGetSuggestionsParams, AutocompleteItemType } from '../../'; +import util from 'util'; + +const autocompleteTagParams = { + itemType: AutocompleteItemType.Tag, + query: 'ambient', + limit: 10 +} as const; + +const autocompleteLocationParams = { + itemType: AutocompleteItemType.Location, + query: 'Fin', + limit: 10 +} as const; + +function printResults(results: any, params: AutocompleteAPIGetSuggestionsParams) { + const title = `Autocomplete ${params.itemType.toLowerCase()} suggestions: ${params.query}, Limit: ${params.limit}`; + console.log(title); + console.log('-'.repeat(title.length)); + console.log(util.inspect(results, false, null, false)); + console.log(''); +} + +bcfetch.autocomplete.getSuggestions(autocompleteTagParams).then((results) => { + printResults(results, autocompleteTagParams); +}); + +bcfetch.autocomplete.getSuggestions(autocompleteLocationParams).then((results) => { + printResults(results, autocompleteLocationParams); +}); diff --git a/examples/autocomplete/getSuggestions_output.txt b/examples/autocomplete/getSuggestions_output.txt new file mode 100644 index 0000000..8fe6776 --- /dev/null +++ b/examples/autocomplete/getSuggestions_output.txt @@ -0,0 +1,101 @@ +Autocomplete tag suggestions: ambient, Limit: 10 +------------------------------------------------ +[ + { type: 'tag', count: 586893, value: 'ambient', name: 'ambient' }, + { + type: 'tag', + count: 87051, + value: 'ambient-electronic', + name: 'ambient electronic' + }, + { + type: 'tag', + count: 15222, + value: 'ambient-rock', + name: 'ambient rock' + }, + { + type: 'tag', + count: 9207, + value: 'ambient-techno', + name: 'ambient techno' + }, + { + type: 'tag', + count: 5607, + value: 'ambient-music', + name: 'ambient music' + }, + { + type: 'tag', + count: 3606, + value: 'ambient-drone', + name: 'ambient drone' + } +] + +Autocomplete location suggestions: Fin, Limit: 10 +------------------------------------------------- +[ + { + type: 'location', + value: 2186224, + name: 'New Zealand', + fullName: 'New Zealand' + }, + { + type: 'location', + value: 660013, + name: 'Finland', + fullName: 'Finland' + }, + { + type: 'location', + value: 5308655, + name: 'Phoenix', + fullName: 'Phoenix, Arizona' + }, + { + type: 'location', + value: 830685, + name: 'Keski-Suomi', + fullName: 'Keski-Suomi, Republic of Finland' + }, + { + type: 'location', + value: 2139685, + name: 'New Caledonia', + fullName: 'New Caledonia' + }, + { + type: 'location', + value: 5153924, + name: 'Findlay', + fullName: 'Findlay, Ohio' + }, + { + type: 'location', + value: 830708, + name: 'Finland Proper', + fullName: 'Finland Proper, Republic of Finland' + }, + { + type: 'location', + value: 780166, + name: 'Finnmark Fylke', + fullName: 'Finnmark Fylke, Kingdom of Norway' + }, + { + type: 'location', + value: 2714903, + name: 'Finspång', + fullName: 'Finspång, Östergötland' + }, + { + type: 'location', + value: 3176983, + name: 'Finale Emilia', + fullName: 'Finale Emilia, Emilia-Romagna' + } +] + diff --git a/examples/band/getDiscography.ts b/examples/band/getDiscography.ts new file mode 100644 index 0000000..0da7abd --- /dev/null +++ b/examples/band/getDiscography.ts @@ -0,0 +1,27 @@ +import bcfetch from '../../'; +import util from 'util'; + +const artistUrl = 'https://musique.coeurdepirate.com'; +const labelUrl = 'https://randsrecords.bandcamp.com'; + +const artistDiscogParams = { + bandUrl: artistUrl, + imageFormat: 'art_app_large' +}; + +const labelDiscogParams = { + bandUrl: labelUrl, + imageFormat: 'art_app_large' +}; + +bcfetch.band.getDiscography(artistDiscogParams).then((results) => { + console.log(`Artist URL: ${artistUrl}`); + console.log(util.inspect(results, false, null, false)); + console.log(); +}); + +bcfetch.band.getDiscography(labelDiscogParams).then((results) => { + console.log(`Label URL: ${labelUrl}`); + console.log(util.inspect(results, false, null, false)); + console.log(); +}); diff --git a/examples/band/getDiscography_output.txt b/examples/band/getDiscography_output.txt new file mode 100644 index 0000000..d9c08b9 --- /dev/null +++ b/examples/band/getDiscography_output.txt @@ -0,0 +1,849 @@ +Artist URL: https://musique.coeurdepirate.com +[ + { + url: 'https://musique.coeurdepirate.com/track/parfait-no-l', + type: 'track', + name: 'Parfait Noël', + artist: { name: 'Cœur de pirate' }, + imageUrl: 'https://f4.bcbits.com/img/a0887598283_16.jpg' + }, + { + url: 'https://musique.coeurdepirate.com/album/clou', + type: 'album', + name: 'Clou', + artist: { name: 'Cœur de pirate' }, + imageUrl: 'https://f4.bcbits.com/img/a0422319817_16.jpg' + }, + { + url: 'https://musique.coeurdepirate.com/album/impossible-aimer', + type: 'album', + name: 'Impossible à aimer', + artist: { name: 'Cœur de pirate' }, + imageUrl: 'https://f4.bcbits.com/img/a3147632514_16.jpg' + }, + { + url: 'https://musique.coeurdepirate.com/track/on-saimera-toujours-2', + type: 'track', + name: "On s'aimera toujours", + artist: { name: 'Cœur de pirate' }, + imageUrl: 'https://f4.bcbits.com/img/a0599648879_16.jpg' + }, + { + url: 'https://musique.coeurdepirate.com/track/plan-trois', + type: 'track', + name: 'Plan à trois', + artist: { name: 'Cœur de pirate' }, + imageUrl: 'https://f4.bcbits.com/img/a4236666126_16.jpg' + }, + { + url: 'https://musique.coeurdepirate.com/album/pers-ides', + type: 'album', + name: 'Perséides', + artist: { name: 'Cœur de pirate' }, + imageUrl: 'https://f4.bcbits.com/img/a3751956000_16.jpg' + }, + { + url: 'https://musique.coeurdepirate.com/track/tes-belle', + type: 'track', + name: "T'es belle", + artist: { name: 'Cœur de pirate' }, + imageUrl: 'https://f4.bcbits.com/img/a0774650359_16.jpg' + }, + { + url: 'https://musique.coeurdepirate.com/track/ne-mappelle-pas', + type: 'track', + name: "Ne m'appelle pas", + artist: { name: 'Cœur de pirate' }, + imageUrl: 'https://f4.bcbits.com/img/a2603960871_16.jpg' + }, + { + url: 'https://musique.coeurdepirate.com/album/en-cas-de-temp-te-ce-jardin-sera-ferm', + type: 'album', + name: 'en cas de tempête, ce jardin sera fermé.', + artist: { name: 'Cœur de pirate' }, + imageUrl: 'https://f4.bcbits.com/img/a0248413645_16.jpg' + }, + { + url: 'https://musique.coeurdepirate.com/album/somnambule', + type: 'album', + name: 'Somnambule', + artist: { name: 'Cœur de pirate' }, + imageUrl: 'https://f4.bcbits.com/img/a1903816474_16.jpg' + }, + { + url: 'https://musique.coeurdepirate.com/album/pr-monition-2', + type: 'album', + name: 'Prémonition', + artist: { name: 'Cœur de pirate' }, + imageUrl: 'https://f4.bcbits.com/img/a2454271957_16.jpg' + }, + { + url: 'https://musique.coeurdepirate.com/album/chansons-tristes-pour-no-l', + type: 'album', + name: 'Chansons tristes pour Noël', + artist: { name: 'Cœur de pirate' }, + imageUrl: 'https://f4.bcbits.com/img/a2476670277_16.jpg' + }, + { + url: 'https://musique.coeurdepirate.com/album/roses', + type: 'album', + name: 'Roses', + artist: { name: 'Cœur de pirate' }, + imageUrl: 'https://f4.bcbits.com/img/a3347055233_16.jpg' + }, + { + url: 'https://musique.coeurdepirate.com/album/carry-on-2', + type: 'album', + name: 'Carry On', + artist: { name: 'Cœur de pirate' }, + imageUrl: 'https://f4.bcbits.com/img/a4277887982_16.jpg' + }, + { + url: 'https://musique.coeurdepirate.com/album/oublie-moi-carry-on', + type: 'album', + name: 'Oublie-moi (Carry On)', + artist: { name: 'Cœur de pirate' }, + imageUrl: 'https://f4.bcbits.com/img/a0891943451_16.jpg' + }, + { + url: 'https://musique.coeurdepirate.com/album/child-of-light', + type: 'album', + name: 'Child of Light', + artist: { name: 'Cœur de pirate' }, + imageUrl: 'https://f4.bcbits.com/img/a3984758353_16.jpg' + }, + { + url: 'https://musique.coeurdepirate.com/album/trauma', + type: 'album', + name: 'Trauma', + artist: { name: 'Cœur de pirate' }, + imageUrl: 'https://f4.bcbits.com/img/a3799110102_16.jpg' + }, + { + url: 'https://musique.coeurdepirate.com/album/blonde', + type: 'album', + name: 'Blonde', + artist: { name: 'Cœur de pirate' }, + imageUrl: 'https://f4.bcbits.com/img/a1328452291_16.jpg' + }, + { + url: 'https://musique.coeurdepirate.com/album/comme-des-enfants-version-originale-et-remix-par-le-matos', + type: 'album', + name: 'Comme des enfants (Version originale et remix par Le Matos)', + artist: { name: 'Cœur de pirate' }, + imageUrl: 'https://f4.bcbits.com/img/a1250129776_16.jpg' + }, + { + url: 'https://musique.coeurdepirate.com/album/coeur-de-pirate', + type: 'album', + name: 'Coeur de pirate', + artist: { name: 'Cœur de pirate' }, + imageUrl: 'https://f4.bcbits.com/img/a2201751482_16.jpg' + } +] + +Label URL: https://randsrecords.bandcamp.com +[ + { + url: 'https://randsrecords.bandcamp.com/album/in-order-to-dance-40', + type: 'album', + name: 'In Order To Dance 4.0', + artist: { name: 'Various Artists' }, + imageUrl: 'https://f4.bcbits.com/img/a2214855589_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/20hz-remixes', + type: 'album', + name: '20HZ (Remixes)', + artist: { name: 'Capricorn' }, + imageUrl: 'https://f4.bcbits.com/img/a2242386435_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/the-commencement', + type: 'album', + name: 'The Commencement', + artist: { name: 'Tainted Tazz' }, + imageUrl: 'https://f4.bcbits.com/img/a0567195783_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/btvc', + type: 'album', + name: 'BTVC', + artist: { name: 'Batavia Collective' }, + imageUrl: 'https://f4.bcbits.com/img/a1821875798_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/ultimatum', + type: 'album', + name: 'Ultimatum', + artist: { name: 'Som.1' }, + imageUrl: 'https://f4.bcbits.com/img/a0135985582_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/hold-on', + type: 'album', + name: 'Hold On', + artist: { name: 'Pascal Nuzzo' }, + imageUrl: 'https://f4.bcbits.com/img/a1698391615_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/rites-ep', + type: 'album', + name: 'Rites EP', + artist: { name: 'Antagonist' }, + imageUrl: 'https://f4.bcbits.com/img/a1018155367_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/something-flash', + type: 'album', + name: 'Something Flash', + artist: { name: 'Insider' }, + imageUrl: 'https://f4.bcbits.com/img/a3932573284_16.jpg' + }, + { + url: 'https://progedia.bandcamp.com/album/di-scuri', + type: 'album', + name: 'Diõscuri', + artist: { name: 'PROGedia' }, + imageUrl: 'https://f4.bcbits.com/img/a0994367687_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/red-tuna', + type: 'album', + name: 'Red Tuna', + artist: { name: 'Vromm' }, + imageUrl: 'https://f4.bcbits.com/img/a3935902897_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/did-this', + type: 'album', + name: 'Did This', + artist: { name: 'Dino Lenny' }, + imageUrl: 'https://f4.bcbits.com/img/a2817556781_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/beltram-volume-1-remastered', + type: 'album', + name: 'Beltram Volume 1 (Remastered)', + artist: { name: 'Joey Beltram' }, + imageUrl: 'https://f4.bcbits.com/img/a3995752886_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/iyndub01-live', + type: 'album', + name: 'IYNDUB01 (Live)', + artist: { name: 'Saytek' }, + imageUrl: 'https://f4.bcbits.com/img/a2135706255_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/stairway-to-heaven', + type: 'album', + name: 'Stairway To Heaven', + artist: { name: 'Dave Angel' }, + imageUrl: 'https://f4.bcbits.com/img/a3899868680_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/rumble-around', + type: 'album', + name: 'Rumble Around', + artist: { name: 'Nphonix & Matrika' }, + imageUrl: 'https://f4.bcbits.com/img/a0204771946_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/1st-voyage', + type: 'album', + name: '1st Voyage', + artist: { name: 'Dave Angel' }, + imageUrl: 'https://f4.bcbits.com/img/a3826924267_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/dripping-sauce', + type: 'album', + name: 'Dripping Sauce', + artist: { name: 'Subject 13 & Conscious Route' }, + imageUrl: 'https://f4.bcbits.com/img/a0753969393_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/bapteme', + type: 'album', + name: 'Bapteme', + artist: { name: 'Paul Roux' }, + imageUrl: 'https://f4.bcbits.com/img/a0864638093_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/winter-sky', + type: 'album', + name: 'Winter Sky', + artist: { name: 'Hyphen' }, + imageUrl: 'https://f4.bcbits.com/img/a4007624129_16.jpg' + }, + { + url: 'https://synkro.bandcamp.com/album/information-ep', + type: 'album', + name: 'Information EP', + artist: { name: 'Synkro' }, + imageUrl: 'https://f4.bcbits.com/img/a1049512642_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/entrance-dave-angel-remix', + type: 'album', + name: 'Entrance (Dave Angel Remix)', + artist: { name: 'Sun Electric' }, + imageUrl: 'https://f4.bcbits.com/img/a3225864825_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/lost-asia-ep', + type: 'album', + name: 'Lost Asia EP', + artist: { name: 'Chizawa Q' }, + imageUrl: 'https://f4.bcbits.com/img/a1789331695_16.jpg' + }, + { + url: 'https://bataviacollective.bandcamp.com/album/propulsion', + type: 'album', + name: 'Propulsion', + artist: { name: 'Batavia Collective' }, + imageUrl: 'https://f4.bcbits.com/img/a3366817764_16.jpg' + }, + { + url: 'https://loxyink.bandcamp.com/album/spray-trains-of-thought', + type: 'album', + name: 'Spray Trains Of Thought', + artist: { name: 'Loxy & Ink' }, + imageUrl: 'https://f4.bcbits.com/img/a2075117797_16.jpg' + }, + { + url: 'https://progedia.bandcamp.com/album/nobody-screams', + type: 'album', + name: 'Nobody Screams', + artist: { name: 'PROGedia' }, + imageUrl: 'https://f4.bcbits.com/img/a3261990859_16.jpg' + }, + { + url: 'https://blocksescher.bandcamp.com/album/shot-in-the-dark-ep', + type: 'album', + name: 'Shot In The Dark EP', + artist: { name: 'Blocks & Escher' }, + imageUrl: 'https://f4.bcbits.com/img/a3719331016_16.jpg' + }, + { + url: 'https://progedia.bandcamp.com/album/down-incl-the-bug-remix', + type: 'album', + name: 'Down (incl. The Bug Remix)', + artist: { name: 'PROGedia' }, + imageUrl: 'https://f4.bcbits.com/img/a1516236223_16.jpg' + }, + { + url: 'https://progedia.bandcamp.com/album/we-are-the-night', + type: 'album', + name: 'We Are The Night', + artist: { name: 'PROGedia' }, + imageUrl: 'https://f4.bcbits.com/img/a3155966537_16.jpg' + }, + { + url: 'https://bataviacollective.bandcamp.com/album/affirmation-feat-kamga', + type: 'album', + name: 'Affirmation feat. Kamga', + artist: { name: 'Batavia Collective' }, + imageUrl: 'https://f4.bcbits.com/img/a0436292227_16.jpg' + }, + { + url: 'https://loxyink.bandcamp.com/album/manifested-visions', + type: 'album', + name: 'Manifested Visions', + artist: { name: 'Loxy & Ink' }, + imageUrl: 'https://f4.bcbits.com/img/a0075554190_16.jpg' + }, + { + url: 'https://paulwhite.bandcamp.com/album/smile-see-the-light', + type: 'album', + name: 'Smile (See The Light)', + artist: { name: 'Paul White feat. Iyamah & Remi' }, + imageUrl: 'https://f4.bcbits.com/img/a0078770650_16.jpg' + }, + { + url: 'https://saminterface.bandcamp.com/album/pink-dolphins-ep', + type: 'album', + name: 'Pink Dolphins EP', + artist: { name: 'Sam Interface' }, + imageUrl: 'https://f4.bcbits.com/img/a0822271284_16.jpg' + }, + { + url: 'https://forestdrivewest.bandcamp.com/album/terminus-ep', + type: 'album', + name: 'Terminus EP', + artist: { name: 'Forest Drive West' }, + imageUrl: 'https://f4.bcbits.com/img/a3636491375_16.jpg' + }, + { + url: 'https://lone.bandcamp.com/album/lone-x-kettama', + type: 'album', + name: 'Lone x KETTAMA', + artist: { name: 'Lone x KETTAMA' }, + imageUrl: 'https://f4.bcbits.com/img/a3844432643_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/rv-trax-vol-5', + type: 'album', + name: 'RV Trax, Vol. 5', + artist: { name: 'R&S Records' }, + imageUrl: 'https://f4.bcbits.com/img/a2129309811_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/in-order-to-care-deleted', + type: 'album', + name: 'In Order To Care (DELETED)', + artist: { name: 'R&S Records' }, + imageUrl: 'https://f4.bcbits.com/img/a1662919219_16.jpg' + }, + { + url: 'https://architecturalrecs.bandcamp.com/album/planet-is-but-a-dream', + type: 'album', + name: 'Planet Is But A Dream', + artist: { name: 'Architectural' }, + imageUrl: 'https://f4.bcbits.com/img/a2801260386_16.jpg' + }, + { + url: 'https://futurebeatalliance.bandcamp.com/album/never-forever', + type: 'album', + name: 'Never Forever', + artist: { name: 'Future Beat Alliance' }, + imageUrl: 'https://f4.bcbits.com/img/a4088803645_16.jpg' + }, + { + url: 'https://yansima.bandcamp.com/album/tweede-cans', + type: 'album', + name: 'Tweede Cans', + artist: { name: 'Yansima' }, + imageUrl: 'https://f4.bcbits.com/img/a1336844977_16.jpg' + }, + { + url: 'https://more-time.bandcamp.com/album/r-s-presents-more-time-records-vol-1', + type: 'album', + name: 'R&S presents: More Time Records Vol 1', + artist: { name: 'More Time' }, + imageUrl: 'https://f4.bcbits.com/img/a0703436468_16.jpg' + }, + { + url: 'https://spacedimensioncontroller.bandcamp.com/album/love-beyond-the-intersect', + type: 'album', + name: 'Love Beyond The Intersect', + artist: { name: 'Space Dimension Controller' }, + imageUrl: 'https://f4.bcbits.com/img/a4057499431_16.jpg' + }, + { + url: 'https://bartaub.bandcamp.com/album/marble-ya-betta-jam', + type: 'album', + name: 'Marble / Ya Betta Jam', + artist: { name: 'Bärtaub' }, + imageUrl: 'https://f4.bcbits.com/img/a1656383403_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/rv-trax-vol-4', + type: 'album', + name: 'RV Trax Vol 4', + artist: { name: 'Various Artists' }, + imageUrl: 'https://f4.bcbits.com/img/a2374816883_16.jpg' + }, + { + url: 'https://afriqua.bandcamp.com/album/colored', + type: 'album', + name: 'Colored', + artist: { name: 'Afriqua' }, + imageUrl: 'https://f4.bcbits.com/img/a3931309346_16.jpg' + }, + { + url: 'https://sportinglife.bandcamp.com/album/black-diamond-ep', + type: 'album', + name: 'Black Diamond EP', + artist: { name: 'Sporting Life' }, + imageUrl: 'https://f4.bcbits.com/img/a1752543133_16.jpg' + }, + { + url: 'https://djrum.bandcamp.com/album/hard-to-say-tournesol', + type: 'album', + name: 'Hard to Say / Tournesol', + artist: { name: 'Djrum' }, + imageUrl: 'https://f4.bcbits.com/img/a1414096599_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/neverlasting-love', + type: 'album', + name: 'Neverlasting Love', + artist: { name: 'East Of Oceans' }, + imageUrl: 'https://f4.bcbits.com/img/a3334302883_16.jpg' + }, + { + url: 'https://adakaleh.bandcamp.com/album/chemare-cosmic', + type: 'album', + name: 'Chemare cosmică', + artist: { name: 'Ada Kaleh' }, + imageUrl: 'https://f4.bcbits.com/img/a4225860793_16.jpg' + }, + { + url: 'https://6siss.bandcamp.com/album/prisma', + type: 'album', + name: 'Prisma', + artist: { name: '6SISS' }, + imageUrl: 'https://f4.bcbits.com/img/a3746802060_16.jpg' + }, + { + url: 'https://afriqua.bandcamp.com/album/jumpteenth', + type: 'album', + name: 'Jumpteenth', + artist: { name: 'Afriqua' }, + imageUrl: 'https://f4.bcbits.com/img/a3540548131_16.jpg' + }, + { + url: 'https://lostsoulsofsaturn.bandcamp.com/album/lost-souls-of-saturn', + type: 'album', + name: 'Lost Souls Of Saturn', + artist: { name: 'Lost Souls Of Saturn' }, + imageUrl: 'https://f4.bcbits.com/img/a3766159075_16.jpg' + }, + { + url: 'https://yaksound.bandcamp.com/album/termina-ep', + type: 'album', + name: 'Termina EP', + artist: { name: 'Yak' }, + imageUrl: 'https://f4.bcbits.com/img/a2700459812_16.jpg' + }, + { + url: 'https://lostsoulsofsaturn.bandcamp.com/album/the-awakening-inc-james-holden-remix', + type: 'album', + name: 'The Awakening [inc James Holden remix]', + artist: { name: 'Lost Souls Of Saturn' }, + imageUrl: 'https://f4.bcbits.com/img/a0319633005_16.jpg' + }, + { + url: 'https://spacedimensioncontroller.bandcamp.com/album/reseq-ep', + type: 'album', + name: 'ReSEQ EP', + artist: { name: 'Space Dimension Controller' }, + imageUrl: 'https://f4.bcbits.com/img/a2561252353_16.jpg' + }, + { + url: 'https://benhayes.bandcamp.com/album/see-sun-ep', + type: 'album', + name: 'See Sun EP', + artist: { name: 'Ben Hayes' }, + imageUrl: 'https://f4.bcbits.com/img/a2177273264_16.jpg' + }, + { + url: 'https://lakker.bandcamp.com/album/poca', + type: 'album', + name: 'Época', + artist: { name: 'Lakker' }, + imageUrl: 'https://f4.bcbits.com/img/a3481268406_16.jpg' + }, + { + url: 'https://lostsoulsofsaturn.bandcamp.com/album/holes-in-the-holoverse-ep-inc-wolfgang-tillmans-remix', + type: 'album', + name: 'Holes In The Holoverse EP [inc Wolfgang Tillmans remix]', + artist: { name: 'Lost Souls Of Saturn' }, + imageUrl: 'https://f4.bcbits.com/img/a3422639050_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/rv-trax-vol-3', + type: 'album', + name: 'RV Trax Vol 3', + artist: { name: 'R&S Records' }, + imageUrl: 'https://f4.bcbits.com/img/a1395065717_16.jpg' + }, + { + url: 'https://benhayes.bandcamp.com/album/ready-yet-feat-nubya-garcia', + type: 'album', + name: 'Ready Yet (feat Nubya Garcia)', + artist: { name: 'Ben Hayes' }, + imageUrl: 'https://f4.bcbits.com/img/a3751006084_16.jpg' + }, + { + url: 'https://shcaa.bandcamp.com/album/an-ungrateful-death', + type: 'album', + name: 'An Ungrateful Death', + artist: { name: 'Shcaa' }, + imageUrl: 'https://f4.bcbits.com/img/a2877713528_16.jpg' + }, + { + url: 'https://nicolasjaar.bandcamp.com/album/nymphs-triple-lp', + type: 'album', + name: 'Nymphs - Triple LP', + artist: { name: 'Nicolas Jaar' }, + imageUrl: 'https://f4.bcbits.com/img/a3174238166_16.jpg' + }, + { + url: 'https://gabriels.bandcamp.com/album/loyalty', + type: 'album', + name: 'Loyalty', + artist: { name: 'Gabriels' }, + imageUrl: 'https://f4.bcbits.com/img/a0407526073_16.jpg' + }, + { + url: 'https://hermetics.bandcamp.com/album/techgnosis-ep', + type: 'album', + name: 'Techgnosis EP', + artist: { name: 'Hermetics' }, + imageUrl: 'https://f4.bcbits.com/img/a3133044058_16.jpg' + }, + { + url: 'https://lone.bandcamp.com/album/ambivert-tools-one-four-japan-cd', + type: 'album', + name: 'Ambivert Tools One-Four Japan CD', + artist: { name: 'Lone' }, + imageUrl: 'https://f4.bcbits.com/img/a2292587669_16.jpg' + }, + { + url: 'https://marielito.bandcamp.com/album/2000-2005', + type: 'album', + name: '2000-2005', + artist: { name: 'Mariel Ito' }, + imageUrl: 'https://f4.bcbits.com/img/a3837740573_16.jpg' + }, + { + url: 'https://paulwhite.bandcamp.com/album/returning-rival-consoles-remix', + type: 'album', + name: 'Returning (Rival Consoles Remix)', + artist: { name: 'Paul White' }, + imageUrl: 'https://f4.bcbits.com/img/a3475848676_16.jpg' + }, + { + url: 'https://lone.bandcamp.com/album/ambivert-tools-volume-four', + type: 'album', + name: 'Ambivert Tools Volume Four', + artist: { name: 'Lone' }, + imageUrl: 'https://f4.bcbits.com/img/a2380250227_16.jpg' + }, + { + url: 'https://djrum.bandcamp.com/album/portrait-with-firewood', + type: 'album', + name: 'Portrait With Firewood', + artist: { name: 'Djrum' }, + imageUrl: 'https://f4.bcbits.com/img/a1924727225_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/rv-trax-vol-2', + type: 'album', + name: 'RV Trax Vol 2', + artist: { name: 'Various Artists' }, + imageUrl: 'https://f4.bcbits.com/img/a0843418465_16.jpg' + }, + { + url: 'https://paulwhite.bandcamp.com/album/ice-cream-man', + type: 'album', + name: 'Ice Cream Man', + artist: { name: 'Paul White Feat. Shungudzo' }, + imageUrl: 'https://f4.bcbits.com/img/a0488414899_16.jpg' + }, + { + url: 'https://benjamindamage.bandcamp.com/album/malfunction', + type: 'album', + name: 'Malfunction', + artist: { name: 'Benjamin Damage' }, + imageUrl: 'https://f4.bcbits.com/img/a1569426334_16.jpg' + }, + { + url: 'https://afriqua.bandcamp.com/album/vice-principle-ep', + type: 'album', + name: 'Vice/Principle EP', + artist: { name: 'Afriqua' }, + imageUrl: 'https://f4.bcbits.com/img/a1112113317_16.jpg' + }, + { + url: 'https://talaboman.bandcamp.com/album/the-night-land-remixed', + type: 'album', + name: 'The Night Land Remixed', + artist: { name: 'Talaboman' }, + imageUrl: 'https://f4.bcbits.com/img/a1841699442_16.jpg' + }, + { + url: 'https://paulwhite.bandcamp.com/album/rejuvenate', + type: 'album', + name: 'Rejuvenate', + artist: { name: 'Paul White' }, + imageUrl: 'https://f4.bcbits.com/img/a3202037917_16.jpg' + }, + { + url: 'https://karimsahraoui.bandcamp.com/album/plenitude-ep', + type: 'album', + name: 'Plenitude EP', + artist: { name: 'Karim Sahraoui' }, + imageUrl: 'https://f4.bcbits.com/img/a2450943221_16.jpg' + }, + { + url: 'https://blondes.bandcamp.com/album/quality-of-life-struction-remix', + type: 'album', + name: 'Quality Of Life (Struction Remix)', + artist: { name: 'Blondes' }, + imageUrl: 'https://f4.bcbits.com/img/a0167516807_16.jpg' + }, + { + url: 'https://music.maartenvandervleuten.com/album/maarten-van-der-vleuten-presents-integrity-outrage', + type: 'album', + name: 'Maarten van der Vleuten Presents Integrity - Outrage', + artist: { name: 'Maarten van der Vleuten' }, + imageUrl: 'https://f4.bcbits.com/img/a1909170598_16.jpg' + }, + { + url: 'https://lone.bandcamp.com/album/ambivert-tools-volume-three', + type: 'album', + name: 'Ambivert Tools Volume Three', + artist: { name: 'Lone' }, + imageUrl: 'https://f4.bcbits.com/img/a3303507735_16.jpg' + }, + { + url: 'https://acidmondays.bandcamp.com/album/universal-rhythm', + type: 'album', + name: 'Universal Rhythm', + artist: { name: 'ACID MONDAYS' }, + imageUrl: 'https://f4.bcbits.com/img/a2763790377_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/rv-trax', + type: 'album', + name: 'RV Trax', + artist: { name: 'Various Artists' }, + imageUrl: 'https://f4.bcbits.com/img/a2057042953_16.jpg' + }, + { + url: 'https://afriqua.bandcamp.com/album/aleph-ep', + type: 'album', + name: 'Aleph EP', + artist: { name: 'Afriqua' }, + imageUrl: 'https://f4.bcbits.com/img/a3629577728_16.jpg' + }, + { + url: 'https://djrum.bandcamp.com/album/broken-glass-arch', + type: 'album', + name: 'Broken Glass Arch', + artist: { name: 'Djrum' }, + imageUrl: 'https://f4.bcbits.com/img/a4267147376_16.jpg' + }, + { + url: 'https://benjamindamage.bandcamp.com/album/montreal-ep', + type: 'album', + name: 'Montreal EP', + artist: { name: 'Benjamin Damage' }, + imageUrl: 'https://f4.bcbits.com/img/a2262177785_16.jpg' + }, + { + url: 'https://blondes.bandcamp.com/track/kdm-barker-baumecker-remix', + type: 'track', + name: 'KDM (Barker & Baumecker remix)', + artist: { name: 'Blondes' }, + imageUrl: 'https://f4.bcbits.com/img/a4008618888_16.jpg' + }, + { + url: 'https://lostsoulsofsaturn.bandcamp.com/album/bint-el-khandaq-vs-mashrou-leila', + type: 'album', + name: "Bint El Khandaq (vs Mashrou' Leila)", + artist: { name: 'Lost Souls Of Saturn' }, + imageUrl: 'https://f4.bcbits.com/img/a2811752237_16.jpg' + }, + { + url: 'https://lone.bandcamp.com/album/ambivert-tools-volume-two', + type: 'album', + name: 'Ambivert Tools Volume Two', + artist: { name: 'Lone' }, + imageUrl: 'https://f4.bcbits.com/img/a0296687793_16.jpg' + }, + { + url: 'https://blondes.bandcamp.com/album/warmth', + type: 'album', + name: 'Warmth', + artist: { name: 'Blondes' }, + imageUrl: 'https://f4.bcbits.com/img/a2313765301_16.jpg' + }, + { + url: 'https://unknownarchetype.bandcamp.com/album/into-ether', + type: 'album', + name: 'Into Ether', + artist: { name: 'Unknown Archetype' }, + imageUrl: 'https://f4.bcbits.com/img/a2044920027_16.jpg' + }, + { + url: 'https://adakaleh.bandcamp.com/album/palatul-de-cle-tar', + type: 'album', + name: 'Palatul de cleştar', + artist: { name: 'Ada Kaleh' }, + imageUrl: 'https://f4.bcbits.com/img/a3022230788_16.jpg' + }, + { + url: 'https://michelemininni.bandcamp.com/album/rave-oscillations-vortex-stasi', + type: 'album', + name: 'Rave Oscillations / Vortex Stasi', + artist: { name: 'Michele Mininni' }, + imageUrl: 'https://f4.bcbits.com/img/a2445110003_16.jpg' + }, + { + url: 'https://slackk.bandcamp.com/album/a-little-light', + type: 'album', + name: 'A Little Light', + artist: { name: 'Slackk' }, + imageUrl: 'https://f4.bcbits.com/img/a2384206037_16.jpg' + }, + { + url: 'https://lone.bandcamp.com/album/ambivert-tools-volume-one', + type: 'album', + name: 'Ambivert Tools Volume One', + artist: { name: 'Lone' }, + imageUrl: 'https://f4.bcbits.com/img/a0389879254_16.jpg' + }, + { + url: 'https://sportinglife.bandcamp.com/album/slam-dunk-remixes', + type: 'album', + name: 'Slam Dunk Remixes', + artist: { name: 'Sporting Life' }, + imageUrl: 'https://f4.bcbits.com/img/a2896007633_16.jpg' + }, + { + url: 'https://spacedimensioncontroller.bandcamp.com/album/exostack', + type: 'album', + name: 'EXOSTACK', + artist: { name: 'Space Dimension Controller' }, + imageUrl: 'https://f4.bcbits.com/img/a2211118179_16.jpg' + }, + { + url: 'https://talaboman.bandcamp.com/album/the-night-land', + type: 'album', + name: 'The Night Land', + artist: { name: 'Talaboman' }, + imageUrl: 'https://f4.bcbits.com/img/a0709795804_16.jpg' + }, + { + url: 'https://paulwhite.bandcamp.com/album/accelerator', + type: 'album', + name: 'Accelerator', + artist: { name: 'Paul White ft Danny Brown' }, + imageUrl: 'https://f4.bcbits.com/img/a3525288854_16.jpg' + }, + { + url: 'https://unknownarchetype.bandcamp.com/album/tripp-ep', + type: 'album', + name: 'TRIPP EP', + artist: { name: 'Unknown Archetype' }, + imageUrl: 'https://f4.bcbits.com/img/a0827482149_16.jpg' + }, + { + url: 'https://sportinglife.bandcamp.com/album/slam-dunk-vinyl-lp', + type: 'album', + name: 'Slam Dunk - Vinyl LP', + artist: { name: 'Sporting Life' }, + imageUrl: 'https://f4.bcbits.com/img/a2750684373_16.jpg' + }, + { + url: 'https://paulwhite.bandcamp.com/album/everything-youve-forgotten-free-beat-tape', + type: 'album', + name: "Everything You've Forgotten (Free Beat Tape)", + artist: { name: 'Paul White' }, + imageUrl: 'https://f4.bcbits.com/img/a0945396280_16.jpg' + }, + { + url: 'https://randsrecords.bandcamp.com/album/meditation-will-manifest', + type: 'album', + name: 'Meditation Will Manifest', + artist: { name: 'Josh Wink' }, + imageUrl: 'https://f4.bcbits.com/img/a0983137742_16.jpg' + }, + ... 93 more items +] + diff --git a/examples/band/getInfo.ts b/examples/band/getInfo.ts new file mode 100644 index 0000000..4c689bf --- /dev/null +++ b/examples/band/getInfo.ts @@ -0,0 +1,28 @@ +import bcfetch from '../../'; +import util from 'util'; + +const artistUrl = 'https://macmccaughan.bandcamp.com'; +const labelUrl = 'https://mergerecords.bandcamp.com'; + +const artistInfoParams = { + bandUrl: artistUrl, + imageFormat: 'art_app_large', + labelId: 1415932000 +}; + +const labelInfoParams = { + bandUrl: labelUrl, + imageFormat: 'art_app_large' +}; + +bcfetch.band.getInfo(artistInfoParams).then((results) => { + console.log(`Artist URL: ${artistUrl}`); + console.log(util.inspect(results, false, null, false)); + console.log(); +}); + +bcfetch.band.getInfo(labelInfoParams).then((results) => { + console.log(`Label URL: ${labelUrl}`); + console.log(util.inspect(results, false, null, false)); + console.log(); +}); diff --git a/examples/getArtistOrLabelInfo_output.txt b/examples/band/getInfo_output.txt similarity index 68% rename from examples/getArtistOrLabelInfo_output.txt rename to examples/band/getInfo_output.txt index 161b6d8..e5eb77f 100644 --- a/examples/getArtistOrLabelInfo_output.txt +++ b/examples/band/getInfo_output.txt @@ -1,20 +1,29 @@ -Artist URL: https://macmccaughan.bandcamp.com -{ type: 'artist', - name: 'Mac McCaughan', - url: 'https://macmccaughan.bandcamp.com', - description: 'Superchunk, Portastatic, and now this...\n\nFor booking contact www.groundcontroltouring.com\n For more records go to www.mergerecords.com', - location: 'Chapel Hill, North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0004961611_16.jpg', - label: - { name: 'Merge Records', - url: 'https://mergerecords.bandcamp.com' } } - Label URL: https://mergerecords.bandcamp.com -{ type: 'label', +{ + type: 'label', name: 'Merge Records', - url: 'https://mergerecords.bandcamp.com', description: 'Merge Records is an independent record label based in Durham, North Carolina. It was founded in 1989 by Laura Ballance and Mac McCaughan. It began as a way to release music from their band Superchunk and music created by friends, and has expanded to include artists from around the world and records reaching the top of the Billboard music charts.', + url: 'https://mergerecords.bandcamp.com', location: 'Durham, North Carolina', imageUrl: 'https://f4.bcbits.com/img/0018287817_16.jpg', - labelId: 1415932000 } + labelId: 1415932000 +} + +Artist URL: https://macmccaughan.bandcamp.com +{ + type: 'artist', + name: 'Mac McCaughan', + description: 'Superchunk, Portastatic, and now this...\n' + + '\n' + + 'For booking contact www.groundcontroltouring.com\n' + + ' For more records go to www.mergerecords.com', + url: 'https://macmccaughan.bandcamp.com', + location: 'Chapel Hill, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/0004961611_16.jpg', + label: { + type: 'label', + name: 'Merge Records', + url: 'https://mergerecords.bandcamp.com' + } +} diff --git a/examples/band/getLabelArtists.ts b/examples/band/getLabelArtists.ts new file mode 100644 index 0000000..6ba90ba --- /dev/null +++ b/examples/band/getLabelArtists.ts @@ -0,0 +1,13 @@ +import bcfetch from '../../'; +import util from 'util'; + +const labelUrl = 'https://mergerecords.bandcamp.com'; + +const params = { + labelUrl, + imageFormat: 'art_app_large' +}; + +bcfetch.band.getLabelArtists(params).then((results) => { + console.log(util.inspect(results, false, null, false)); +}); diff --git a/examples/band/getLabelArtists_output.txt b/examples/band/getLabelArtists_output.txt new file mode 100644 index 0000000..e7470ab --- /dev/null +++ b/examples/band/getLabelArtists_output.txt @@ -0,0 +1,603 @@ +[ + { + name: 'A Giant Dog', + url: 'https://agiantdog.bandcamp.com', + location: 'Austin, Texas', + imageUrl: 'https://f4.bcbits.com/img/0000651146_16.jpg' + }, + { + name: 'Hiss Golden Messenger', + url: 'https://hissgoldenmessenger.bandcamp.com', + location: 'North Carolina', + imageUrl: 'https://f4.bcbits.com/img/0024016536_16.jpg' + }, + { + name: 'Wye Oak', + url: 'https://wyeoak.bandcamp.com', + location: 'Baltimore, Maryland', + imageUrl: 'https://f4.bcbits.com/img/0032186979_16.jpg' + }, + { + name: 'William Tyler', + url: 'https://williamtyler.bandcamp.com', + location: 'Los Angeles, California', + imageUrl: 'https://f4.bcbits.com/img/0018844022_16.jpg' + }, + { + name: 'Tanlines', + url: 'https://tanlinestheband.bandcamp.com', + location: 'New York, New York', + imageUrl: 'https://f4.bcbits.com/img/0031492685_16.jpg' + }, + { + name: 'The New Pornographers', + url: 'https://thenewpornos.bandcamp.com', + location: 'Vancouver, Washington', + imageUrl: 'https://f4.bcbits.com/img/0031022314_16.jpg' + }, + { + name: 'Neutral Milk Hotel', + url: 'https://neutralmilkhotel.bandcamp.com', + location: '', + imageUrl: 'https://f4.bcbits.com/img/0013466114_16.jpg' + }, + { + name: 'Fruit Bats', + url: 'https://fruit-bats.bandcamp.com', + location: '', + imageUrl: 'https://f4.bcbits.com/img/0031581619_16.jpg' + }, + { + name: 'Fucked Up', + url: 'https://fuckedup.bandcamp.com', + location: 'Toronto, Ontario', + imageUrl: 'https://f4.bcbits.com/img/0029719426_16.jpg' + }, + { + name: 'H.C. McEntire', + url: 'https://hcmcentire.bandcamp.com', + location: 'Durham, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/0020946952_16.jpg' + }, + { + name: 'Sneaks', + url: 'https://sneaks.bandcamp.com', + location: 'Washington, D.C.', + imageUrl: 'https://f4.bcbits.com/img/0019714164_16.jpg' + }, + { + name: 'Titus Andronicus', + url: 'https://titusandronicus.bandcamp.com', + location: '', + imageUrl: 'https://f4.bcbits.com/img/0029288983_16.jpg' + }, + { + name: 'Archers of Loaf', + url: 'https://archersofloaf.bandcamp.com', + location: 'North Carolina', + imageUrl: 'https://f4.bcbits.com/img/0029225752_16.jpg' + }, + { + name: 'Lambchop', + url: 'https://lambchop.bandcamp.com', + location: 'Nashville, Tennessee', + imageUrl: 'https://f4.bcbits.com/img/0024120187_16.jpg' + }, + { + name: 'Tall Dwarfs', + url: 'https://talldwarfs.bandcamp.com', + location: 'New Zealand', + imageUrl: 'https://f4.bcbits.com/img/0028914236_16.jpg' + }, + { + name: 'the Mountain Goats', + url: 'https://themountaingoats.bandcamp.com', + location: 'Durham, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/0028851781_16.jpg' + }, + { + name: 'Friendship', + url: 'https://friendshipphl.bandcamp.com', + location: 'Philadelphia, Pennsylvania', + imageUrl: 'https://f4.bcbits.com/img/0028727388_16.jpg' + }, + { + name: 'Redd Kross', + url: 'https://reddkross.bandcamp.com', + location: 'Hawthorne, California', + imageUrl: 'https://f4.bcbits.com/img/0028555577_16.jpg' + }, + { + name: 'Hollie Cook', + url: 'https://holliecook.bandcamp.com', + location: 'London, UK', + imageUrl: 'https://f4.bcbits.com/img/0028056444_16.jpg' + }, + { + name: 'Superchunk', + url: 'https://superchunk.bandcamp.com', + location: 'Chapel Hill, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/0026971728_16.jpg' + }, + { + name: 'Ibibio Sound Machine', + url: 'https://ibibiosoundmachine.bandcamp.com', + location: 'UK', + imageUrl: 'https://f4.bcbits.com/img/0027398052_16.jpg' + }, + { + name: 'Destroyer', + url: 'https://destroyer.bandcamp.com', + location: 'Vancouver, British Columbia', + imageUrl: 'https://f4.bcbits.com/img/0027319221_16.jpg' + }, + { + name: 'Carson McHone', + url: 'https://carsonmchone.bandcamp.com', + location: 'Austin, Texas', + imageUrl: 'https://f4.bcbits.com/img/0026448389_16.jpg' + }, + { + name: 'The Magnetic Fields', + url: 'https://themagneticfields.bandcamp.com', + location: 'New York, New York', + imageUrl: 'https://f4.bcbits.com/img/0014275749_16.jpg' + }, + { + name: 'Mac McCaughan', + url: 'https://macmccaughan.bandcamp.com', + location: 'Chapel Hill, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/0004961611_16.jpg' + }, + { + name: 'Caribou', + url: 'https://caribouband.bandcamp.com', + location: 'London, UK', + imageUrl: 'https://f4.bcbits.com/img/0018041279_16.jpg' + }, + { + name: 'DAWN', + url: 'https://dawnrichard.bandcamp.com', + location: 'Los Angeles, California', + imageUrl: 'https://f4.bcbits.com/img/0030483218_16.jpg' + }, + { + name: 'Torres', + url: 'https://torrestorrestorres.bandcamp.com', + location: 'New York, New York', + imageUrl: 'https://f4.bcbits.com/img/0024864649_16.jpg' + }, + { + name: 'Reigning Sound', + url: 'https://reigningsound.bandcamp.com', + location: 'Asheville, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/0023915882_16.jpg' + }, + { + name: 'Cable Ties', + url: 'https://cableties.bandcamp.com', + location: 'Melbourne, Australia', + imageUrl: 'https://f4.bcbits.com/img/0008448159_16.jpg' + }, + { + name: 'Teenage Fanclub', + url: 'https://teenage-fanclub.bandcamp.com', + location: 'Glasgow, UK', + imageUrl: 'https://f4.bcbits.com/img/0024101151_16.jpg' + }, + { + name: 'Will Butler', + url: 'https://willbutler.bandcamp.com', + location: 'Brooklyn, New York', + imageUrl: 'https://f4.bcbits.com/img/0024918652_16.jpg' + }, + { + name: 'Waxahatchee', + url: 'https://waxahatchee.bandcamp.com', + location: 'Philadelphia, Pennsylvania', + imageUrl: 'https://f4.bcbits.com/img/0018456389_16.jpg' + }, + { + name: 'The Clean', + url: 'https://theclean.bandcamp.com', + location: 'New Zealand', + imageUrl: 'https://f4.bcbits.com/img/0023754516_16.jpg' + }, + { + name: 'Mike Krol', + url: 'https://mikekrol.bandcamp.com', + location: 'Los Angeles, California', + imageUrl: 'https://f4.bcbits.com/img/0010450062_16.jpg' + }, + { + name: 'Bob Mould', + url: 'https://bobmould.bandcamp.com', + location: 'San Francisco, California', + imageUrl: 'https://f4.bcbits.com/img/0020299197_16.jpg' + }, + { + name: 'The Clientele', + url: 'https://theclientele.bandcamp.com', + location: 'London, UK', + imageUrl: 'https://f4.bcbits.com/img/0010580560_16.jpg' + }, + { + name: 'Lou Barlow', + url: 'https://loubarlow.bandcamp.com', + location: 'Massachusetts', + imageUrl: 'https://f4.bcbits.com/img/0029880016_16.jpg' + }, + { + name: 'Mikal Cronin', + url: 'https://mikalcronin.bandcamp.com', + location: 'Los Angeles, California', + imageUrl: 'https://f4.bcbits.com/img/0016983063_16.jpg' + }, + { + name: 'Jade Hairpins', + url: 'https://jadehairpins.bandcamp.com', + location: '', + imageUrl: 'https://f4.bcbits.com/img/0018584374_16.jpg' + }, + { + name: 'Sweet Spirit', + url: 'https://sweetspirittheband.bandcamp.com', + location: 'Austin, Texas', + imageUrl: 'https://f4.bcbits.com/img/0019349627_16.jpg' + }, + { + name: 'Mt. Wilson Repeater', + url: 'https://mtwilsonrptr.bandcamp.com', + location: '', + imageUrl: 'https://f4.bcbits.com/img/0019621062_16.jpg' + }, + { + name: 'The Music Tapes', + url: 'https://themusictapes.bandcamp.com', + location: 'Athens, Georgia', + imageUrl: 'https://f4.bcbits.com/img/0017776773_16.jpg' + }, + { + name: 'Martin Frawley', + url: 'https://martinfrawley.bandcamp.com', + location: 'Australia', + imageUrl: 'https://f4.bcbits.com/img/0031110755_16.jpg' + }, + { + name: 'Polvo', + url: 'https://polvonc.bandcamp.com', + location: 'Chapel Hill, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/0018801929_16.jpg' + }, + { + name: 'Richard Buckner', + url: 'https://richardbuckner.bandcamp.com', + location: 'New York, New York', + imageUrl: 'https://f4.bcbits.com/img/0017863325_16.jpg' + }, + { + name: 'The Love Language', + url: 'https://thelovelanguage.bandcamp.com', + location: 'Chapel Hill, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/0013444279_16.jpg' + }, + { + name: 'Little Scream', + url: 'https://littlescream.bandcamp.com', + location: 'Montréal, Québec', + imageUrl: 'https://f4.bcbits.com/img/0005272073_16.jpg' + }, + { + name: 'Eric Bachmann', + url: 'https://ericbachmann.bandcamp.com', + location: 'North Carolina', + imageUrl: 'https://f4.bcbits.com/img/0017551127_16.jpg' + }, + { + name: 'Gauche', + url: 'https://g-a-u-c-h-e.bandcamp.com', + location: 'Washington, D.C.', + imageUrl: 'https://f4.bcbits.com/img/0016153963_16.jpg' + }, + { + name: 'Joyero', + url: 'https://joyero.bandcamp.com', + location: 'Durham, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/0016703523_16.jpg' + }, + { + name: 'David Kilgour', + url: 'https://davidkilgour.bandcamp.com', + location: 'Dunedin', + imageUrl: 'https://f4.bcbits.com/img/0006370477_16.jpg' + }, + { + name: 'Apex Manor', + url: 'https://apexmanor.bandcamp.com', + location: 'Los Angeles, California', + imageUrl: 'https://f4.bcbits.com/img/0015921186_16.jpg' + }, + { + name: 'Ex Hex', + url: 'https://exhexband.bandcamp.com', + location: 'Washington, D.C.', + imageUrl: 'https://f4.bcbits.com/img/0015244159_16.jpg' + }, + { + name: 'Imperial Teen', + url: 'https://imperialteen.bandcamp.com', + location: 'San Francisco, California', + imageUrl: 'https://f4.bcbits.com/img/0016255829_16.jpg' + }, + { + name: 'SACRED//PAWS', + url: 'https://sacredpaws.bandcamp.com', + location: 'Glasgow, UK', + imageUrl: 'https://f4.bcbits.com/img/0000041645_16.jpg' + }, + { + name: 'The Broken West', + url: 'https://thebrokenwest.bandcamp.com', + location: 'Los Angeles, California', + imageUrl: 'https://f4.bcbits.com/img/0016185383_16.jpg' + }, + { + name: 'Telekinesis', + url: 'https://telekinesis.bandcamp.com', + location: 'Seattle, Washington', + imageUrl: 'https://f4.bcbits.com/img/0014904916_16.jpg' + }, + { + name: 'The Spinanes', + url: 'https://thespinanes.bandcamp.com', + location: 'Portland, Oregon', + imageUrl: 'https://f4.bcbits.com/img/0014533028_16.jpg' + }, + { + name: "Swearin'", + url: 'https://swearin.bandcamp.com', + location: 'Philadelphia, Pennsylvania', + imageUrl: 'https://f4.bcbits.com/img/0019671909_16.jpg' + }, + { + name: 'Tracyanne & Danny', + url: 'https://tracyanneanddanny.bandcamp.com', + location: 'UK', + imageUrl: 'https://f4.bcbits.com/img/0012612375_16.jpg' + }, + { + name: 'Spider Bags', + url: 'https://spiderbags.bandcamp.com', + location: 'Chapel Hill, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/0006370483_16.jpg' + }, + { + name: 'Ought', + url: 'https://ought.bandcamp.com', + location: 'Montréal, Québec', + imageUrl: 'https://f4.bcbits.com/img/0011617874_16.jpg' + }, + { + name: 'She & Him', + url: 'https://sheandhim.bandcamp.com', + location: 'Los Angeles, California', + imageUrl: 'https://f4.bcbits.com/img/0028704726_16.jpg' + }, + { + name: 'The Essex Green', + url: 'https://theessexgreen.bandcamp.com', + location: 'Brooklyn, New York', + imageUrl: 'https://f4.bcbits.com/img/0013167432_16.jpg' + }, + { + name: 'The Rock*A*Teens', + url: 'https://therockateens.bandcamp.com', + location: 'Atlanta, Georgia', + imageUrl: 'https://f4.bcbits.com/img/0013223966_16.jpg' + }, + { + name: 'Crooked Fingers', + url: 'https://crookedfingers.bandcamp.com', + location: 'North Carolina', + imageUrl: 'https://f4.bcbits.com/img/0013404323_16.jpg' + }, + { + name: 'Spoon', + url: 'https://spoontheband.bandcamp.com', + location: 'Austin, Texas', + imageUrl: 'https://f4.bcbits.com/img/0021448647_16.jpg' + }, + { + name: 'Coco Hames', + url: 'https://cocohames.bandcamp.com', + location: 'Memphis, Tennessee', + imageUrl: 'https://f4.bcbits.com/img/0009197807_16.jpg' + }, + { + name: 'Allison Crutchfield', + url: 'https://allisoncrutchfield.bandcamp.com', + location: 'Philadelphia, Pennsylvania', + imageUrl: 'https://f4.bcbits.com/img/0008598378_16.jpg' + }, + { + name: 'M. Ward', + url: 'https://m-ward.bandcamp.com', + location: 'Portland, Oregon', + imageUrl: 'https://f4.bcbits.com/img/0018177684_16.jpg' + }, + { + name: 'Mount Moriah', + url: 'https://mountmoriah.bandcamp.com', + location: 'Durham, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/0006370463_16.jpg' + }, + { + name: 'Benji Hughes', + url: 'https://benjihughes.bandcamp.com', + location: 'Charlotte, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/0006362688_16.jpg' + }, + { + name: 'King Khan', + url: 'https://khannibalism.bandcamp.com', + location: 'Berlin, Germany', + imageUrl: 'https://f4.bcbits.com/img/0003997501_16.jpg' + }, + { + name: 'Drive Like Jehu', + url: 'https://drivelikejehu.bandcamp.com', + location: 'San Diego, California', + imageUrl: 'https://f4.bcbits.com/img/0007577688_16.jpg' + }, + { + name: 'HeCTA', + url: 'https://hecta.bandcamp.com', + location: 'Nashville, Tennessee', + imageUrl: 'https://f4.bcbits.com/img/0006073962_16.jpg' + }, + { + name: 'Seaweed', + url: 'https://seaweed.bandcamp.com', + location: 'Tacoma, Washington', + imageUrl: 'https://f4.bcbits.com/img/0006209491_16.jpg' + }, + { + name: 'Portastatic', + url: 'https://portastatic.bandcamp.com', + location: 'Chapel Hill, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/0006164342_16.jpg' + }, + { + name: 'East River Pipe', + url: 'https://eastriverpipe.bandcamp.com', + location: 'New York, New York', + imageUrl: 'https://f4.bcbits.com/img/0006133419_16.jpg' + }, + { + name: 'Volcano Suns', + url: 'https://volcanosuns.bandcamp.com', + location: 'Boston, Massachusetts', + imageUrl: 'https://f4.bcbits.com/img/0006581067_16.jpg' + }, + { + name: 'Flesh Wounds', + url: 'https://fleshwounds.bandcamp.com', + location: 'Carrboro, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/0006150428_16.jpg' + }, + { + name: 'Breadwinner', + url: 'https://breadwinner.bandcamp.com', + location: 'Richmond, Virginia', + imageUrl: 'https://f4.bcbits.com/img/0006382307_16.jpg' + }, + { + name: 'Vertical Scratchers', + url: 'https://verticalscratchers.bandcamp.com', + location: 'California', + imageUrl: 'https://f4.bcbits.com/img/0006149958_16.jpg' + }, + { + name: 'Hospitality', + url: 'https://hospitality.bandcamp.com', + location: 'Brooklyn, New York', + imageUrl: 'https://f4.bcbits.com/img/0006370459_16.jpg' + }, + { + name: 'SAINT RICH', + url: 'https://saintrich.bandcamp.com', + location: 'New Jersey', + imageUrl: 'https://f4.bcbits.com/img/0001635145_16.jpg' + }, + { + name: 'Future Bible Heroes', + url: 'https://futurebibleheroes.bandcamp.com', + location: 'New York, New York', + imageUrl: 'https://f4.bcbits.com/img/0006151240_16.jpg' + }, + { + name: 'Eleanor Friedberger', + url: 'https://eleanorfriedberger.bandcamp.com', + location: 'New York, New York', + imageUrl: 'https://f4.bcbits.com/img/0006151654_16.jpg' + }, + { + name: 'Barren Girls', + url: 'https://barrengirls.bandcamp.com', + location: 'Raleigh, North Carolina', + imageUrl: 'https://f4.bcbits.com/img/0006156356_16.jpg' + }, + { + name: 'Amor de Días', + url: 'https://amordedias.bandcamp.com', + location: 'London, UK', + imageUrl: 'https://f4.bcbits.com/img/0006156859_16.jpg' + }, + { + name: 'Radar Brothers', + url: 'https://radarbrothers.bandcamp.com', + location: 'Los Angeles, California', + imageUrl: 'https://f4.bcbits.com/img/0006580634_16.jpg' + }, + { + name: 'Flock of Dimes', + url: 'https://flockofdimes.bandcamp.com', + location: 'Baltimore, Maryland', + imageUrl: 'https://f4.bcbits.com/img/0020591675_16.jpg' + }, + { + name: 'Wild Flag', + url: 'https://wildflag.bandcamp.com', + location: 'Portland, Oregon', + imageUrl: 'https://f4.bcbits.com/img/0006158205_16.jpg' + }, + { + name: 'The Extra Lens', + url: 'https://theextralens.bandcamp.com', + location: '', + imageUrl: 'https://f4.bcbits.com/img/0006158864_16.jpg' + }, + { + name: 'Julian Koster', + url: 'https://juliankoster.bandcamp.com', + location: 'Athens, Georgia', + imageUrl: 'https://f4.bcbits.com/img/0006164232_16.jpg' + }, + { + name: 'Conor Oberst', + url: 'https://conoroberst.bandcamp.com', + location: 'Omaha, Nebraska', + imageUrl: 'https://f4.bcbits.com/img/0014736743_16.jpg' + }, + { + name: 'Annie Hayden', + url: 'https://anniehayden.bandcamp.com', + location: 'New Jersey', + imageUrl: 'https://f4.bcbits.com/img/0006580804_16.jpg' + }, + { + name: 'Tenement Halls', + url: 'https://tenementhalls.bandcamp.com', + location: 'Atlanta, Georgia', + imageUrl: 'https://f4.bcbits.com/img/0006171792_16.jpg' + }, + { + name: 'Shark Quest', + url: 'https://sharkquest.bandcamp.com', + location: 'North Carolina', + imageUrl: 'https://f4.bcbits.com/img/0006172106_16.jpg' + }, + { + name: 'Matt Suggs', + url: 'https://mattsuggs.bandcamp.com', + location: 'Visalia, California', + imageUrl: 'https://f4.bcbits.com/img/0006172356_16.jpg' + }, + { + name: 'The Karl Hendricks Trio', + url: 'https://thekarlhendrickstrio.bandcamp.com', + location: 'Pittsburgh, Pennsylvania', + imageUrl: 'https://f4.bcbits.com/img/0006172432_16.jpg' + }, + ... 15 more items +] diff --git a/examples/discover.js b/examples/discover.js deleted file mode 100644 index 3f7693a..0000000 --- a/examples/discover.js +++ /dev/null @@ -1,15 +0,0 @@ -const bcfetch = require('../'); -const util = require('util'); - -const params = { - genre: 'ambient' -} - -const options = { - albumImageFormat: 2, - artistImageFormat: 'bio_featured' -}; - -bcfetch.discover(params, options).then( results => { - console.log(util.inspect(results, false, null, false)); -}); \ No newline at end of file diff --git a/examples/discover_output.txt b/examples/discover_output.txt deleted file mode 100644 index b5ebf41..0000000 --- a/examples/discover_output.txt +++ /dev/null @@ -1,730 +0,0 @@ -{ items: - [ { type: 'album', - name: 'Kofū II / 古風 II', - url: 'https://meitei.bandcamp.com/album/kofu-ii-ii', - imageUrl: 'https://f4.bcbits.com/img/a612322967_2.jpg', - genre: 'ambient', - artist: - { name: 'Meitei / 冥丁', - url: 'https://meitei.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/26404121_28.jpg' }, - location: 'Hiroshima, Japan', - featuredTrack: - { name: 'Happyaku-yachō / 八百八町', - duration: 187.457, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/8104d9c2511ae8cb8a4fdb6546620782/mp3-128/2665809227?p=0&ts=1634706473&t=c5fc7c346942f6b8847bbbba40b1145a1d733f7b&token=1634706473_3bdc73ba630eef04eb5362fcf27c75e0286ca1b9' } } }, - { type: 'album', - name: 'Everywhere at the end of time', - url: 'https://thecaretaker.bandcamp.com/album/everywhere-at-the-end-of-time', - imageUrl: 'https://f4.bcbits.com/img/a2383326070_2.jpg', - genre: 'ambient', - artist: - { name: 'The Caretaker', - url: 'https://thecaretaker.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/8262351_28.jpg' }, - location: 'Manchester, UK', - featuredTrack: - { name: 'A1 - It\'s just a burning memory', - duration: 212.213, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/5374bf89169c62150232c9813a977c9a/mp3-128/1541188837?p=0&ts=1634706473&t=d15d20218584886762fe3df6865d5dcac508b8a3&token=1634706473_154201e2e9a842268337b95996476cab3d66dfe3' } } }, - { type: 'album', - name: 'Repetition Hymns', - url: 'https://pitp.bandcamp.com/album/repetition-hymns', - imageUrl: 'https://f4.bcbits.com/img/a3883081516_2.jpg', - genre: 'ambient', - artist: - { name: 'Black Swan', - url: 'https://pitp.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/21692848_28.jpg' }, - location: 'Indianapolis, Indiana', - featuredTrack: - { name: 'The Innocence of Sleep', - duration: 242.992, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/7980fa949580f8ee110f8b40c5cf548f/mp3-128/3377175240?p=0&ts=1634706473&t=ab76b234d18336875ca166f1f075bc25433b04ca&token=1634706473_d498b35aec0b854d7a39e86d441e73c27574e538' } } }, - { type: 'album', - name: 'Night Sobbed a Potion Diseased', - url: 'https://dungeonsdeeprecords.bandcamp.com/album/night-sobbed-a-potion-diseased', - imageUrl: 'https://f4.bcbits.com/img/a3442827912_2.jpg', - genre: 'ambient', - artist: - { name: 'Wallachian Cobwebs', - url: 'https://dungeonsdeeprecords.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/26390472_28.jpg' }, - location: null, - featuredTrack: - { name: 'The Electrical Secrets of Heaven', - duration: 444.083, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/152994e13c92e5162e23cabebbeaf31d/mp3-128/2270160924?p=0&ts=1634706473&t=73d2e47316817284cd27848e97cf4779c804021b&token=1634706473_864da9da2829bb0296e79752665cccf5e35c6287' } } }, - { type: 'album', - name: 'An empty bliss beyond this World', - url: 'https://thecaretaker.bandcamp.com/album/an-empty-bliss-beyond-this-world', - imageUrl: 'https://f4.bcbits.com/img/a4087775263_2.jpg', - genre: 'ambient', - artist: - { name: 'The Caretaker', - url: 'https://thecaretaker.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/8262351_28.jpg' }, - location: 'Manchester, UK', - featuredTrack: - { name: 'All you are going to want to do is get back there', - duration: 226.236, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/153845156a25011e0d4e3939103f8f02/mp3-128/1186879202?p=0&ts=1634706473&t=fa8393dc2872c805c3d3ff1f0833e4b7d0ea2a87&token=1634706473_bcad6a1022377cf1d6d776ddd6b27f3e351566d4' } } }, - { type: 'album', - name: 'mountain ambient III', - url: 'https://0foundation.bandcamp.com/album/mountain-ambient-iii', - imageUrl: 'https://f4.bcbits.com/img/a3930873931_2.jpg', - genre: 'ambient', - artist: - { name: '.foundation', - url: 'https://0foundation.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/19851094_28.jpg' }, - location: 'Dallas, Texas', - featuredTrack: - { name: 'terminalia', - duration: 551.186, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/dcee0d7e2181d12f5e0ed53d50eebfcb/mp3-128/2718293832?p=0&ts=1634706473&t=ab7f2d22a840a239ada2a11dc463b5f9c312a6c3&token=1634706473_79490e083aedf1e35283ff49995b9afeda227e33' } } }, - { type: 'album', - name: 'The Stains of the Embodied Sacrifice (expanded edition 2021)', - url: 'https://raisondetre.bandcamp.com/album/the-stains-of-the-embodied-sacrifice-expanded-edition-2021', - imageUrl: 'https://f4.bcbits.com/img/a171448036_2.jpg', - genre: 'ambient', - artist: - { name: 'raison d\'être', - url: 'https://raisondetre.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/5576_28.jpg' }, - location: 'Sweden', - featuredTrack: - { name: 'The Spirit Will Not Share The Guilt', - duration: 504.426, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/e99e8f8d75af1379ee77fe04d58b6a8c/mp3-128/3313990578?p=0&ts=1634706473&t=0d983188e702827d9231cd79180e5ba5f32c974b&token=1634706473_bf446b87d82b6ea03c7917da4a9b7879378efcb2' } } }, - { type: 'album', - name: 'The Empty Hollow Unfolds (expanded edition 2021)', - url: 'https://raisondetre.bandcamp.com/album/the-empty-hollow-unfolds-expanded-edition-2021', - imageUrl: 'https://f4.bcbits.com/img/a1088429176_2.jpg', - genre: 'ambient', - artist: - { name: 'raison d\'être', - url: 'https://raisondetre.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/5576_28.jpg' }, - location: 'Sweden', - featuredTrack: - { name: 'The Slow Ascent', - duration: 337.383, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/3038515eaa095fa03fceb155fb6f0e81/mp3-128/3749936751?p=0&ts=1634706473&t=bec5eac4045db835ab5d065e5522e1225c90f25c&token=1634706473_2f50d19327e058f0dd6251e21c83c5b8e6eff738' } } }, - { type: 'album', - name: 'Return From Tomorrow', - url: 'https://oliviaway.bandcamp.com/album/return-from-tomorrow', - imageUrl: 'https://f4.bcbits.com/img/a2381991646_2.jpg', - genre: 'ambient', - artist: - { name: 'oliviaway', - url: 'https://oliviaway.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/15039429_28.jpg' }, - location: 'London, UK', - featuredTrack: - { name: 'Return From Tomorrow', - duration: 328.895, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/a7e838968debd731cab19a4f542582d7/mp3-128/485734895?p=0&ts=1634706473&t=28b230aca6663e28a2ff1a27927291cc83f7cf11&token=1634706473_a2b63584467f09e7a0393af8f6cac8189748c663' } } }, - { type: 'album', - name: 'Leaves Painted In A Darker Blue', - url: 'https://lontanoseries.bandcamp.com/album/leaves-painted-in-a-darker-blue', - imageUrl: 'https://f4.bcbits.com/img/a1886935783_2.jpg', - genre: 'ambient', - artist: - { name: 'Blanket Swimming', - url: 'https://lontanoseries.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/14016527_28.jpg' }, - location: 'Sardinia, Italy', - featuredTrack: - { name: 'The Rains Began', - duration: 400.591, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/cffd5e00e6a1d13e16c8715f89409db2/mp3-128/3429221434?p=0&ts=1634706473&t=95f9a640c4a297207f6d5e18a9ad0b2f1667f301&token=1634706473_7eac09701c6621a9acea057c7748901260038b5b' } } }, - { type: 'album', - name: 'Niflheim', - url: 'https://cryochamber.bandcamp.com/album/niflheim', - imageUrl: 'https://f4.bcbits.com/img/a1760368140_2.jpg', - genre: 'ambient', - artist: - { name: 'Ager Sonus', - url: 'https://cryochamber.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/4516843_28.jpg' }, - location: 'Oregon', - featuredTrack: - { name: 'Going North', - duration: 327.134, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/acdb930e7756bc696610131c9c66771b/mp3-128/2079600906?p=0&ts=1634706473&t=f815b4a39948cb14d79bbdaee53f9aca3681d463&token=1634706473_6d349380a369953744d44400b9c4f910744e942c' } } }, - { type: 'album', - name: 'Hexerei Im Zwielicht Der Finsternis', - url: 'https://infinitefog.bandcamp.com/album/hexerei-im-zwielicht-der-finsternis', - imageUrl: 'https://f4.bcbits.com/img/a2193963070_2.jpg', - genre: 'ambient', - artist: - { name: 'Aghast', - url: 'https://infinitefog.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/4297391_28.jpg' }, - location: 'Sankt Koloman, Austria', - featuredTrack: - { name: 'Enthral', - duration: 98.8267, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/58714a595a0942a261ec06ca7bcd3de3/mp3-128/3535471356?p=0&ts=1634706473&t=3c1eae8a75e15e3e51d036bc8f5d0923723dced8&token=1634706473_99ebcd2ddbc9af2906feb6c29e64c215c1ce6220' } } }, - { type: 'album', - name: 'Manifestation of Forgotten Souls', - url: 'https://dungeonsdeeprecords.bandcamp.com/album/manifestation-of-forgotten-souls', - imageUrl: 'https://f4.bcbits.com/img/a419294204_2.jpg', - genre: 'ambient', - artist: - { name: 'Hexelwir', - url: 'https://dungeonsdeeprecords.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/26390472_28.jpg' }, - location: null, - featuredTrack: - { name: 'Intro: Funeral Mists Over the Woods', - duration: 329.073, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/c51f707adda6c886a7b01aa7ee84c1cd/mp3-128/1145609889?p=0&ts=1634706473&t=99762d0ddd608a0f7510d5f4ed8d0f7279d6c7dc&token=1634706473_390f4b82c9fbb716da0bdeefe92b2d62fc48479d' } } }, - { type: 'album', - name: 'Spell of the Mycomancer', - url: 'https://hideousgomphidius.bandcamp.com/album/spell-of-the-mycomancer', - imageUrl: 'https://f4.bcbits.com/img/a3352421655_2.jpg', - genre: 'ambient', - artist: - { name: 'Hideous Gomphidius', - url: 'https://hideousgomphidius.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/26252035_28.jpg' }, - location: null, - featuredTrack: - { name: 'Incantation of the Amethyst Deceiver', - duration: 244, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/b7b7d67f8911b638296f59c089c251fa/mp3-128/3633112195?p=0&ts=1634706473&t=1e11e81bddf595a80f9b1dee9c24aadcfe88ad94&token=1634706473_2275ac4a56871d4faa6c946b785e3f68558d9a21' } } }, - { type: 'album', - name: 'Gemstones II [CYD 0035]', - url: 'https://cyclicaldreams.bandcamp.com/album/gemstones-ii-cyd-0035', - imageUrl: 'https://f4.bcbits.com/img/a2030104897_2.jpg', - genre: 'ambient', - artist: - { name: 'Cyclical Dreams', - url: 'https://cyclicaldreams.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/20005733_28.jpg' }, - location: 'Buenos Aires, Argentina', - featuredTrack: - { name: 'Gradient 12', - duration: 418.478, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/2ad1d6ef311e26c10aae46653a44191c/mp3-128/2210355710?p=0&ts=1634706473&t=049958cd52addb6d41f0c1a5778b67e5384be994&token=1634706473_6a7de578464c4115c4638d59ff851d2733025423' } } }, - { type: 'album', - name: 'Kofū / 古風', - url: 'https://meitei.bandcamp.com/album/kofu', - imageUrl: 'https://f4.bcbits.com/img/a3633290297_2.jpg', - genre: 'ambient', - artist: - { name: 'Meitei / 冥丁', - url: 'https://meitei.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/26404121_28.jpg' }, - location: 'Hiroshima, Japan', - featuredTrack: - { name: 'Oiran II / 花魁 II', - duration: 247.478, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/0926f13f64462bbbfe2ba6c01a862504/mp3-128/2669034153?p=0&ts=1634706473&t=48044aab593cda7e15955be28d70f0c981c05b23&token=1634706473_3665107806d6c3a6008daf7f24477be1138fc16f' } } }, - { type: 'album', - name: 'Hexerei Im Zwielicht Der Finsternis', - url: 'https://eternalpride.bandcamp.com/album/hexerei-im-zwielicht-der-finsternis', - imageUrl: 'https://f4.bcbits.com/img/a3076750010_2.jpg', - genre: 'ambient', - artist: - { name: 'Aghast', - url: 'https://eternalpride.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/5125136_28.jpg' }, - location: null, - featuredTrack: - { name: 'Enthral', - duration: 98.8267, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/3b3f6c580a60b446dd8e82a6fcde8a31/mp3-128/2143396005?p=0&ts=1634706473&t=f843df755f18674d4600ea8e31fa4bbd7fb6b12d&token=1634706473_379244ffc161935ec5fbb20381415518050ce87a' } } }, - { type: 'album', - name: 'outer worlds', - url: 'https://blankembrace.bandcamp.com/album/outer-worlds', - imageUrl: 'https://f4.bcbits.com/img/a3251828777_2.jpg', - genre: 'ambient', - artist: - { name: 'Blank Embrace', - url: 'https://blankembrace.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/16756242_28.jpg' }, - location: 'Yessey, Russia', - featuredTrack: - { name: 'no rush', - duration: 320, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/b9979a3090cbedea92acdfcff55910a6/mp3-128/3905371245?p=0&ts=1634706473&t=d5a6d266c6020a94169a9ed1b5c85e1396bcb2e4&token=1634706473_2b0aa3a14404b860fa483cae8ffd1f79d8e5a6f4' } } }, - { type: 'album', - name: 'Demo VI (With Stitched Scars)', - url: 'https://ghoest.bandcamp.com/album/demo-vi-with-stitched-scars', - imageUrl: 'https://f4.bcbits.com/img/a1858827119_2.jpg', - genre: 'ambient', - artist: - { name: 'Ghoëst', - url: 'https://ghoest.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/26326235_28.jpg' }, - location: 'Växjö, Sweden', - featuredTrack: - { name: 'With Stitched Scars', - duration: 621.273, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/d0e7db59793ee8c5e0cbe3626f4112a8/mp3-128/449396578?p=0&ts=1634706473&t=3161e51d58e11b3693f567d17a5403fedb69b6b2&token=1634706473_8a2a3d42131a929c639c74cb6403ea94f26fcf20' } } }, - { type: 'album', - name: 'Back to Beyond', - url: 'https://cryochamber.bandcamp.com/album/back-to-beyond', - imageUrl: 'https://f4.bcbits.com/img/a2678244643_2.jpg', - genre: 'ambient', - artist: - { name: 'Alphaxone & ProtoU', - url: 'https://cryochamber.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/4516843_28.jpg' }, - location: 'Oregon', - featuredTrack: - { name: 'Future Underground', - duration: 555.438, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/69bc54481b5c409804529b60a93408f7/mp3-128/2887238000?p=0&ts=1634706473&t=5ed2514024327782f4d48876f7afeb4f8ae8266c&token=1634706473_367435936eed03fd74bfa787805d78f762cef927' } } }, - { type: 'album', - name: 'Illusive', - url: 'https://kelly-david.bandcamp.com/album/illusive', - imageUrl: 'https://f4.bcbits.com/img/a860831734_2.jpg', - genre: 'ambient', - artist: - { name: 'Kelly David', - url: 'https://kelly-david.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/25783439_28.jpg' }, - location: 'Colorado', - featuredTrack: - { name: 'Top of the Trees', - duration: 483.26, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/775e93d3ff955d54fcdf42ca55904dc6/mp3-128/3853325841?p=0&ts=1634706473&t=09874318500234814199a3b64e290bba32101816&token=1634706473_117dfe27d4b1599a8a0083b882c0250e074a984f' } } }, - { type: 'album', - name: 'Demo V (Under Somniferous Shadows)', - url: 'https://ghoest.bandcamp.com/album/demo-v-under-somniferous-shadows', - imageUrl: 'https://f4.bcbits.com/img/a698818530_2.jpg', - genre: 'ambient', - artist: - { name: 'Ghoëst', - url: 'https://ghoest.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/26326235_28.jpg' }, - location: 'Växjö, Sweden', - featuredTrack: - { name: 'Under Somniferous Shadows', - duration: 740.727, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/442a7ea6e053d7fa83a3dc98c23aedac/mp3-128/1275983329?p=0&ts=1634706473&t=eb89b51f26c63d221847ff5205cde78fc24a0b2f&token=1634706473_fa9c885003cb9393f2f9a0fc62b399e3bcb14f31' } } }, - { type: 'album', - name: 'The Candlelight Tomes', - url: 'https://gelure.bandcamp.com/album/the-candlelight-tomes', - imageUrl: 'https://f4.bcbits.com/img/a3983583451_2.jpg', - genre: 'ambient', - artist: - { name: 'Gelure', - url: 'https://gelure.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/20412751_28.jpg' }, - location: 'Adelaide, Australia', - featuredTrack: - { name: 'Hoarfrost Winter Pines', - duration: 273.566, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/5c4a2a594adeca832e4297fb28a9d697/mp3-128/109192422?p=0&ts=1634706473&t=13872b49ecf618bfe35ca2283119d065c990d4d5&token=1634706473_c811292c2f47d218506f3345c33e8c2ed025ef05' } } }, - { type: 'album', - name: 'Short Stories 5', - url: 'https://cousinsilas1.bandcamp.com/album/short-stories-5', - imageUrl: 'https://f4.bcbits.com/img/a2705917410_2.jpg', - genre: 'ambient', - artist: - { name: 'Cousin Silas', - url: 'https://cousinsilas1.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/20716271_28.jpg' }, - location: 'UK', - featuredTrack: - { name: 'Red Horizon', - duration: 127.829, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/8b42a61939da3c6b5fde2896f916c897/mp3-128/1575099798?p=0&ts=1634706473&t=ab95c01ea0fa3134c3c0774b39add3a673947b62&token=1634706473_249a5ba8b98e6d499a233ccc3d8f6c4fa9f1f6a3' } } }, - { type: 'album', - name: 'A LITTLE FABLE (Remastered LP Edition)', - url: 'https://aspidistraflyx.bandcamp.com/album/a-little-fable-remastered-lp-edition', - imageUrl: 'https://f4.bcbits.com/img/a739310652_2.jpg', - genre: 'ambient', - artist: - { name: 'ASPIDISTRAFLY', - url: 'https://aspidistraflyx.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/20164089_28.jpg' }, - location: 'Singapore', - featuredTrack: - { name: 'Landscape With A Fairy', - duration: 265.941, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/9d15c83171846b1dafc210d321c45a03/mp3-128/3084523971?p=0&ts=1634706473&t=6e73d9403f7e462aaf33b061142b4884313e0e53&token=1634706473_b84d9551b1ce770be3cac934ed0dffc260fd38d5' } } }, - { type: 'album', - name: 'Stochastic', - url: 'https://carbonbasedlifeforms.bandcamp.com/album/stochastic', - imageUrl: 'https://f4.bcbits.com/img/a4045577295_2.jpg', - genre: 'ambient', - artist: - { name: 'Carbon Based Lifeforms', - url: 'https://carbonbasedlifeforms.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/3526550_28.jpg' }, - location: 'Sweden', - featuredTrack: - { name: '6EQUJ5', - duration: 814, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/3bbafd87a2b843b9df07c8a129c62f7f/mp3-128/2154357164?p=0&ts=1634706473&t=a165144eb63bae97324da947ffde4cd7d192f9f9&token=1634706473_98311576756c36e4b3de68627b2981ab3fb76321' } } }, - { type: 'album', - name: 'Withywindle\'s Windings 1: Keyboard Fantasies and other Subharmonic Creations.', - url: 'https://theguelo.bandcamp.com/album/withywindles-windings-1-keyboard-fantasies-and-other-subharmonic-creations', - imageUrl: 'https://f4.bcbits.com/img/a4285815810_2.jpg', - genre: 'ambient', - artist: - { name: 'Guelo', - url: 'https://theguelo.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/18430836_28.jpg' }, - location: null, - featuredTrack: - { name: 'SunMovesSlow', - duration: 218.571, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/195abdf72fb966406a7244de07b03a3e/mp3-128/1198480564?p=0&ts=1634706473&t=0124bc39c56335c9bb49a5d5407bfa803eb33d8d&token=1634706473_8bb9af08eec39d99ded103261b3cc99631edc55a' } } }, - { type: 'album', - name: 'Take Chances In Your Music Or Perish: The Aleatoric Solo Piano Masterpieces Vol​.​3', - url: 'https://wingsofanangel.bandcamp.com/album/take-chances-in-your-music-or-perish-the-aleatoric-solo-piano-masterpieces-vol-3-2', - imageUrl: 'https://f4.bcbits.com/img/a3174076765_2.jpg', - genre: 'ambient', - artist: - { name: 'Wings Of An Angel', - url: 'https://wingsofanangel.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/26349093_28.jpg' }, - location: 'Israel', - featuredTrack: - { name: 'How Do Feelings Develop From The Shadows Of The Day?', - duration: 2230.97, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/8b47fcbf4ec5463e56382caca78f8af1/mp3-128/916976743?p=0&ts=1634706473&t=76fda93f920f542478ad4fe5fffb68d80ee29c72&token=1634706473_7e2923f2e66973854810d8bd97ede218ffb63958' } } }, - { type: 'album', - name: 'Intersectionality', - url: 'https://rkaicmoderne.bandcamp.com/album/intersectionality', - imageUrl: 'https://f4.bcbits.com/img/a2025586526_2.jpg', - genre: 'ambient', - artist: - { name: 'Glenn Sogge', - url: 'https://rkaicmoderne.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/13143980_28.jpg' }, - location: 'Troutdale, Oregon', - featuredTrack: - { name: 'Essences Are Regarded As Surfaces', - duration: 871.259, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/9bf9081369aa23e03372376abbfe4f33/mp3-128/1233556374?p=0&ts=1634706473&t=0ad7c2efacb60d920ccd1d2a05178b3c2e0dd8a0&token=1634706473_c1ed0823a72ba45a2182e98d109b075334edb0e2' } } }, - { type: 'album', - name: 'Endorphin [24bit 96khz]', - url: 'https://cosmicleaf.bandcamp.com/album/endorphin-24bit-96khz', - imageUrl: 'https://f4.bcbits.com/img/a3044117336_2.jpg', - genre: 'ambient', - artist: - { name: 'Germind', - url: 'https://cosmicleaf.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/19220328_28.jpg' }, - location: 'Greece', - featuredTrack: - { name: 'Deserted Coast', - duration: 320.508, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/9d3cd9c1319a0ec53187face3c77dd58/mp3-128/1693230657?p=0&ts=1634706473&t=d939700a704feeb59f112d5e1f909b7c0e0dd59c&token=1634706473_3f9756030b1c27b0e831d146ff4d72bd4650e550' } } }, - { type: 'album', - name: 'Codes of the Biosphere', - url: 'https://exospheremusic.bandcamp.com/album/codes-of-the-biosphere', - imageUrl: 'https://f4.bcbits.com/img/a2152518532_2.jpg', - genre: 'ambient', - artist: - { name: 'Hinterland', - url: 'https://exospheremusic.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/14278285_28.jpg' }, - location: 'Los Angeles, California', - featuredTrack: - { name: 'Vossai Moonfall', - duration: 382, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/95b81f1ae68cccf75c4ddfb55e3332d0/mp3-128/2603903197?p=0&ts=1634706473&t=39099bd24036f78c125b63b782abb305744b5961&token=1634706473_cd3fad13f2af88bffc1bf690fa307e22bee89020' } } }, - { type: 'album', - name: 'On Dark Horses', - url: 'https://emmaruthrundle.bandcamp.com/album/on-dark-horses', - imageUrl: 'https://f4.bcbits.com/img/a4234956256_2.jpg', - genre: 'ambient', - artist: - { name: 'Emma Ruth Rundle', - url: 'https://emmaruthrundle.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/25635538_28.jpg' }, - location: 'Los Angeles, California', - featuredTrack: - { name: 'Fever Dreams', - duration: 289.984, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/b87d7b0054495e163bd2642640fc8c66/mp3-128/2272938294?p=0&ts=1634706473&t=e6267ca9e5e0c3d3d263cb0857e4692466053aef&token=1634706473_fb915a041fbdd6594f6c77d705b6a0d317a0230e' } } }, - { type: 'album', - name: 'Odyssey', - url: 'https://remkoarentz.bandcamp.com/album/odyssey', - imageUrl: 'https://f4.bcbits.com/img/a3999090591_2.jpg', - genre: 'ambient', - artist: - { name: 'Remko Arentz', - url: 'https://remkoarentz.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/10184425_28.jpg' }, - location: 'Amsterdam, Netherlands', - featuredTrack: - { name: 'Odyssey', - duration: 3442.85, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/2d3f88bc383a4986da8b6f83a257e100/mp3-128/126837785?p=0&ts=1634706473&t=f6fd25d64f470e598c07cf92fd008537d3938cae&token=1634706473_8282157c00df4efa6697dae32c7b21c60a4eac99' } } }, - { type: 'album', - name: 'The Origin of Supernatural Astronomical Phenomena 4 (Deluxe Edition)', - url: 'https://scottlawlor.bandcamp.com/album/the-origin-of-supernatural-astronomical-phenomena-4-deluxe-edition', - imageUrl: 'https://f4.bcbits.com/img/a3920768851_2.jpg', - genre: 'ambient', - artist: - { name: 'Scott Lawlor', - url: 'https://scottlawlor.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/15083372_28.jpg' }, - location: 'Corinth, Texas', - featuredTrack: - { name: 'The Origin of Supernatural Astronomical Phenomena 4, Part 1', - duration: 2053.68, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/8e6c0a888ff5188a8a2b76d06286e0e4/mp3-128/4051420359?p=0&ts=1634706473&t=15fc6f0ba8bad2b83b1c1ad89193982fdeab26dc&token=1634706473_8c4252154e82872a1eb9b16b15f90493ef0e4bf0' } } }, - { type: 'album', - name: 'The Old King of Witches', - url: 'https://oldtower.bandcamp.com/album/the-old-king-of-witches', - imageUrl: 'https://f4.bcbits.com/img/a3683270939_2.jpg', - genre: 'ambient', - artist: - { name: 'Old Tower', - url: 'https://oldtower.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/26268337_28.jpg' }, - location: 'Netherlands', - featuredTrack: - { name: 'Night\'s Spell', - duration: 346.37, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/106013536c2c76f4ca7df9bbc8efccbc/mp3-128/3295708039?p=0&ts=1634706473&t=d939dce7876da31ea8af037b5ddc5d69fb7085b1&token=1634706473_d33db6dd2e36f324189a6143ac52bf30e80accd2' } } }, - { type: 'album', - name: 'Vandals and Thugs: Charmed By Gorgoroth', - url: 'https://vandalorum.bandcamp.com/album/vandals-and-thugs-charmed-by-gorgoroth', - imageUrl: 'https://f4.bcbits.com/img/a1999685773_2.jpg', - genre: 'ambient', - artist: - { name: 'Vandalorum', - url: 'https://vandalorum.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/23898243_28.jpg' }, - location: 'San Diego, California', - featuredTrack: - { name: 'Broken Brick Fountain', - duration: 179.556, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/aa392cf513c1c9bd4b03427f70de903c/mp3-128/1079693964?p=0&ts=1634706473&t=fe90c5eb034d069f57539b4111668e5a5e4e2c89&token=1634706473_88c3f7c964d18e5e1bd6603cd8741f471a4f95ea' } } }, - { type: 'album', - name: 'Spore Sorcery', - url: 'https://hideousgomphidius.bandcamp.com/album/spore-sorcery', - imageUrl: 'https://f4.bcbits.com/img/a1522678341_2.jpg', - genre: 'ambient', - artist: - { name: 'Hideous Gomphidius', - url: 'https://hideousgomphidius.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/26252035_28.jpg' }, - location: null, - featuredTrack: - { name: 'The Lair of Hideous Gomphidius', - duration: 384, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/5094b4dc5e2adc8c982a723ca65b0c54/mp3-128/4100240985?p=0&ts=1634706473&t=bbf442d894130b8be19d41bd92bcc4b9bb5f299a&token=1634706473_2c13c27bd410154645cba3bcd5bfa8e3f7592cb1' } } }, - { type: 'album', - name: 'Engine of Hell', - url: 'https://emmaruthrundle.bandcamp.com/album/engine-of-hell', - imageUrl: 'https://f4.bcbits.com/img/a3091452218_2.jpg', - genre: 'ambient', - artist: - { name: 'Emma Ruth Rundle', - url: 'https://emmaruthrundle.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/25635538_28.jpg' }, - location: 'Los Angeles, California', - featuredTrack: - { name: 'Return', - duration: 316.413, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/7718c34b67291b1a1920d6723b5d7d98/mp3-128/794300803?p=0&ts=1634706473&t=90744900bf543c222b7a6e8377c4bba0a3447fe5&token=1634706473_bff31291d603c2de87f39f925150b37e4b5f92dd' } } }, - { type: 'album', - name: 'Intersectionality 2', - url: 'https://rkaicmoderne.bandcamp.com/album/intersectionality-2', - imageUrl: 'https://f4.bcbits.com/img/a3197389425_2.jpg', - genre: 'ambient', - artist: - { name: 'Glenn Sogge', - url: 'https://rkaicmoderne.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/13143980_28.jpg' }, - location: 'Troutdale, Oregon', - featuredTrack: - { name: 'Stillness As A Form Of Action', - duration: 1309.79, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/2da707b090e7c6e3e6844cbe685ffc73/mp3-128/4222733359?p=0&ts=1634706473&t=87b10166efce6653a3c031f81ac0963ae3920fb8&token=1634706473_27e1ff5f8d87e1c7fb4fb666223b02d271c19847' } } }, - { type: 'album', - name: 'Kandu', - url: 'https://reversealignment.bandcamp.com/album/kandu', - imageUrl: 'https://f4.bcbits.com/img/a678274996_2.jpg', - genre: 'ambient', - artist: - { name: 'Taphephobia & IDFT', - url: 'https://reversealignment.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/6123554_28.jpg' }, - location: 'Gothenburg, Sweden', - featuredTrack: - { name: 'Darkroom', - duration: 236, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/ff00ca662b0815daa8b103d325e125e6/mp3-128/2865747530?p=0&ts=1634706473&t=5c999ce4684f3f889f1e03ec004b4a0e55b05e0c&token=1634706473_fb5ddef52827ba0c91a54b0d260a5577c009d670' } } }, - { type: 'album', - name: 'Royalty Free Tropical Rain And Wind Sound Library La Reunion', - url: 'https://freetousesounds.bandcamp.com/album/royalty-free-tropical-rain-and-wind-sound-library-la-reunion', - imageUrl: 'https://f4.bcbits.com/img/a1826168553_2.jpg', - genre: 'ambient', - artist: - { name: 'freetousesounds', - url: 'https://freetousesounds.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/20334833_28.jpg' }, - location: 'Los Angeles, California', - featuredTrack: - { name: 'RAINConc, Concrete, Plastic, Pergola, Dripping, Terrace, Residential Neighbourhood, Light Distance Traffic, St. Denis, La Reunion, FTUS, 19232, 01', - duration: 360.525, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/88f736142b56752e8c325c79c3df6d04/mp3-128/4152116305?p=0&ts=1634706473&t=bb48388a0bfbfa899c06a16780a99c2d8a8242ef&token=1634706473_aea9798c98b0410c4550afb6b195bf7a5824dd6e' } } }, - { type: 'album', - name: 'Skrosdrumir', - url: 'https://castellandungeonsynth.bandcamp.com/album/skrosdrumir', - imageUrl: 'https://f4.bcbits.com/img/a2233409638_2.jpg', - genre: 'ambient', - artist: - { name: 'Castellan', - url: 'https://castellandungeonsynth.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/26419776_28.jpg' }, - location: 'Tromsø, Norway', - featuredTrack: - { name: 'A Storm Of Rust', - duration: 187.642, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/de2c27b83e609e3e48d3fcd3d551a1ac/mp3-128/1382719946?p=0&ts=1634706473&t=b6d6dd7a6fca88bf02ad84f0a0837d8f928f17aa&token=1634706473_cb872afca5f42e26e1c3cd112eaed241c7577a9f' } } }, - { type: 'album', - name: 'Metamorphosis', - url: 'https://multicastdynamics.bandcamp.com/album/metamorphosis', - imageUrl: 'https://f4.bcbits.com/img/a1624277762_2.jpg', - genre: 'ambient', - artist: - { name: 'Multicast Dynamics & Sid Hille', - url: 'https://multicastdynamics.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/24110053_28.jpg' }, - location: 'Helsinki, Finland', - featuredTrack: - { name: 'Part Two', - duration: 1440.59, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/efeb13c57e144644c7c6331e44f9dd6f/mp3-128/2183322355?p=0&ts=1634706473&t=5c37d50aedd06eaa2d8969c113294aeddb615297&token=1634706473_7d9056ec151c78f410b36a85e166665d4f89e3bb' } } }, - { type: 'album', - name: 'Royalty Free German Church Bell Sound Effects', - url: 'https://freetousesounds.bandcamp.com/album/royalty-free-german-church-bell-sound-effects', - imageUrl: 'https://f4.bcbits.com/img/a825602784_2.jpg', - genre: 'ambient', - artist: - { name: 'freetousesounds', - url: 'https://freetousesounds.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/20334833_28.jpg' }, - location: 'Los Angeles, California', - featuredTrack: - { name: 'Church Bell, German Church, Church, Ring, Pedestrian, Marketplace, Sunday, Noon, Close Up, Germany, Biberach, Zoom H6, 9624', - duration: 395, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/5e5f91fc57e511b9f10b1bd7f6ed7c2f/mp3-128/2114017936?p=0&ts=1634706473&t=563b261c185930c8049c64d2a613a569ff10ccfc&token=1634706473_f5cd798b26701c7f67d8bc924b43d8d61ac84a10' } } }, - { type: 'album', - name: 'The Great Divide', - url: 'https://malignantrecs.bandcamp.com/album/the-great-divide', - imageUrl: 'https://f4.bcbits.com/img/a172539436_2.jpg', - genre: 'ambient', - artist: - { name: 'Control', - url: 'https://malignantrecs.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/3009583_28.jpg' }, - location: 'Maryland', - featuredTrack: - { name: 'For the Ashes', - duration: 354.872, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/0b7d60748b52c8e55f05910aefc451ea/mp3-128/1202755517?p=0&ts=1634706473&t=3b913961d985d82cf69692cfb6fe44cc16f18578&token=1634706473_ec68b11df76a94b662807efe8af876464650f910' } } }, - { type: 'album', - name: 'Vers la Montagne Noire', - url: 'https://cimerion.bandcamp.com/album/vers-la-montagne-noire', - imageUrl: 'https://f4.bcbits.com/img/a4154555418_2.jpg', - genre: 'ambient', - artist: - { name: 'Cimerion', - url: 'https://cimerion.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/25726764_28.jpg' }, - location: 'Quebec City, Québec', - featuredTrack: - { name: 'Portail d\'Obsidienne', - duration: 293.445, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/9a70e94397929ce7f74ab1e7788c6702/mp3-128/2715915001?p=0&ts=1634706473&t=7ab0b1213eaceebeed74a6b48de7bfdf7789041d&token=1634706473_8d1559016f1b9036e547700956f91098cf3c548e' } } }, - { type: 'album', - name: 'We, so tired of all the darkness in our lives', - url: 'https://leylandkirby.bandcamp.com/album/we-so-tired-of-all-the-darkness-in-our-lives', - imageUrl: 'https://f4.bcbits.com/img/a3430404850_2.jpg', - genre: 'ambient', - artist: - { name: 'Leyland Kirby', - url: 'https://leylandkirby.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/8262468_28.jpg' }, - location: 'Manchester, UK', - featuredTrack: - { name: 'A1 - Consolation', - duration: 278.335, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/afe0f703add9c365d433d7a0b4692047/mp3-128/1295241041?p=0&ts=1634706473&t=88b2f1e9e4e68c206921de1758d31f9f003a753c&token=1634706473_d087a8c2f20703af86a9570a5bba40c69a47e802' } } }, - { type: 'album', - name: 'Light Caught The Edges', - url: 'https://ftmots.bandcamp.com/album/light-caught-the-edges', - imageUrl: 'https://f4.bcbits.com/img/a2389868679_2.jpg', - genre: 'ambient', - artist: - { name: 'From the Mouth of the Sun', - url: 'https://ftmots.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/10878115_28.jpg' }, - location: null, - featuredTrack: - { name: 'For a Moment We Were Weightless', - duration: 456.453, - streamUrl: - { 'mp3-128': 'https://t4.bcbits.com/stream/ee74b4e41b80c0273cf2696b6fbff4fd/mp3-128/1045766573?p=0&ts=1634706473&t=1c156731c1d066b41a3759a0faee2d5cf58b4eb2&token=1634706473_1ce84105fdb45e9dc2e8df01752aa7f46ed2d7a2' } } } ], - total: 869, - params: - { genre: 'ambient', - sortBy: 'top', - page: 0, - subgenre: 'all-ambient', - time: 0, - location: '0', - format: 'all' } } diff --git a/examples/discovery/discover.ts b/examples/discovery/discover.ts new file mode 100644 index 0000000..06e2c24 --- /dev/null +++ b/examples/discovery/discover.ts @@ -0,0 +1,12 @@ +import bcfetch from '../../'; +import util from 'util'; + +const params = { + genre: 'ambient', + albumImageFormat: 2, + artistImageFormat: 'bio_featured' +}; + +bcfetch.discovery.discover(params).then((result) => { + console.log(util.inspect(result, false, null, false)); +}); diff --git a/examples/discovery/discover_output.txt b/examples/discovery/discover_output.txt new file mode 100644 index 0000000..9d674c2 --- /dev/null +++ b/examples/discovery/discover_output.txt @@ -0,0 +1,926 @@ +{ + items: [ + { + type: 'album', + name: 'Only the Stars Remain', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Jeff Pearce', + url: 'https://jeffpearcemusic.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/32586927_28.jpg' + }, + location: 'Indiana', + url: 'https://jeffpearcemusic.bandcamp.com/album/only-the-stars-remain', + imageUrl: 'https://f4.bcbits.com/img/a3655216211_2.jpg', + featuredTrack: { + name: 'Beyond and Within', + duration: 310.423, + streamUrl: 'https://t4.bcbits.com/stream/a304946688ef6853c4f4fdfcef5a8794/mp3-128/803367117?p=0&ts=1686494610&t=38177950851798872f0e33b4bcb1385fd0f1d820&token=1686494610_99080318cca92f59990f7c39e98c89f0a24d2a02' + } + }, + { + type: 'album', + name: 'Disambiguation', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Garda', + url: 'https://neotantra.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/32233206_28.jpg' + }, + location: 'Italy', + url: 'https://neotantra.bandcamp.com/album/disambiguation', + imageUrl: 'https://f4.bcbits.com/img/a3809147604_2.jpg', + featuredTrack: { + name: 'Childhood Memories', + duration: 240.023, + streamUrl: 'https://t4.bcbits.com/stream/24e7cfd1ce3375b4dc72352c7337e289/mp3-128/2747253521?p=0&ts=1686494610&t=a49422df6809b2cd0532fdc84cfc0b4a3bf9b27d&token=1686494610_53766b145429fdad82ae0e348b6ffcb727de50fe' + } + }, + { + type: 'album', + name: 'The Waves (Remodel)', + genre: 'ambient', + artist: { + type: 'artist', + name: 'SVLBRD', + url: 'https://faintmusic.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/12673995_28.jpg' + }, + location: 'Spain', + url: 'https://faintmusic.bandcamp.com/album/the-waves-remodel', + imageUrl: 'https://f4.bcbits.com/img/a2923180093_2.jpg', + featuredTrack: { + name: 'Anchor (Hilyard Remix)', + duration: 191.897, + streamUrl: 'https://t4.bcbits.com/stream/67c3074d9d2eb56e0b915db684b7116e/mp3-128/3286324040?p=0&ts=1686494610&t=8e89086af4150e065222bce0249e64f50c598789&token=1686494610_8e0a7268b655f9610d35af396927d92b585e7994' + } + }, + { + type: 'album', + name: 'and then there was nothing', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Ian Hawgood', + url: 'https://homenormal.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/32152548_28.jpg' + }, + location: 'London, UK', + url: 'https://homenormal.bandcamp.com/album/and-then-there-was-nothing', + imageUrl: 'https://f4.bcbits.com/img/a2193252233_2.jpg', + featuredTrack: { + name: 'Movement I', + duration: 466.569, + streamUrl: 'https://t4.bcbits.com/stream/fa3854202b854e72f9ae1819b4b1557c/mp3-128/296940566?p=0&ts=1686494610&t=f9c0c1d968bd5279b8d3b733160d6759d40b9d3d&token=1686494610_fc848d792559d97a14d6f5330f475f60d9ea37d9' + } + }, + { + type: 'album', + name: 'Spiral of Grief', + genre: 'ambient', + artist: { + type: 'artist', + name: 'God Body Disconnect', + url: 'https://cryochamber.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/4516843_28.jpg' + }, + location: 'Oregon', + url: 'https://cryochamber.bandcamp.com/album/spiral-of-grief', + imageUrl: 'https://f4.bcbits.com/img/a1784924922_2.jpg', + featuredTrack: { + name: 'The Furthest from God', + duration: 193.5, + streamUrl: 'https://t4.bcbits.com/stream/d067ed086ff635c25c9ef11ad72e6762/mp3-128/986735501?p=0&ts=1686494610&t=060d4d721012660e006fe571d8725744804c421e&token=1686494610_e71274bab9a598c0b45880722d4ca6bb7f49fb57' + } + }, + { + type: 'album', + name: 'The Dormancy', + genre: 'ambient', + artist: { + type: 'artist', + name: 'God Body Disconnect', + url: 'https://cryochamber.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/4516843_28.jpg' + }, + location: 'Oregon', + url: 'https://cryochamber.bandcamp.com/album/the-dormancy', + imageUrl: 'https://f4.bcbits.com/img/a691981029_2.jpg', + featuredTrack: { + name: 'The Dormancy', + duration: 272.439, + streamUrl: 'https://t4.bcbits.com/stream/7a6c57fdf53dcfdc4161ddbc1497703a/mp3-128/2748123688?p=0&ts=1686494610&t=8e90dfe57845b7ee03694182e0ee2025c246511a&token=1686494610_b851b223e6c0c4b1a05a3e849c98e8edfd3d364b' + } + }, + { + type: 'album', + name: 'Nocens', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Thme', + url: 'https://thme-sound.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/26544397_28.jpg' + }, + location: 'Paris, France', + url: 'https://thme-sound.bandcamp.com/album/nocens', + imageUrl: 'https://f4.bcbits.com/img/a4251468025_2.jpg', + featuredTrack: { + name: 'A surge of tenderness', + duration: 302, + streamUrl: 'https://t4.bcbits.com/stream/4087cf9c27db7e20f466cead114394e4/mp3-128/2287974867?p=0&ts=1686494610&t=05f687918dd2e6c09fe8406c196e43845bc45540&token=1686494610_94e2cb292490000e47bfb7600dc6e46528e8475b' + } + }, + { + type: 'album', + name: 'Willoweald', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Loneward', + url: 'https://altusmusic.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/27220053_28.jpg' + }, + location: 'Ottawa, Ontario', + url: 'https://altusmusic.bandcamp.com/album/willoweald', + imageUrl: 'https://f4.bcbits.com/img/a3729396994_2.jpg', + featuredTrack: { + name: 'Grey Fading', + duration: 458, + streamUrl: 'https://t4.bcbits.com/stream/9284b31756d8f6dc430e459e173a4398/mp3-128/239086640?p=0&ts=1686494610&t=620c322895377bf077d90402307d35ad96f07752&token=1686494610_2dd2be9f6fd84775128b8367798583bbe51cc684' + } + }, + { + type: 'album', + name: 'Everywhere at the end of time', + genre: 'ambient', + artist: { + type: 'artist', + name: 'The Caretaker', + url: 'https://thecaretaker.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/8262351_28.jpg' + }, + location: 'Manchester, UK', + url: 'https://thecaretaker.bandcamp.com/album/everywhere-at-the-end-of-time', + imageUrl: 'https://f4.bcbits.com/img/a2383326070_2.jpg', + featuredTrack: { + name: "A1 - It's just a burning memory", + duration: 212.213, + streamUrl: 'https://t4.bcbits.com/stream/5374bf89169c62150232c9813a977c9a/mp3-128/1541188837?p=0&ts=1686494610&t=44da4f583b8cf99aa3d6af83b79bd889553b6045&token=1686494610_f6ffc3bee18aba2d60c61f0d9b8d0ac31eeded46' + } + }, + { + type: 'album', + name: 'Longsword IV', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Longsword', + url: 'https://longsword.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/23991929_28.jpg' + }, + location: 'montana', + url: 'https://longsword.bandcamp.com/album/longsword-iv', + imageUrl: 'https://f4.bcbits.com/img/a2016551705_2.jpg', + featuredTrack: { + name: 'Champions of The Kingdom Pt. I', + duration: 188.15, + streamUrl: 'https://t4.bcbits.com/stream/8c07f7fc8f850a5a9697fa4b2d0bdf18/mp3-128/4009095460?p=0&ts=1686494610&t=ad99b0eb6955cede74cb4a9519fd65886ff920bc&token=1686494610_f6906521c279392140a6ba443eb04a973cd23d32' + } + }, + { + type: 'album', + name: 'Cosmic Echo', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Martin Stürtzer', + url: 'https://synphaera.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/5308514_28.jpg' + }, + location: 'Los Angeles, California', + url: 'https://synphaera.bandcamp.com/album/cosmic-echo', + imageUrl: 'https://f4.bcbits.com/img/a64982474_2.jpg', + featuredTrack: { + name: 'Portal to Eris', + duration: 410, + streamUrl: 'https://t4.bcbits.com/stream/44810d425f0df4cb59084c2c6db90de2/mp3-128/1376804004?p=0&ts=1686494610&t=1ade5c844bf5485ffdf1e00af39a3cc2af4099a2&token=1686494610_d3ac739d847bec1c737671dcedee222b524bc76d' + } + }, + { + type: 'album', + name: 'where i go you cannot follow', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Ian Hawgood', + url: 'https://homenormal.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/32152548_28.jpg' + }, + location: 'London, UK', + url: 'https://homenormal.bandcamp.com/album/where-i-go-you-cannot-follow', + imageUrl: 'https://f4.bcbits.com/img/a2949681641_2.jpg', + featuredTrack: { + name: 'guitar refrain for a midmorning blur', + duration: 933.936, + streamUrl: 'https://t4.bcbits.com/stream/560719cee97ad40619cc0f9a489737c3/mp3-128/3867808585?p=0&ts=1686494610&t=9a6bb683fb0aa7abf5c2219848f323125d246de1&token=1686494610_2cbd95557b50067ca1080bf4e676222dd2b82a5e' + } + }, + { + type: 'album', + name: 'The Map Is Not The Territory', + genre: 'ambient', + artist: { + type: 'artist', + name: 'MYRRHMAN', + url: 'https://myrrhman-cis.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/32552569_28.jpg' + }, + location: 'Scotland, UK', + url: 'https://myrrhman-cis.bandcamp.com/album/the-map-is-not-the-territory', + imageUrl: 'https://f4.bcbits.com/img/a863278589_2.jpg', + featuredTrack: { + name: '[Title Redacted]', + duration: 312.453, + streamUrl: 'https://t4.bcbits.com/stream/fef0f7041064f5d7d91d17a3e419ee2f/mp3-128/695098163?p=0&ts=1686494610&t=dec1fd374c444e00805d1e24986e9c881ba6cf50&token=1686494610_3ffe5447cc7b1d2990d3d1cd400fb0e436455977' + } + }, + { + type: 'album', + name: 'Forest', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Shane Morris', + url: 'https://neotantra.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/32233206_28.jpg' + }, + location: 'Italy', + url: 'https://neotantra.bandcamp.com/album/forest', + imageUrl: 'https://f4.bcbits.com/img/a2750886634_2.jpg', + featuredTrack: { + name: 'Forest', + duration: 2775.13, + streamUrl: 'https://t4.bcbits.com/stream/bf9fbc5f6605c225064ae4c791fcc10c/mp3-128/1004638667?p=0&ts=1686494610&t=5bc3fee67cd5f4879b68ffd36ddd73fbf2dd1c59&token=1686494610_933275dc769460dae92ab1b21f47ab63f94607c0' + } + }, + { + type: 'album', + name: 'Lost Memories', + genre: 'ambient', + artist: { + type: 'artist', + name: 'teeth', + url: 'https://teeth777.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/29136829_28.jpg' + }, + location: 'Pennsylvania', + url: 'https://teeth777.bandcamp.com/album/lost-memories', + imageUrl: 'https://f4.bcbits.com/img/a976960321_2.jpg', + featuredTrack: { + name: 'In my restless dreams', + duration: 286.944, + streamUrl: 'https://t4.bcbits.com/stream/6fff3715d53188a40f2bf62d2230b60c/mp3-128/350561166?p=0&ts=1686494610&t=1a0f6be204ba0ac5ff43612cea5c678f9ffe8168&token=1686494610_443f9a5a725b80f63c61c266c5671765dec23913' + } + }, + { + type: 'album', + name: 'Random Friday (Remastered) (24bit)', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Solar Fields', + url: 'https://solarfields.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/3471719_28.jpg' + }, + location: 'Sweden', + url: 'https://solarfields.bandcamp.com/album/random-friday-remastered-24bit', + imageUrl: 'https://f4.bcbits.com/img/a1514016712_2.jpg', + featuredTrack: { + name: 'Swoosh (Remastered) (24bit)', + duration: 502.741, + streamUrl: 'https://t4.bcbits.com/stream/e9c7156568a97b8850d2c2f668a9396e/mp3-128/4156640513?p=0&ts=1686494610&t=ec401ca25c2017ae295516554de0459c0004d362&token=1686494610_1d362e154dc9bd8ebab0bc7248860b71baebb724' + } + }, + { + type: 'album', + name: 'The full moon series Volume 06 - June The Full Rose Moon', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Scott Lawlor', + url: 'https://scottlawlor.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/27186387_28.jpg' + }, + location: 'Corinth, Texas', + url: 'https://scottlawlor.bandcamp.com/album/the-full-moon-series-volume-06-june-the-full-rose-moon', + imageUrl: 'https://f4.bcbits.com/img/a4136502177_2.jpg', + featuredTrack: { + name: 'The full moon series Volume 06 - June The Full Rose Moon', + duration: 10599.1, + streamUrl: 'https://t4.bcbits.com/stream/5fb1d3f321754f6cca6ae7365c735a10/mp3-128/1530482290?p=0&ts=1686494610&t=6441127137555dc5eee618240311d81786a0043c&token=1686494610_f334d0adc68cae3be86417164ca8250c2d8e7717' + } + }, + { + type: 'album', + name: 'Endless Vacation', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Grotta Veterano & Music For Sleep', + url: 'https://mforsleep.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/31126617_28.jpg' + }, + location: 'Sardinia, Italy', + url: 'https://mforsleep.bandcamp.com/album/endless-vacation', + imageUrl: 'https://f4.bcbits.com/img/a1402257172_2.jpg', + featuredTrack: { + name: 'If only anything could change you', + duration: 433.9, + streamUrl: 'https://t4.bcbits.com/stream/96f8e94d72332f5982c61777ad25b0c1/mp3-128/2822820283?p=0&ts=1686494610&t=4f4f8aaf8906ca9e0926289efa84b6a9fff47bb4&token=1686494610_e1e01133b150dfa3d90ee5b253b0e264dc6c2ed0' + } + }, + { + type: 'album', + name: 'Waldkult', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Winterblood', + url: 'https://winterblood78.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/23052888_28.jpg' + }, + location: 'Florence, Italy', + url: 'https://winterblood78.bandcamp.com/album/waldkult', + imageUrl: 'https://f4.bcbits.com/img/a2492856898_2.jpg', + featuredTrack: { + name: 'I', + duration: 719.884, + streamUrl: 'https://t4.bcbits.com/stream/59a8315ff488cf7007fc548835276ad9/mp3-128/170087785?p=0&ts=1686494610&t=f21bef1055df543e2cdbcd5d7441214484fd9f56&token=1686494610_762cb9ca8660c401eed61ffdaf4e8a93572f0796' + } + }, + { + type: 'album', + name: 'Therapy', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Brendan Eder Ensemble', + url: 'https://brendaneder.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/32143692_28.jpg' + }, + location: 'Los Angeles, California', + url: 'https://brendaneder.bandcamp.com/album/therapy', + imageUrl: 'https://f4.bcbits.com/img/a3654795033_2.jpg', + featuredTrack: { + name: 'Pure (Ride the World)', + duration: 198.188, + streamUrl: 'https://t4.bcbits.com/stream/82ccd86fb9fbbf8709098b58b2a12317/mp3-128/3568761168?p=0&ts=1686494610&t=6b32eabb0117617f9548f2e0ff9d1e3bf7cbd552&token=1686494610_49d3c3e6be8d2558a25755593d27070ea1a4fc11' + } + }, + { + type: 'album', + name: 'Mystic Chords and Sacred Spaces - Complete Edition', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Steve Roach', + url: 'https://steveroach.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/15138466_28.jpg' + }, + location: 'Tucson, Arizona', + url: 'https://steveroach.bandcamp.com/album/mystic-chords-and-sacred-spaces-complete-edition', + imageUrl: 'https://f4.bcbits.com/img/a3017315830_2.jpg', + featuredTrack: { + name: 'Palace of Nectar', + duration: 855, + streamUrl: 'https://t4.bcbits.com/stream/473ddf67c3a9c8da49f9bb85a1c47e01/mp3-128/750688143?p=0&ts=1686494610&t=c604c577fbafce728d27603d0ad52c41de7b078d&token=1686494610_16d0d58358ee72c9a56305d6ee34c91810c7aeb0' + } + }, + { + type: 'album', + name: 'respirate', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Jameson Nathan Jones', + url: 'https://jamesonnathanjones.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/29678900_28.jpg' + }, + location: 'Laurel, Mississippi', + url: 'https://jamesonnathanjones.bandcamp.com/album/respirate', + imageUrl: 'https://f4.bcbits.com/img/a297542043_2.jpg', + featuredTrack: { + name: 'gaps', + duration: 208.125, + streamUrl: 'https://t4.bcbits.com/stream/ffccf694612c0e236613d95a2f41e3a3/mp3-128/873781351?p=0&ts=1686494610&t=91430845c6d58eda57e0b665cdf027c7bcbd6a64&token=1686494610_c5c80c8e92f0d7b657c948dca5ce8ac00d571438' + } + }, + { + type: 'album', + name: 'Tales of The Lost and Decrepit Forest', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Purgatruum', + url: 'https://dispelltd.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/27653507_28.jpg' + }, + location: 'Washington', + url: 'https://dispelltd.bandcamp.com/album/tales-of-the-lost-and-decrepit-forest', + imageUrl: 'https://f4.bcbits.com/img/a159215261_2.jpg', + featuredTrack: { + name: 'Hymn of a Lost Soul', + duration: 217.889, + streamUrl: 'https://t4.bcbits.com/stream/ead1e54000c7e39560a3a9a27d2149ac/mp3-128/590660397?p=0&ts=1686494610&t=34e8c3e13150abf6495cfcbd4cb3df5c30822e54&token=1686494610_e9d9e761ddc835c35c8ba15ed41ff14271c4040c' + } + }, + { + type: 'album', + name: 'TexTrue', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Lorenzo Montanà', + url: 'https://lorenzomontana.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/32356785_28.jpg' + }, + location: 'Italy', + url: 'https://lorenzomontana.bandcamp.com/album/textrue', + imageUrl: 'https://f4.bcbits.com/img/a3504640428_2.jpg', + featuredTrack: { + name: 'Parallel Towers', + duration: 325.679, + streamUrl: 'https://t4.bcbits.com/stream/ecb03ab6851e7af9a2f5f2e8801a8461/mp3-128/730022842?p=0&ts=1686494610&t=3085409191e79e76e1f47cdac5c41763bc78461c&token=1686494610_114b0a8ef74a9ae91cb9924eb96fbc05ebe24312' + } + }, + { + type: 'album', + name: 'The Outer Darkness', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Scott Lawlor', + url: 'https://scottlawlor.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/27186387_28.jpg' + }, + location: 'Corinth, Texas', + url: 'https://scottlawlor.bandcamp.com/album/the-outer-darkness', + imageUrl: 'https://f4.bcbits.com/img/a2287438060_2.jpg', + featuredTrack: { + name: 'Suffering the Vengeance of Eternal Fire', + duration: 842.969, + streamUrl: 'https://t4.bcbits.com/stream/b22f35eaed482f051806fa708b8c58bd/mp3-128/3125066543?p=0&ts=1686494610&t=debdfa03b233aec98aed04f9affa93e7cb476ad2&token=1686494610_5451c3569c6b2abe3841c00dcbcccb82f89d484d' + } + }, + { + type: 'album', + name: "Apothecary's Satchel", + genre: 'ambient', + artist: { + type: 'artist', + name: "Witch's Amulet", + url: 'https://wallachianopulence.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/31388985_28.jpg' + }, + location: null, + url: 'https://wallachianopulence.bandcamp.com/album/apothecarys-satchel', + imageUrl: 'https://f4.bcbits.com/img/a2279646251_2.jpg', + featuredTrack: { + name: 'Glow Dust and Fire Salt', + duration: 281.5, + streamUrl: 'https://t4.bcbits.com/stream/a855cb0f3c764ea4d18ac8be57bfd571/mp3-128/3217002939?p=0&ts=1686494610&t=484941764dca947cd7a45603bc208f28e77fa325&token=1686494610_d87ddadff21fa8e54811cc5c60c78d8ad1a87f1a' + } + }, + { + type: 'album', + name: 'Sorrow & Memories', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Wydraddear', + url: 'https://wydraddear.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/25417717_28.jpg' + }, + location: 'France', + url: 'https://wydraddear.bandcamp.com/album/sorrow-memories', + imageUrl: 'https://f4.bcbits.com/img/a1825572478_2.jpg', + featuredTrack: { + name: 'Privriell (The Nightmare)', + duration: 202.2, + streamUrl: 'https://t4.bcbits.com/stream/bc907e194b209196a77c5ac4613e82ce/mp3-128/3810159358?p=0&ts=1686494610&t=a38a21be23dae96d174c32454753a99224e74d19&token=1686494610_3a42aed79e71330ba3d989e126de57e035592302' + } + }, + { + type: 'album', + name: 'The Leems Boyste', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Petteril', + url: 'https://petteril.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/25620724_28.jpg' + }, + location: 'England, UK', + url: 'https://petteril.bandcamp.com/album/the-leems-boyste', + imageUrl: 'https://f4.bcbits.com/img/a647776586_2.jpg', + featuredTrack: { + name: 'DIRIVVV', + duration: 235, + streamUrl: 'https://t4.bcbits.com/stream/31bcb8235f01c656541fa5be63af2e03/mp3-128/3773525411?p=0&ts=1686494610&t=c336f79604afccf77191c7396baaf7771e9e1099&token=1686494610_1c367c105669124a914a1f6b6a7018ea8575d64d' + } + }, + { + type: 'album', + name: 'Alchemy', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Witan', + url: 'https://thewitan.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/30822306_28.jpg' + }, + location: 'England, UK', + url: 'https://thewitan.bandcamp.com/album/alchemy', + imageUrl: 'https://f4.bcbits.com/img/a435551709_2.jpg', + featuredTrack: { + name: 'Shimmering Stream', + duration: 305.08, + streamUrl: 'https://t4.bcbits.com/stream/8cff072a83b9fa3190dee921d7c7334f/mp3-128/2137586520?p=0&ts=1686494610&t=f796b22ac3a270d5a397cb7c0898834a12a02e3c&token=1686494610_941c6e71894e5eb256e71e6d57595782c490771b' + } + }, + { + type: 'album', + name: 'An empty bliss beyond this World', + genre: 'ambient', + artist: { + type: 'artist', + name: 'The Caretaker', + url: 'https://thecaretaker.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/8262351_28.jpg' + }, + location: 'Manchester, UK', + url: 'https://thecaretaker.bandcamp.com/album/an-empty-bliss-beyond-this-world', + imageUrl: 'https://f4.bcbits.com/img/a4087775263_2.jpg', + featuredTrack: { + name: 'All you are going to want to do is get back there', + duration: 226.236, + streamUrl: 'https://t4.bcbits.com/stream/153845156a25011e0d4e3939103f8f02/mp3-128/1186879202?p=0&ts=1686494610&t=ed14e322b916af33772bbcca6250073a34b0e3c0&token=1686494610_fcb5300cf79254dd55101cadd09ede7d3d11611c' + } + }, + { + type: 'album', + name: 'Spiral of Galaxies (24 bit)', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Astropilot', + url: 'https://astro-sphere.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/31536378_28.jpg' + }, + location: null, + url: 'https://astro-sphere.bandcamp.com/album/spiral-of-galaxies-24-bit', + imageUrl: 'https://f4.bcbits.com/img/a3946410691_2.jpg', + featuredTrack: { + name: 'Spiral of Galaxies', + duration: 3231.24, + streamUrl: 'https://t4.bcbits.com/stream/77e21e698ded4ea77442f6bb93017543/mp3-128/2960587501?p=0&ts=1686494610&t=e7ecc02e04880505942ca562433c8882d09aefe4&token=1686494610_07547aa4fbcdfc01ec6833c182c3087bcade99c9' + } + }, + { + type: 'album', + name: 'Legacy of Silence (24bit)', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Steve Brand', + url: 'https://steve-brand.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/26697882_28.jpg' + }, + location: null, + url: 'https://steve-brand.bandcamp.com/album/legacy-of-silence-24bit', + imageUrl: 'https://f4.bcbits.com/img/a1883270699_2.jpg', + featuredTrack: { + name: 'Legacy Of Silence', + duration: 486, + streamUrl: 'https://t4.bcbits.com/stream/c7d1869f78f0e7d7f2f7df29f1513d50/mp3-128/2147191742?p=0&ts=1686494610&t=fc6a74b9f174f25329c6dc35d97b1e169d9313f1&token=1686494610_73d79fa351ae4f9915a02014384922ac077a41ec' + } + }, + { + type: 'album', + name: 'Verdant', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Rog', + url: 'https://rogofficial.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/18084376_28.jpg' + }, + location: 'Croatia', + url: 'https://rogofficial.bandcamp.com/album/verdant', + imageUrl: 'https://f4.bcbits.com/img/a3501959396_2.jpg', + featuredTrack: { + name: 'Earth magic', + duration: 155.769, + streamUrl: 'https://t4.bcbits.com/stream/90f3d41c4f193c0f613268202489ff7e/mp3-128/4161429065?p=0&ts=1686494610&t=3a7a4a5daed15410daddec070c2dff3a8d0e7b05&token=1686494610_21122f06c813ede3121f4e7794f8e70336ae59be' + } + }, + { + type: 'album', + name: 'and then there was nothing', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Ian Hawgood', + url: 'https://folkreels.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/23221489_28.jpg' + }, + location: 'Brighton, UK', + url: 'https://folkreels.bandcamp.com/album/and-then-there-was-nothing', + imageUrl: 'https://f4.bcbits.com/img/a1022848621_2.jpg', + featuredTrack: { + name: 'Movement I', + duration: 466.569, + streamUrl: 'https://t4.bcbits.com/stream/cba3132d54671139cbcb70253520c16e/mp3-128/2789158750?p=0&ts=1686494610&t=fe3bcb7e97bdaf72af0140e2ffa749c44c7abc2e&token=1686494610_124a3d669df9fd6acda075696623d00986c2e26a' + } + }, + { + type: 'album', + name: 'Kwaidan / 怪談 (5th Anniversary Edition)', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Meitei / 冥丁', + url: 'https://meitei.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/32623951_28.jpg' + }, + location: 'Hiroshima, Japan', + url: 'https://meitei.bandcamp.com/album/kwaidan-5th-anniversary-edition', + imageUrl: 'https://f4.bcbits.com/img/a3442473534_2.jpg', + featuredTrack: { + name: 'Sazanami / 漣', + duration: 61.76, + streamUrl: 'https://t4.bcbits.com/stream/e9a0056047e65dd7a508a5e8e0170b49/mp3-128/681024076?p=0&ts=1686494610&t=d28e4480c13c411736b460bc1b2f314b7269fe22&token=1686494610_8569cbf58b5089c26aa16fbf6fd23015ff5f62ca' + } + }, + { + type: 'album', + name: 'Soulside Eclipse I', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Soulside Eclipse', + url: 'https://soulsideeclipse.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/22089155_28.jpg' + }, + location: 'England, UK', + url: 'https://soulsideeclipse.bandcamp.com/album/soulside-eclipse-i', + imageUrl: 'https://f4.bcbits.com/img/a4288579673_2.jpg', + featuredTrack: { + name: 'The Curse Of The Eclipse', + duration: 249.919, + streamUrl: 'https://t4.bcbits.com/stream/4e7a78c86fab6a9a8295be9b5650da3f/mp3-128/2567491046?p=0&ts=1686494610&t=bd18c87a36ffb675f284e18e182173a7fdf641cc&token=1686494610_be6bb205e7f41c2f9c72e8bbfbeeea026fb86b18' + } + }, + { + type: 'album', + name: 'Slip Casting', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Solo Andata', + url: 'https://soloandata.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/32594075_28.jpg' + }, + location: 'Melbourne, Australia', + url: 'https://soloandata.bandcamp.com/album/slip-casting', + imageUrl: 'https://f4.bcbits.com/img/a3106077643_2.jpg', + featuredTrack: { + name: 'A Meditation', + duration: 266.739, + streamUrl: 'https://t4.bcbits.com/stream/f7e39cd3b8aaffe7dde2fa758283bbf2/mp3-128/4087192303?p=0&ts=1686494610&t=77edd33cd55e3c75ec75e8532bc9b5792a925a5b&token=1686494610_448fb24caaa65d8366ecbc1e70150272a64248ec' + } + }, + { + type: 'album', + name: 'Upon The Lonesome Throne', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Windlance', + url: 'https://ithildintapeproduction.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/30302681_28.jpg' + }, + location: 'Grand Rapids, Michigan', + url: 'https://ithildintapeproduction.bandcamp.com/album/upon-the-lonesome-throne', + imageUrl: 'https://f4.bcbits.com/img/a263832899_2.jpg', + featuredTrack: { + name: 'Upon The Lonesome Throne Full Tape', + duration: 1856.63, + streamUrl: 'https://t4.bcbits.com/stream/af304be85d01975626595ef4719a4a60/mp3-128/358813463?p=0&ts=1686494610&t=8db365c003677f2ec3e211e5ddbd0a739e883914&token=1686494610_30f06d268c679e9651fd6a5420c618a5d8881c1d' + } + }, + { + type: 'album', + name: 'Mountains', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Onfang & Tütsen', + url: 'https://tutsen.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/32599373_28.jpg' + }, + location: 'Memphis, Tennessee', + url: 'https://tutsen.bandcamp.com/album/mountains', + imageUrl: 'https://f4.bcbits.com/img/a689770525_2.jpg', + featuredTrack: { + name: 'Onfang - Birds Flock To Flooded Farmland', + duration: 207.568, + streamUrl: 'https://t4.bcbits.com/stream/ffe8356c30b9823575e975fac3c3259a/mp3-128/129326439?p=0&ts=1686494610&t=943a0da070ced226438f5f833da887553c9f23f3&token=1686494610_864ff746468725185fde5902e7a240bc919a4425' + } + }, + { + type: 'album', + name: 'Winter Rehearsal II', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Ageless Twilight', + url: 'https://dispelltd.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/27653507_28.jpg' + }, + location: 'Washington', + url: 'https://dispelltd.bandcamp.com/album/winter-rehearsal-ii', + imageUrl: 'https://f4.bcbits.com/img/a2749738262_2.jpg', + featuredTrack: { + name: 'Winter Rehearsal II', + duration: 1049.01, + streamUrl: 'https://t4.bcbits.com/stream/4300a17eaf0bea8ecd2086da1561697d/mp3-128/1324885397?p=0&ts=1686494610&t=d57099293215a82ffc26c951d2e44c0946668aa0&token=1686494610_67ff54a3a8982df3ad3317e97763a012452d3b57' + } + }, + { + type: 'album', + name: '(Whirring Marvels In) Consensus Reality', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Eluvium', + url: 'https://eluvium.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/30480831_28.jpg' + }, + location: 'Portland, Oregon', + url: 'https://eluvium.bandcamp.com/album/whirring-marvels-in-consensus-reality', + imageUrl: 'https://f4.bcbits.com/img/a428488522_2.jpg', + featuredTrack: { + name: 'Escapement', + duration: 96.795, + streamUrl: 'https://t4.bcbits.com/stream/7800e7d3cc0a94b958170ffa753375a5/mp3-128/3954546360?p=0&ts=1686494610&t=4297de861e028eb7a5c3870a3bbc11488f8c91f9&token=1686494610_0a2624796dec743123d986cf6290eac1a4d75c04' + } + }, + { + type: 'album', + name: 'Drift', + genre: 'ambient', + artist: { + type: 'artist', + name: 'zakè & Tyresta', + url: 'https://zakedrone.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/25890429_28.jpg' + }, + location: null, + url: 'https://zakedrone.bandcamp.com/album/drift', + imageUrl: 'https://f4.bcbits.com/img/a2312650413_2.jpg', + featuredTrack: { + name: 'Monuments at Sea', + duration: 527.62, + streamUrl: 'https://t4.bcbits.com/stream/f8ef4dbd53dcd9dfce3ca1fe0819a11f/mp3-128/4135932532?p=0&ts=1686494610&t=da96d8366830a79079151d97ac084abbccaac774&token=1686494610_2374819469551df95cb77e70cb8936e9212f2f26' + } + }, + { + type: 'album', + name: 'New Wave / Expander', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Weird Genetics / Anymus', + url: 'https://stroomtv.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/25159466_28.jpg' + }, + location: 'Belgium', + url: 'https://stroomtv.bandcamp.com/album/new-wave-expander', + imageUrl: 'https://f4.bcbits.com/img/a4114777652_2.jpg', + featuredTrack: { + name: 'New Wave', + duration: 392.763, + streamUrl: 'https://t4.bcbits.com/stream/272151d836c869fe2bc8e4b6f0c27281/mp3-128/579624871?p=0&ts=1686494610&t=73b2ce68287b793ed1c1a6cc159a75a8d9dc6e57&token=1686494610_8a69b3235da548be60d924027a8f7f8b774ee52a' + } + }, + { + type: 'album', + name: 'Windbags/Lune', + genre: 'ambient', + artist: { + type: 'artist', + name: 'KMRU', + url: 'https://kmru.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/24810541_28.jpg' + }, + location: 'Nairobi, Kenya', + url: 'https://kmru.bandcamp.com/album/windbags-lune', + imageUrl: 'https://f4.bcbits.com/img/a2106482823_2.jpg', + featuredTrack: { + name: 'Lune', + duration: 352.019, + streamUrl: 'https://t4.bcbits.com/stream/54524abbfdd582ee24877bc0aa4a8ec8/mp3-128/1921678791?p=0&ts=1686494610&t=334f160997fc1b390d36cd520f53f0b78e35c73b&token=1686494610_8dd49c9712671808835ea3fa053ee5ab4ad7a24a' + } + }, + { + type: 'album', + name: 'VOID-002 - 暗号零', + genre: 'ambient', + artist: { + type: 'artist', + name: '暗号零', + url: 'https://c1ph3r.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/17943885_28.jpg' + }, + location: 'Shenzhen, China', + url: 'https://c1ph3r.bandcamp.com/album/void-002', + imageUrl: 'https://f4.bcbits.com/img/a1374804580_2.jpg', + featuredTrack: { + name: '虚空', + duration: 157.375, + streamUrl: 'https://t4.bcbits.com/stream/ff73eb2f0d8d0b328bf1d8aff8f9e23d/mp3-128/751186513?p=0&ts=1686494610&t=b21bd5e250713c49a100e9b5d6e8d7103808dd40&token=1686494610_97c96e1a8bf78c12660469a922788f635be1bbe4' + } + }, + { + type: 'album', + name: 'Requiem For A Dying Animal', + genre: 'ambient', + artist: { + type: 'artist', + name: 'AWARE', + url: 'https://glacialmovements.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/953324_28.jpg' + }, + location: 'Rome, Italy', + url: 'https://glacialmovements.bandcamp.com/album/requiem-for-a-dying-animal', + imageUrl: 'https://f4.bcbits.com/img/a1600402189_2.jpg', + featuredTrack: { + name: 'I', + duration: 754.924, + streamUrl: 'https://t4.bcbits.com/stream/1bb17adea41951bf6e7a1fa1a0642e6c/mp3-128/195542269?p=0&ts=1686494610&t=63ade034f50f3e0dd73c304ce93e1853925c251b&token=1686494610_cb2f0eb83f669d750ed27c72a8c45ced5046884f' + } + }, + { + type: 'album', + name: 'Trust', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Theodore Cale Schafer', + url: 'https://theodoreschafer.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/32038640_28.jpg' + }, + location: 'Queens, New York', + url: 'https://theodoreschafer.bandcamp.com/album/trust', + imageUrl: 'https://f4.bcbits.com/img/a614543226_2.jpg', + featuredTrack: { + name: 'Best Friend', + duration: 317.427, + streamUrl: 'https://t4.bcbits.com/stream/67efe3b8e9aeca143209e132a9998fa9/mp3-128/2568154562?p=0&ts=1686494610&t=46394dd1313cd15ef7adcc78f1ae5bebc20e62e3&token=1686494610_3e6cda66a541df33dfd89a16cf4bae89584687c9' + } + }, + { + type: 'album', + name: 'The Function Of Life', + genre: 'ambient', + artist: { + type: 'artist', + name: 'Mick Chillage', + url: 'https://mickchillage.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/20194684_28.jpg' + }, + location: 'Dublin, Ireland', + url: 'https://mickchillage.bandcamp.com/album/the-function-of-life-2', + imageUrl: 'https://f4.bcbits.com/img/a1423456594_2.jpg', + featuredTrack: { + name: 'Strange Space', + duration: 1704, + streamUrl: 'https://t4.bcbits.com/stream/ec1ad62ecd5a4d5206cd6fd64e218c57/mp3-128/3183111690?p=0&ts=1686494610&t=df8d6dd564a0872843b842c663d2a4c0578a56e8&token=1686494610_13e949b3b22e0a7a065d888738a457544adcf046' + } + } + ], + total: 1400, + params: { + genre: 'ambient', + sortBy: 'top', + page: 0, + subgenre: 'all-ambient', + time: 0, + location: '0', + format: 'all' + } +} diff --git a/examples/discovery/getAvailableOptions.ts b/examples/discovery/getAvailableOptions.ts new file mode 100644 index 0000000..16a066d --- /dev/null +++ b/examples/discovery/getAvailableOptions.ts @@ -0,0 +1,6 @@ +import bcfetch from '../../'; +import util from 'util'; + +bcfetch.discovery.getAvailableOptions().then((results) => { + console.log(util.inspect(results, false, null, false)); +}); diff --git a/examples/discovery/getAvailableOptions_output.txt b/examples/discovery/getAvailableOptions_output.txt new file mode 100644 index 0000000..7c3b9a6 --- /dev/null +++ b/examples/discovery/getAvailableOptions_output.txt @@ -0,0 +1,410 @@ +{ + genres: [ + { name: 'all', value: 'all' }, + { name: 'electronic', value: 'electronic' }, + { name: 'rock', value: 'rock' }, + { name: 'metal', value: 'metal' }, + { name: 'alternative', value: 'alternative' }, + { name: 'hip-hop/rap', value: 'hip-hop-rap' }, + { name: 'experimental', value: 'experimental' }, + { name: 'punk', value: 'punk' }, + { name: 'folk', value: 'folk' }, + { name: 'pop', value: 'pop' }, + { name: 'ambient', value: 'ambient' }, + { name: 'soundtrack', value: 'soundtrack' }, + { name: 'world', value: 'world' }, + { name: 'jazz', value: 'jazz' }, + { name: 'acoustic', value: 'acoustic' }, + { name: 'funk', value: 'funk' }, + { name: 'r&b/soul', value: 'r-b-soul' }, + { name: 'devotional', value: 'devotional' }, + { name: 'classical', value: 'classical' }, + { name: 'reggae', value: 'reggae' }, + { name: 'podcasts', value: 'podcasts' }, + { name: 'country', value: 'country' }, + { name: 'spoken word', value: 'spoken-word' }, + { name: 'comedy', value: 'comedy' }, + { name: 'blues', value: 'blues' }, + { name: 'audiobooks', value: 'audiobooks' }, + { name: 'latin', value: 'latin' } + ], + subgenres: { + acoustic: [ + { name: 'all acoustic', value: 'all-acoustic' }, + { name: 'folk', value: 'folk' }, + { name: 'singer-songwriter', value: 'singer-songwriter' }, + { name: 'rock', value: 'rock' }, + { name: 'pop', value: 'pop' }, + { name: 'guitar', value: 'guitar' }, + { name: 'americana', value: 'americana' }, + { name: 'electro-acoustic', value: 'electro-acoustic' }, + { name: 'instrumental', value: 'instrumental' }, + { name: 'piano', value: 'piano' }, + { name: 'bluegrass', value: 'bluegrass' }, + { name: 'roots', value: 'roots' } + ], + alternative: [ + { name: 'all alternative', value: 'all-alternative' }, + { name: 'indie rock', value: 'indie-rock' }, + { name: 'industrial', value: 'industrial' }, + { name: 'shoegaze', value: 'shoegaze' }, + { name: 'grunge', value: 'grunge' }, + { name: 'goth', value: 'goth' }, + { name: 'dream pop', value: 'dream-pop' }, + { name: 'emo', value: 'emo' }, + { name: 'math rock', value: 'math-rock' }, + { name: 'britpop', value: 'britpop' }, + { name: 'jangle pop', value: 'jangle-pop' } + ], + ambient: [ + { name: 'all ambient', value: 'all-ambient' }, + { name: 'chill-out', value: 'chill-out' }, + { name: 'drone', value: 'drone' }, + { name: 'dark ambient', value: 'dark-ambient' }, + { name: 'electronic', value: 'electronic' }, + { name: 'soundscapes', value: 'soundscapes' }, + { name: 'field recordings', value: 'field-recordings' }, + { name: 'atmospheric', value: 'atmospheric' }, + { name: 'meditation', value: 'meditation' }, + { name: 'noise', value: 'noise' }, + { name: 'new age', value: 'new-age' }, + { name: 'idm', value: 'idm' }, + { name: 'industrial', value: 'industrial' } + ], + blues: [ + { name: 'all blues', value: 'all-blues' }, + { name: 'rhythm & blues', value: 'rhythm-blues' }, + { name: 'blues rock', value: 'blues-rock' }, + { name: 'country blues', value: 'country-blues' }, + { name: 'boogie-woogie', value: 'boogie-woogie' }, + { name: 'delta blues', value: 'delta-blues' }, + { name: 'americana', value: 'americana' }, + { name: 'electric blues', value: 'electric-blues' }, + { name: 'gospel', value: 'gospel' }, + { name: 'bluegrass', value: 'bluegrass' } + ], + classical: [ + { name: 'all classical', value: 'all-classical' }, + { name: 'orchestral', value: 'orchestral' }, + { name: 'neo-classical', value: 'neo-classical' }, + { name: 'chamber music', value: 'chamber-music' }, + { name: 'classical piano', value: 'classical-piano' }, + { + name: 'contemporary classical', + value: 'contemporary-classical' + }, + { name: 'baroque', value: 'baroque' }, + { name: 'opera', value: 'opera' }, + { name: 'choral', value: 'choral' }, + { name: 'modern classical', value: 'modern-classical' }, + { name: 'avant garde', value: 'avant-garde' } + ], + comedy: [ + { name: 'all comedy', value: 'all-comedy' }, + { name: 'improv', value: 'improv' }, + { name: 'stand-up', value: 'stand-up' } + ], + country: [ + { name: 'all country', value: 'all-country' }, + { name: 'bluegrass', value: 'bluegrass' }, + { name: 'country rock', value: 'country-rock' }, + { name: 'americana', value: 'americana' }, + { name: 'country folk', value: 'country-folk' }, + { name: 'alt-country', value: 'alt-country' }, + { name: 'country blues', value: 'country-blues' }, + { name: 'western', value: 'western' }, + { name: 'singer-songwriter', value: 'singer-songwriter' }, + { name: 'outlaw', value: 'outlaw' }, + { name: 'honky-tonk', value: 'honky-tonk' }, + { name: 'roots', value: 'roots' }, + { name: 'hillbilly', value: 'hillbilly' } + ], + devotional: [ + { name: 'all devotional', value: 'all-devotional' }, + { name: 'christian', value: 'christian' }, + { name: 'gospel', value: 'gospel' }, + { name: 'meditation', value: 'meditation' }, + { name: 'spiritual', value: 'spiritual' }, + { name: 'worship', value: 'worship' }, + { name: 'inspirational', value: 'inspirational' } + ], + electronic: [ + { name: 'all electronic', value: 'all-electronic' }, + { name: 'house', value: 'house' }, + { name: 'electronica', value: 'electronica' }, + { name: 'downtempo', value: 'downtempo' }, + { name: 'techno', value: 'techno' }, + { name: 'electro', value: 'electro' }, + { name: 'dubstep', value: 'dubstep' }, + { name: 'beats', value: 'beats' }, + { name: 'dance', value: 'dance' }, + { name: 'idm', value: 'idm' }, + { name: 'drum & bass', value: 'drum-bass' }, + { name: 'breaks', value: 'breaks' }, + { name: 'trance', value: 'trance' }, + { name: 'glitch', value: 'glitch' }, + { name: 'chiptune', value: 'chiptune' }, + { name: 'chillwave', value: 'chillwave' }, + { name: 'dub', value: 'dub' }, + { name: 'edm', value: 'edm' }, + { name: 'instrumental', value: 'instrumental' }, + { name: 'witch house', value: 'witch-house' }, + { name: 'garage', value: 'garage' }, + { name: 'juke', value: 'juke' }, + { name: 'footwork', value: 'footwork' }, + { name: 'vaporwave', value: 'vaporwave' }, + { name: 'synthwave', value: 'synthwave' } + ], + experimental: [ + { name: 'all experimental', value: 'all-experimental' }, + { name: 'noise', value: 'noise' }, + { name: 'drone', value: 'drone' }, + { name: 'avant garde', value: 'avant-garde' }, + { name: 'experimental rock', value: 'experimental-rock' }, + { name: 'improvisation', value: 'improvisation' }, + { name: 'sound art', value: 'sound-art' }, + { name: 'musique concrete', value: 'musique-concrete' } + ], + folk: [ + { name: 'all folk', value: 'all-folk' }, + { name: 'singer-songwriter', value: 'singer-songwriter' }, + { name: 'folk rock', value: 'folk-rock' }, + { name: 'indie folk', value: 'indie-folk' }, + { name: 'pop folk', value: 'pop-folk' }, + { name: 'traditional', value: 'traditional' }, + { name: 'experimental folk', value: 'experimental-folk' }, + { name: 'roots', value: 'roots' } + ], + funk: [ + { name: 'all funk', value: 'all-funk' }, + { name: 'funk jam', value: 'funk-jam' }, + { name: 'deep funk', value: 'deep-funk' }, + { name: 'funk rock', value: 'funk-rock' }, + { name: 'jazz funk', value: 'jazz-funk' }, + { name: 'boogie', value: 'boogie' }, + { name: 'g-funk', value: 'g-funk' }, + { name: 'rare groove', value: 'rare-groove' }, + { name: 'electro', value: 'electro' }, + { name: 'go-go', value: 'go-go' } + ], + 'hip-hop-rap': [ + { name: 'all hip-hop/rap', value: 'all-hip-hop-rap' }, + { name: 'rap', value: 'rap' }, + { name: 'underground hip-hop', value: 'underground-hip-hop' }, + { name: 'instrumental hip-hop', value: 'instrumental-hip-hop' }, + { name: 'trap', value: 'trap' }, + { name: 'conscious hip-hop', value: 'conscious-hip-hop' }, + { name: 'boom-bap', value: 'boom-bap' }, + { name: 'beat-tape', value: 'beat-tape' }, + { name: 'hardcore', value: 'hardcore' }, + { name: 'grime', value: 'grime' } + ], + jazz: [ + { name: 'all jazz', value: 'all-jazz' }, + { name: 'fusion', value: 'fusion' }, + { name: 'big band', value: 'big-band' }, + { name: 'nu jazz', value: 'nu-jazz' }, + { name: 'modern jazz', value: 'modern-jazz' }, + { name: 'swing', value: 'swing' }, + { name: 'free jazz', value: 'free-jazz' }, + { name: 'soul jazz', value: 'soul-jazz' }, + { name: 'latin jazz', value: 'latin-jazz' }, + { name: 'vocal jazz', value: 'vocal-jazz' }, + { name: 'bebop', value: 'bebop' }, + { name: 'spiritual jazz', value: 'spiritual-jazz' } + ], + kids: [ + { name: 'all kids', value: 'all-kids' }, + { name: 'family music', value: 'family-music' }, + { name: 'educational', value: 'educational' }, + { name: 'music therapy', value: 'music-therapy' }, + { name: 'lullaby', value: 'lullaby' }, + { name: 'baby', value: 'baby' } + ], + latin: [ + { name: 'all latin', value: 'all-latin' }, + { name: 'brazilian', value: 'brazilian' }, + { name: 'cumbia', value: 'cumbia' }, + { name: 'tango', value: 'tango' }, + { name: 'latin rock', value: 'latin-rock' }, + { name: 'flamenco', value: 'flamenco' }, + { name: 'salsa', value: 'salsa' }, + { name: 'reggaeton', value: 'reggaeton' }, + { name: 'merengue', value: 'merengue' }, + { name: 'bolero', value: 'bolero' }, + { name: 'méxico d.f.', value: 'méxico-d.f.' }, + { name: 'bachata', value: 'bachata' } + ], + metal: [ + { name: 'all metal', value: 'all-metal' }, + { name: 'hardcore', value: 'hardcore' }, + { name: 'black metal', value: 'black-metal' }, + { name: 'death metal', value: 'death-metal' }, + { name: 'thrash metal', value: 'thrash-metal' }, + { name: 'grindcore', value: 'grindcore' }, + { name: 'doom', value: 'doom' }, + { name: 'post hardcore', value: 'post-hardcore' }, + { name: 'progressive metal', value: 'progressive-metal' }, + { name: 'metalcore', value: 'metalcore' }, + { name: 'sludge metal', value: 'sludge-metal' }, + { name: 'heavy metal', value: 'heavy-metal' }, + { name: 'deathcore', value: 'deathcore' }, + { name: 'noise', value: 'noise' } + ], + pop: [ + { name: 'all pop', value: 'all-pop' }, + { name: 'indie pop', value: 'indie-pop' }, + { name: 'synth pop', value: 'synth-pop' }, + { name: 'power pop', value: 'power-pop' }, + { name: 'new wave', value: 'new-wave' }, + { name: 'dream pop', value: 'dream-pop' }, + { name: 'noise pop', value: 'noise-pop' }, + { name: 'experimental pop', value: 'experimental-pop' }, + { name: 'electro pop', value: 'electro-pop' }, + { name: 'adult contemporary', value: 'adult-contemporary' }, + { name: 'jangle pop', value: 'jangle-pop' }, + { name: 'j-pop', value: 'j-pop' } + ], + punk: [ + { name: 'all punk', value: 'all-punk' }, + { name: 'hardcore punk', value: 'hardcore-punk' }, + { name: 'garage', value: 'garage' }, + { name: 'pop punk', value: 'pop-punk' }, + { name: 'punk rock', value: 'punk-rock' }, + { name: 'post-punk', value: 'post-punk' }, + { name: 'post-hardcore', value: 'post-hardcore' }, + { name: 'thrash', value: 'thrash' }, + { name: 'crust punk', value: 'crust-punk' }, + { name: 'folk punk', value: 'folk-punk' }, + { name: 'emo', value: 'emo' }, + { name: 'ska', value: 'ska' }, + { name: 'no wave', value: 'no-wave' } + ], + 'r-b-soul': [ + { name: 'all r&b/soul', value: 'all-r-b-soul' }, + { name: 'soul', value: 'soul' }, + { name: 'r&b', value: 'r-b' }, + { name: 'neo-soul', value: 'neo-soul' }, + { name: 'gospel', value: 'gospel' }, + { name: 'contemporary r&b', value: 'contemporary-r-b' }, + { name: 'motown', value: 'motown' }, + { name: 'urban', value: 'urban' } + ], + reggae: [ + { name: 'all reggae', value: 'all-reggae' }, + { name: 'dub', value: 'dub' }, + { name: 'ska', value: 'ska' }, + { name: 'roots', value: 'roots' }, + { name: 'dancehall', value: 'dancehall' }, + { name: 'rocksteady', value: 'rocksteady' }, + { name: 'ragga', value: 'ragga' }, + { name: 'lovers rock', value: 'lovers-rock' } + ], + rock: [ + { name: 'all rock', value: 'all-rock' }, + { name: 'indie', value: 'indie' }, + { name: 'prog rock', value: 'prog-rock' }, + { name: 'post-rock', value: 'post-rock' }, + { name: 'rock & roll', value: 'rock-roll' }, + { name: 'psychedelic rock', value: 'psychedelic-rock' }, + { name: 'hard rock', value: 'hard-rock' }, + { name: 'garage rock', value: 'garage-rock' }, + { name: 'surf rock', value: 'surf-rock' }, + { name: 'instrumental', value: 'instrumental' }, + { name: 'math rock', value: 'math-rock' }, + { name: 'rockabilly', value: 'rockabilly' } + ], + soundtrack: [ + { name: 'all soundtrack', value: 'all-soundtrack' }, + { name: 'film music', value: 'film-music' }, + { name: 'video game music', value: 'video-game-music' } + ], + 'spoken-word': [ + { name: 'all spoken word', value: 'all-spoken-word' }, + { name: 'poetry', value: 'poetry' }, + { name: 'inspirational', value: 'inspirational' }, + { name: 'storytelling', value: 'storytelling' }, + { name: 'self-help', value: 'self-help' } + ], + world: [ + { name: 'all world', value: 'all-world' }, + { name: 'latin', value: 'latin' }, + { name: 'roots', value: 'roots' }, + { name: 'african', value: 'african' }, + { name: 'tropical', value: 'tropical' }, + { name: 'tribal', value: 'tribal' }, + { name: 'brazilian', value: 'brazilian' }, + { name: 'celtic', value: 'celtic' }, + { name: 'world fusion', value: 'world-fusion' }, + { name: 'cumbia', value: 'cumbia' }, + { name: 'gypsy', value: 'gypsy' }, + { name: 'new age', value: 'new-age' }, + { name: 'balkan', value: 'balkan' }, + { name: 'reggaeton', value: 'reggaeton' } + ] + }, + sortBys: [ + { name: 'best-selling', value: 'top' }, + { name: 'new arrivals', value: 'new' }, + { name: 'artist-recommended', value: 'rec' } + ], + artistRecommendationTypes: [ + { name: 'most', value: 'most' }, + { name: 'latest', value: 'latest' } + ], + locations: [ + { name: 'artists from anywhere', value: '0' }, + { name: 'amsterdam', value: '2759794' }, + { name: 'atlanta', value: '4180439' }, + { name: 'austin', value: '4671654' }, + { name: 'baltimore', value: '4347778' }, + { name: 'berlin', value: '2950159' }, + { name: 'boston', value: '4930956' }, + { name: 'brooklyn', value: '5110302' }, + { name: 'buenos aires', value: '3435907' }, + { name: 'chicago', value: '4887398' }, + { name: 'denver', value: '5419384' }, + { name: 'detroit', value: '4990729' }, + { name: 'dublin', value: '2964574' }, + { name: 'glasgow', value: '3333231' }, + { name: 'london', value: '2643743' }, + { name: 'los angeles', value: '5368361' }, + { name: 'madrid', value: '3117735' }, + { name: 'manchester', value: '2643123' }, + { name: 'melbourne', value: '2158177' }, + { name: 'mexico city', value: '3530597' }, + { name: 'miami', value: '4164138' }, + { name: 'minneapolis', value: '5037649' }, + { name: 'montreal', value: '6077243' }, + { name: 'nashville', value: '4644585' }, + { name: 'new orleans', value: '4335045' }, + { name: 'new york city', value: '5128581' }, + { name: 'oakland', value: '5378538' }, + { name: 'paris', value: '2988507' }, + { name: 'philadelphia', value: '4560349' }, + { name: 'portland', value: '5746545' }, + { name: 'san francisco', value: '5391959' }, + { name: 'seattle', value: '5809844' }, + { name: 'sydney', value: '2147714' }, + { name: 'toronto', value: '6167865' }, + { name: 'vancouver', value: '6173331' }, + { name: 'washington, dc', value: '4140963' } + ], + formats: [ + { name: 'any format', value: 'all' }, + { name: 'digital', value: 'digital' }, + { name: 'vinyl', value: 'vinyl' }, + { name: 'compact disc', value: 'cd' }, + { name: 'cassette', value: 'cassette' } + ], + times: [ + { name: 'today', value: -1, title: 'last 24 hours' }, + { name: 'this week', value: 0, title: 'week of Jun 04' }, + { name: 'last week', value: 772, title: 'week of May 28' }, + { name: '2 weeks ago', value: 771, title: 'week of May 21' }, + { name: '3 weeks ago', value: 770, title: 'week of May 14' }, + { name: '4 weeks ago', value: 769, title: 'week of May 07' }, + { name: '5 weeks ago', value: 768, title: 'week of Apr 30' }, + { name: '6 weeks ago', value: 767, title: 'week of Apr 23' } + ] +} diff --git a/examples/discovery/sanitizeDiscoverParams.ts b/examples/discovery/sanitizeDiscoverParams.ts new file mode 100644 index 0000000..fe909cd --- /dev/null +++ b/examples/discovery/sanitizeDiscoverParams.ts @@ -0,0 +1,9 @@ +import bcfetch from '../../'; + +const params = { + genre: 'ambient' +}; + +bcfetch.discovery.sanitizeDiscoverParams(params).then((results) => { + console.log(results); +}); diff --git a/examples/sanitizeDiscoverParams_output.txt b/examples/discovery/sanitizeDiscoverParams_output.txt similarity index 67% rename from examples/sanitizeDiscoverParams_output.txt rename to examples/discovery/sanitizeDiscoverParams_output.txt index 9b7821a..41c6b97 100644 --- a/examples/sanitizeDiscoverParams_output.txt +++ b/examples/discovery/sanitizeDiscoverParams_output.txt @@ -1,7 +1,9 @@ -{ genre: 'ambient', +{ + genre: 'ambient', sortBy: 'top', page: 0, subgenre: 'all-ambient', time: 0, location: '0', - format: 'all' } + format: 'all' +} diff --git a/examples/fan/getCollection.ts b/examples/fan/getCollection.ts new file mode 100644 index 0000000..1db8bc5 --- /dev/null +++ b/examples/fan/getCollection.ts @@ -0,0 +1,23 @@ +import bcfetch from '../../'; +import util from 'util'; + +const username = 'dugout'; + +const params = { + target: username, + imageFormat: 'art_app_large' +}; + +bcfetch.fan.getCollection(params).then(async (results) => { + console.log(util.inspect(results, false, null, false)); + + if (results.continuation) { + console.log('Fetching more by continuation...'); + + const moreResults = await bcfetch.fan.getCollection({ + ...params, + target: results.continuation + }); + console.log(util.inspect(moreResults, false, null, false)); + } +}); diff --git a/examples/getFanCollection_output.txt b/examples/fan/getCollection_output.txt similarity index 55% rename from examples/getFanCollection_output.txt rename to examples/fan/getCollection_output.txt index bcfa8f1..fa163a4 100644 --- a/examples/getFanCollection_output.txt +++ b/examples/fan/getCollection_output.txt @@ -1,20 +1,97 @@ { items: [ + { + type: 'album', + name: 'The TOMS (2023 Reissue)', + url: 'https://thetomspowerpop.bandcamp.com/album/the-toms-2023-reissue', + imageUrl: 'https://f4.bcbits.com/img/a2158170419_16.jpg', + artist: { name: 'The TOMS', url: 'https://thetomspowerpop.bandcamp.com' }, + featuredTrack: { + position: 1, + name: "Let's Be Friends Again", + artist: 'The TOMS', + duration: 184.6, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=533589248&ts=1686408280&t=852051c0e628ca9137d27b9d1ad7e0c104f67be5' + } + }, + { + type: 'track', + name: 'The Other Side Of Christmas', + url: 'https://theboyleastlikelyto.bandcamp.com/track/the-other-side-of-christmas', + imageUrl: 'https://f4.bcbits.com/img/a3164529493_16.jpg', + artist: { + name: 'the boy least likely to', + url: 'https://theboyleastlikelyto.bandcamp.com' + }, + duration: 182.087, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3770463584&ts=1686408280&t=63c6d8119fca2fc1785d11c09db3c5751b7aa34b' + }, { type: 'album', name: 'Anything But Country', url: 'https://legendsofcountry.bandcamp.com/album/anything-but-country', imageUrl: 'https://f4.bcbits.com/img/a1262555298_16.jpg', + artist: { + name: 'Legends Of Country', + url: 'https://legendsofcountry.bandcamp.com' + }, 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=1656102395&t=56d2e33866a8402857f0fac00479fba85f889bd4' - }, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=2932389502&ts=1686408280&t=8c88180e33b142e01bb1d0a13541a2561fb8a08e' + } + }, + { + type: 'album', + name: 'Anything But Country', + url: 'https://legendsofcountry.bandcamp.com/album/anything-but-country', + imageUrl: 'https://f4.bcbits.com/img/a1262555298_16.jpg', artist: { name: 'Legends Of Country', url: 'https://legendsofcountry.bandcamp.com' + }, + 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=1686408280&t=8c88180e33b142e01bb1d0a13541a2561fb8a08e' + } + }, + { + type: 'album', + name: 'Get Into The Summer', + url: 'https://theboyleastlikelyto.bandcamp.com/album/get-into-the-summer', + imageUrl: 'https://f4.bcbits.com/img/a561441958_16.jpg', + artist: { + name: 'the boy least likely to', + url: 'https://theboyleastlikelyto.bandcamp.com' + }, + featuredTrack: { + position: 1, + name: 'Get Into The Summer', + artist: 'the boy least likely to', + duration: 205.822, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=2482647781&ts=1686408280&t=210cf01e4e11d6e73eedc5799b56b287125815f5' + } + }, + { + type: 'album', + name: 'Anything But Country', + url: 'https://legendsofcountry.bandcamp.com/album/anything-but-country', + imageUrl: 'https://f4.bcbits.com/img/a1262555298_16.jpg', + artist: { + name: 'Legends Of Country', + url: 'https://legendsofcountry.bandcamp.com' + }, + 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=1686408280&t=8c88180e33b142e01bb1d0a13541a2561fb8a08e' } }, { @@ -22,98 +99,66 @@ name: 'Honey, Honey', url: 'https://acidhousekings.bandcamp.com/track/honey-honey', imageUrl: 'https://f4.bcbits.com/img/a3415987109_16.jpg', - featuredTrack: { - position: null, - name: 'Honey, Honey', - artist: 'Acid House Kings', - duration: 164.278, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3351443466&ts=1656102395&t=b6656f55370bf41089c141e5641aa83afd77b9e7' - }, artist: { name: 'Acid House Kings', url: 'https://acidhousekings.bandcamp.com' - } + }, + duration: 164.278, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3351443466&ts=1686408280&t=8e442ddaa28de4fd81a2a125869316c271942c05' }, { type: 'track', name: "If That's What It Takes", url: 'https://legendsofcountry.bandcamp.com/track/if-thats-what-it-takes', imageUrl: 'https://f4.bcbits.com/img/a2690853067_16.jpg', - featuredTrack: { - position: null, - name: "If That's What It Takes", - artist: 'Legends Of Country', - duration: 181.5, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3804298008&ts=1656102395&t=9133aa975958d51c5a480cd39a0569de4b63c47d' - }, artist: { name: 'Legends Of Country', url: 'https://legendsofcountry.bandcamp.com' - } + }, + duration: 181.5, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3804298008&ts=1686408280&t=0e845b992092983492f6a2260f81dbf66c198ad3' }, { type: 'album', name: 'SiX TAPE', url: 'https://bleu.bandcamp.com/album/six-tape', imageUrl: 'https://f4.bcbits.com/img/a8347264_16.jpg', + artist: { name: 'Bleu', url: 'https://bleu.bandcamp.com' }, featuredTrack: { position: 2, name: 'A Crazy Life! (Explicit)', artist: 'Bleu', duration: 193.24, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3999943811&ts=1656102395&t=d3a25e5da3c7b802b5fc27461a33f011c851b733' - }, - artist: { name: 'Bleu', url: 'https://bleu.bandcamp.com' } + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3999943811&ts=1686408280&t=5b1a9551f89b340330393320e13aa3e427ccae06' + } }, { type: 'track', name: 'The Late Great Cassiopia', url: 'https://theessexgreen.bandcamp.com/track/the-late-great-cassiopia-2', imageUrl: 'https://f4.bcbits.com/img/a3732518472_16.jpg', - featuredTrack: { - position: 2, - name: 'The Late Great Cassiopia', - artist: 'The Essex Green', - duration: 211.36, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3916949617&ts=1656102395&t=af19d13c4b4a688812597487d407704e88e7c0c3' - }, artist: { name: 'The Essex Green', url: 'https://theessexgreen.bandcamp.com' - } + }, + duration: 211.36, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3916949617&ts=1686408280&t=361448cb302f8948eb3f979a767cb86cb92a6377' }, { type: 'album', name: 'Lucky Day', url: 'https://powerpopacademy.bandcamp.com/album/lucky-day', imageUrl: 'https://f4.bcbits.com/img/a1906864882_16.jpg', + artist: { + name: 'David Myhr', + url: 'https://powerpopacademy.bandcamp.com' + }, featuredTrack: { position: 4, name: 'The Perfect Place', artist: 'David Myhr', duration: 236.107, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1044351484&ts=1656102395&t=267284c39370615295a1629298b2f667a26c043b' - }, - artist: { - name: 'David Myhr', - url: 'https://powerpopacademy.bandcamp.com' - } - }, - { - type: 'track', - name: 'Get Into The Summer', - url: 'https://theboyleastlikelyto.bandcamp.com/track/get-into-the-summer', - imageUrl: 'https://f4.bcbits.com/img/a1263524940_16.jpg', - featuredTrack: { - position: null, - name: 'Get Into The Summer', - artist: 'the boy least likely to', - duration: 205.822, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=483282266&ts=1656102395&t=b891aeca04dd2610c2d8e4c177005385b92280f1' - }, - artist: { - name: 'the boy least likely to', - url: 'https://theboyleastlikelyto.bandcamp.com' + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1044351484&ts=1686408280&t=a5ac8eeb491b66dfba3d832a2973c071c7248192' } }, { @@ -121,33 +166,28 @@ name: 'Do Unto Others', url: 'https://curation-records.bandcamp.com/track/do-unto-others', imageUrl: 'https://f4.bcbits.com/img/a1527699362_16.jpg', - featuredTrack: { - position: 5, - name: 'Do Unto Others', - artist: 'GospelbeacH', - duration: 194.8, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=2401340477&ts=1656102395&t=818451abd7ad0b95248a20955c8e9dd2b45b4c10' - }, artist: { name: 'GospelbeacH', url: 'https://curation-records.bandcamp.com' - } + }, + duration: 194.8, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=2401340477&ts=1686408280&t=f73c4a6d6684f5be987f3b25c4e719afc10ed333' }, { type: 'album', name: "Let's Have Fun (In The Summer Sun)", url: 'https://tommyandtherockets.bandcamp.com/album/lets-have-fun-in-the-summer-sun', imageUrl: 'https://f4.bcbits.com/img/a798199646_16.jpg', + artist: { + name: 'Tommy And The Rockets', + url: 'https://tommyandtherockets.bandcamp.com' + }, featuredTrack: { position: 1, name: "Let's Have Fun (In The Summer Sun)", artist: 'Tommy And The Rockets', duration: 118.813, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3166475516&ts=1656102395&t=87afb3e2c66ebbcb8d531550bc944cc6a04b1e56' - }, - artist: { - name: 'Tommy And The Rockets', - url: 'https://tommyandtherockets.bandcamp.com' + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3166475516&ts=1686408280&t=9da2398b87c445a2b8c52dbe3e2dad6a86e927f1' } }, { @@ -155,81 +195,66 @@ name: 'Some Kinda Fun', url: 'https://tommyandtherockets.bandcamp.com/track/some-kinda-fun', imageUrl: 'https://f4.bcbits.com/img/a1431546323_16.jpg', - featuredTrack: { - position: 6, - name: 'Some Kinda Fun', - artist: 'Psychotic Youth / Tommy And The Rockets', - duration: 140.701, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1766763879&ts=1656102395&t=c1f1cc609bd56c223748e12ddc4bcd18ebef1292' - }, artist: { name: 'Psychotic Youth / Tommy And The Rockets', url: 'https://tommyandtherockets.bandcamp.com' - } + }, + duration: 140.701, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1766763879&ts=1686408280&t=cfb833e48be32d0d355282978f717664c2959076' }, { type: 'track', name: 'The Perfect Place', url: 'https://powerpopacademy.bandcamp.com/track/the-perfect-place', imageUrl: 'https://f4.bcbits.com/img/a1906864882_16.jpg', - featuredTrack: { - position: 4, - name: 'The Perfect Place', - artist: 'David Myhr', - duration: 236.107, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1044351484&ts=1656102395&t=267284c39370615295a1629298b2f667a26c043b' - }, artist: { name: 'David Myhr', url: 'https://powerpopacademy.bandcamp.com' - } + }, + duration: 236.107, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1044351484&ts=1686408280&t=a5ac8eeb491b66dfba3d832a2973c071c7248192' }, { type: 'album', name: 'Myrtle Soup', url: 'https://johnmyrtle.bandcamp.com/album/myrtle-soup', imageUrl: 'https://f4.bcbits.com/img/a1273718232_16.jpg', + artist: { name: 'John Myrtle', url: 'https://johnmyrtle.bandcamp.com' }, featuredTrack: { position: 1, name: 'Get Her Off My Mind', artist: 'John Myrtle', duration: 188.11, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1491901815&ts=1656102395&t=c5a2050f81fac1574fba77b2829720ecc792819e' - }, - artist: { name: 'John Myrtle', url: 'https://johnmyrtle.bandcamp.com' } + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1491901815&ts=1686408280&t=bca79244789b0809f67adb4b53cfdaf1cc77027d' + } }, { type: 'track', name: 'Things Are Looking Up', url: 'https://theboyleastlikelyto.bandcamp.com/track/things-are-looking-up', imageUrl: 'https://f4.bcbits.com/img/a2168435596_16.jpg', - featuredTrack: { - position: null, - name: 'Things Are Looking Up', - artist: 'the boy least likely to', - duration: 198.229, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=37417908&ts=1656102395&t=0fbee63e1981dba53add59ca8849dd3701f23bbe' - }, artist: { name: 'the boy least likely to', url: 'https://theboyleastlikelyto.bandcamp.com' - } + }, + duration: 198.229, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=37417908&ts=1686408280&t=5ff8658b86fcb6e8dd2d2b224f4d5f990c2edfa1' }, { type: 'album', name: 'A Little Dancing', url: 'https://acidhousekings.bandcamp.com/album/a-little-dancing', imageUrl: 'https://f4.bcbits.com/img/a3532826210_16.jpg', + artist: { + name: 'Acid House Kings', + url: 'https://acidhousekings.bandcamp.com' + }, featuredTrack: { position: 1, name: 'A Little Dancing', artist: 'Acid House Kings', duration: 167.6, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3861218995&ts=1656102395&t=6157c8b83474b51612b428fac8e276b7d9393aa9' - }, - artist: { - name: 'Acid House Kings', - url: 'https://acidhousekings.bandcamp.com' + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3861218995&ts=1686408280&t=10dc8eaa4813177ec20e1fec04a0a4e581f99704' } }, { @@ -237,432 +262,284 @@ name: 'Even When It Rains', url: 'https://jeremyfisher.bandcamp.com/track/even-when-it-rains', imageUrl: 'https://f4.bcbits.com/img/a3003167714_16.jpg', - featuredTrack: { - position: 1, - name: 'Even When It Rains', - artist: 'Jeremy Fisher', - duration: 179.148, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3553064828&ts=1656102395&t=b02a82476e003e4b67ba8f08e9cb976ec3f47a9f' - }, artist: { name: 'Jeremy Fisher', url: 'https://jeremyfisher.bandcamp.com' - } + }, + duration: 179.148, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3553064828&ts=1686408280&t=8b0798ccfd95f96df5d86a0eae91702bc0b1872b' }, { type: 'track', name: 'Beauty School Dropout', url: 'https://kidgulliver1.bandcamp.com/track/beauty-school-dropout', imageUrl: 'https://f4.bcbits.com/img/a1665930893_16.jpg', - featuredTrack: { - position: null, - name: 'Beauty School Dropout', - artist: 'Kid Gulliver', - duration: 203.089, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=519089851&ts=1656102395&t=bc444183261a6f0cd39dc5c6b3a5cae469b7eb0b' - }, artist: { name: 'Kid Gulliver', url: 'https://kidgulliver1.bandcamp.com' - } - }, + }, + duration: 203.089, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=519089851&ts=1686408280&t=93eb1910b2cfc62d8a048a7482d75de5f8eafccb' + } + ], + total: 110, + continuation: { fanId: 2723895, token: '1618850795:519089851:t::' } +} +Fetching more by continuation... +{ + items: [ { type: 'track', name: 'Heaven Beats Iowa', url: 'https://guidedbyvoices.bandcamp.com/track/heaven-beats-iowa', imageUrl: 'https://f4.bcbits.com/img/a1601812993_16.jpg', - featuredTrack: { - position: 6, - name: 'Heaven Beats Iowa', - artist: 'Guided By Voices', - duration: 169.227, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=4087641480&ts=1656102395&t=f67cbdbd10c7d28a777799b380a24243ccbd02e3' - }, artist: { name: 'Guided By Voices', url: 'https://guidedbyvoices.bandcamp.com' - } + }, + duration: 169.227, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=4087641480&ts=1686408281&t=1b999217be8baa52d55173b44c0decf924980793' }, { type: 'track', name: 'Song in my Heart', url: 'https://jeremyfisher.bandcamp.com/track/song-in-my-heart', imageUrl: 'https://f4.bcbits.com/img/a1532985069_16.jpg', - featuredTrack: { - position: 7, - name: 'Song in my Heart', - artist: 'Jeremy Fisher', - duration: 211.014, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3192698356&ts=1656102395&t=089aef5156f84135bbc9daf06f9730755dd62398' - }, artist: { name: 'Jeremy Fisher', url: 'https://jeremyfisher.bandcamp.com' - } + }, + duration: 211.014, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3192698356&ts=1686408281&t=85f7a808cb073cf7001dd1fffff4df5d93af2dea' }, { type: 'track', name: 'Summer Means Fun', url: 'https://tommyandtherockets.bandcamp.com/track/summer-means-fun', imageUrl: 'https://f4.bcbits.com/img/a2565411884_16.jpg', - featuredTrack: { - position: 2, - name: 'Summer Means Fun', - artist: 'Tommy And The Rockets', - duration: 110.339, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1452549487&ts=1656102395&t=82c09211b9a90706f6d4aebb79463fc1137fa77f' - }, artist: { name: 'Tommy And The Rockets', url: 'https://tommyandtherockets.bandcamp.com' - } + }, + duration: 110.339, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1452549487&ts=1686408281&t=f75e56f31425d386b568f8e465dddbe2bf492629' }, { type: 'track', name: "We're Going Surfin'", url: 'https://tommyandtherockets.bandcamp.com/track/were-going-surfin', imageUrl: 'https://f4.bcbits.com/img/a2565411884_16.jpg', - featuredTrack: { - position: 1, - name: "We're Going Surfin'", - artist: 'Tommy And The Rockets', - duration: 147.697, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=2085625917&ts=1656102395&t=7abfa414c63a5e871c11d6dcbbb492830d034e4e' - }, artist: { name: 'Tommy And The Rockets', url: 'https://tommyandtherockets.bandcamp.com' - } - } - ], - total: 106, - continuationToken: { fanId: 2723895, token: '1618850708:2085625917:t::' } -} -Fetching more with continuation token... -{ - items: [ + }, + duration: 147.697, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=2085625917&ts=1686408281&t=3303e09cb03e177d1d9a92ecb7d95767da4f7ab8' + }, { type: 'track', name: 'Keep Your Hands Off My Baby', url: 'https://tommyandtherockets.bandcamp.com/track/keep-your-hands-off-my-baby', - imageUrl: 'https://f4.bcbits.com/img/a2133945610_9.jpg', - featuredTrack: { - position: null, - name: 'Keep Your Hands Off My Baby', - artist: 'Tommy And The Rockets', - duration: 145.692, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3811139469&ts=1656102397&t=ce6daf9f6d0028fcbd47034a4c2a9445a5591760' - }, + imageUrl: 'https://f4.bcbits.com/img/a2809841177_16.jpg', artist: { name: 'Tommy And The Rockets', url: 'https://tommyandtherockets.bandcamp.com' - } + }, + duration: 145.692, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3811139469&ts=1686408281&t=d8b800e0b03d7746e4768fd32905e79d16e2ff20' }, { type: 'track', name: "They Don't Know", url: 'https://peteyorn.bandcamp.com/track/they-dont-know', - imageUrl: 'https://f4.bcbits.com/img/a627759544_9.jpg', - featuredTrack: { - position: null, - name: "They Don't Know", - artist: 'Pete Yorn', - duration: 179.624, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3978220126&ts=1656102397&t=2491be79bfcf1bc6c36c247c9ff5421bb8ba3102' - }, - artist: { name: 'Pete Yorn', url: 'https://peteyorn.bandcamp.com' } + imageUrl: 'https://f4.bcbits.com/img/a627759544_16.jpg', + artist: { name: 'Pete Yorn', url: 'https://peteyorn.bandcamp.com' }, + duration: 179.624, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3978220126&ts=1686408281&t=cfbd81af3a4732eaf37b5c45e900047ce0493059' }, { type: 'track', name: 'Drip a Drop', url: 'https://theflatfivechicago.bandcamp.com/track/drip-a-drop-2', - imageUrl: 'https://f4.bcbits.com/img/a2005340941_9.jpg', - featuredTrack: { - position: 1, - name: 'Drip a Drop', - artist: 'The Flat Five Chicago', - duration: 159.427, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1688632191&ts=1656102397&t=9866d8ab184189f668ad54043545d6a3af1ec602' - }, + imageUrl: 'https://f4.bcbits.com/img/a2005340941_16.jpg', artist: { name: 'The Flat Five Chicago', url: 'https://theflatfivechicago.bandcamp.com' - } + }, + duration: 159.427, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1688632191&ts=1686408281&t=dc7e434f23cb6a820a78570d6983a08a9b192df1' }, { type: 'track', name: 'It Will Still Be Christmas', url: 'https://theboyleastlikelyto.bandcamp.com/track/it-will-still-be-christmas', - imageUrl: 'https://f4.bcbits.com/img/a1721156636_9.jpg', - featuredTrack: { - position: null, - name: 'It Will Still Be Christmas', - artist: 'the boy least likely to', - duration: 207.461, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=425690647&ts=1656102397&t=05b5e6423ff21b9503e0119f15fa3c254a16ff54' - }, + imageUrl: 'https://f4.bcbits.com/img/a1721156636_16.jpg', artist: { name: 'the boy least likely to', url: 'https://theboyleastlikelyto.bandcamp.com' - } + }, + duration: 207.461, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=425690647&ts=1686408281&t=86386784b19391ba2c2e84e6a9797b5c4e0045c3' }, { type: 'album', name: 'Marelle', url: 'https://julieetjoe.bandcamp.com/album/marelle-2', - imageUrl: 'https://f4.bcbits.com/img/a3131911262_9.jpg', + imageUrl: 'https://f4.bcbits.com/img/a3131911262_16.jpg', + artist: { name: 'JULIE ET JOE', url: 'https://julieetjoe.bandcamp.com' }, featuredTrack: { position: 2, name: 'Le Midi', artist: 'JULIE ET JOE', duration: 178.229, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3172712588&ts=1656102397&t=14e88c46ade123ea7fb7b6bd9527d712920626d5' - }, - artist: { name: 'JULIE ET JOE', url: 'https://julieetjoe.bandcamp.com' } + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3172712588&ts=1686408281&t=b5cacbb4766a94b8584ed13a8187e840678fa6c1' + } }, { type: 'track', name: 'Take A Little Look (Feat. Luci Ashbourne)', url: 'https://theyearning.bandcamp.com/track/take-a-little-look-feat-luci-ashbourne', - imageUrl: 'https://f4.bcbits.com/img/a3015684477_9.jpg', - featuredTrack: { - position: 7, - name: 'Take A Little Look (Feat. Luci Ashbourne)', - artist: 'THE YEARNING', - duration: 152.565, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3049073401&ts=1656102397&t=74dc3390e3a2e1f1550a433ebb5427b697c8ef54' - }, - artist: { name: 'THE YEARNING', url: 'https://theyearning.bandcamp.com' } + imageUrl: 'https://f4.bcbits.com/img/a3015684477_16.jpg', + artist: { name: 'THE YEARNING', url: 'https://theyearning.bandcamp.com' }, + duration: 152.565, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3049073401&ts=1686408281&t=bef2ba7cb5ebaf3b29cb8b93f4163cd30524f7cd' }, { type: 'track', name: '¿Donde estás, Carmen Sandiego?', url: 'https://elgenioequivocado.bandcamp.com/track/donde-est-s-carmen-sandiego', - imageUrl: 'https://f4.bcbits.com/img/a1328393092_9.jpg', - featuredTrack: { - position: null, - name: '¿Donde estás, Carmen Sandiego?', - artist: 'Cosmen', - duration: 206.996, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=2312708988&ts=1656102397&t=a86c36700f902d8f66633d1863ea5dac27fd0841' - }, - artist: { name: 'Cosmen', url: 'https://elgenioequivocado.bandcamp.com' } + imageUrl: 'https://f4.bcbits.com/img/a1328393092_16.jpg', + artist: { name: 'Cosmen', url: 'https://elgenioequivocado.bandcamp.com' }, + duration: 206.996, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=2312708988&ts=1686408281&t=09f7ff30b8644ec1b6b48ffc7b4e89c14374bd99' }, { type: 'track', name: 'Wrong Wrong Wrong', url: 'https://mrossperkins.bandcamp.com/track/wrong-wrong-wrong', - imageUrl: 'https://f4.bcbits.com/img/a2873664564_9.jpg', - featuredTrack: { - position: null, - name: 'Wrong Wrong Wrong', - artist: 'M Ross Perkins', - duration: 175.964, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=232494249&ts=1656102397&t=bb9fb794733e43bd14ba7fe6106eb8f9c7284774' - }, + imageUrl: 'https://f4.bcbits.com/img/a2873664564_16.jpg', artist: { name: 'M Ross Perkins', url: 'https://mrossperkins.bandcamp.com' - } + }, + duration: 175.964, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=232494249&ts=1686408281&t=cec936551d67d18384781979fb954c21befd0f47' }, { type: 'album', name: 'Himalaya', url: 'https://summerfiction.bandcamp.com/album/himalaya', - imageUrl: 'https://f4.bcbits.com/img/a2161060600_9.jpg', + imageUrl: 'https://f4.bcbits.com/img/a2161060600_16.jpg', + artist: { + name: 'Summer Fiction', + url: 'https://summerfiction.bandcamp.com' + }, featuredTrack: { position: 3, name: 'Perfume Paper', artist: 'Summer Fiction', duration: 185.92, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3181539157&ts=1656102397&t=6d456f2ad6c7d2fd4116e91d4aad9a116659b979' - }, - artist: { - name: 'Summer Fiction', - url: 'https://summerfiction.bandcamp.com' + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3181539157&ts=1686408281&t=be9f2452c28d880b074e133d6ec59cc849c00d2a' } }, { type: 'track', name: 'Anywhere You Run', url: 'https://sixtonnesdechairrecords.bandcamp.com/track/anywhere-you-run', - imageUrl: 'https://f4.bcbits.com/img/a3547034463_9.jpg', - featuredTrack: { - position: 1, - name: 'Anywhere You Run', - artist: 'Doug Tuttle', - duration: 222.177, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3703166127&ts=1656102397&t=f6f558024290526facac30bd6cdfe454f2fcacd0' - }, + imageUrl: 'https://f4.bcbits.com/img/a3547034463_16.jpg', artist: { name: 'Doug Tuttle', url: 'https://sixtonnesdechairrecords.bandcamp.com' - } + }, + duration: 222.177, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3703166127&ts=1686408281&t=8dc5f4a69c3a8079331fe0efd0aed5cc60dcaaa4' }, { type: 'album', name: 'Le Midi', url: 'https://julieetjoe.bandcamp.com/album/le-midi', - imageUrl: 'https://f4.bcbits.com/img/a2213370402_9.jpg', + imageUrl: 'https://f4.bcbits.com/img/a2213370402_16.jpg', + artist: { name: 'JULIE ET JOE', url: 'https://julieetjoe.bandcamp.com' }, featuredTrack: { position: 1, name: 'Le Midi', artist: 'JULIE ET JOE', duration: 178.229, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=935467930&ts=1656102397&t=8799d9eee7ac72ee6242e02bfb0bded5ee8d5bae' - }, - artist: { name: 'JULIE ET JOE', url: 'https://julieetjoe.bandcamp.com' } + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=935467930&ts=1686408281&t=f7863f7c1f4156a27c2e9e6d4a08485ad925eaec' + } }, { type: 'track', name: "Only When I'm Dancing (Feat. Luci Ashbourne)", url: 'https://theyearning.bandcamp.com/track/only-when-im-dancing-feat-luci-ashbourne', - imageUrl: 'https://f4.bcbits.com/img/a2550854079_9.jpg', - featuredTrack: { - position: 1, - name: "Only When I'm Dancing (Feat. Luci Ashbourne)", - artist: 'THE YEARNING', - duration: 279.458, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1844772819&ts=1656102397&t=d74d9dbe5696dd100b695abe07c9d0d9439295c9' - }, - artist: { name: 'THE YEARNING', url: 'https://theyearning.bandcamp.com' } + imageUrl: 'https://f4.bcbits.com/img/a2550854079_16.jpg', + artist: { name: 'THE YEARNING', url: 'https://theyearning.bandcamp.com' }, + duration: 279.458, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1844772819&ts=1686408281&t=a6a4b9276cfb47f4d915ab4d70020993ca8dadf3' }, { type: 'album', name: 'The Wrong End Of A Rainbow', url: 'https://theboyleastlikelyto.bandcamp.com/album/the-wrong-end-of-a-rainbow', - imageUrl: 'https://f4.bcbits.com/img/a1981491866_9.jpg', + imageUrl: 'https://f4.bcbits.com/img/a1981491866_16.jpg', + artist: { + name: 'the boy least likely to', + url: 'https://theboyleastlikelyto.bandcamp.com' + }, featuredTrack: { position: 1, name: "It Could've Been Me - Pop Strings version (featuring Gwenno)", artist: 'the boy least likely to', duration: 229.747, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1993649907&ts=1656102397&t=9ce6bbcf1dadd27f9a83626e9122f578adcb43c1' - }, - artist: { - name: 'the boy least likely to', - url: 'https://theboyleastlikelyto.bandcamp.com' + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1993649907&ts=1686408281&t=bddd026b2fc596f2e7d41f6d8369d6ac47d5da63' } }, { type: 'album', name: "It's A Long Way Back From A Dream", url: 'https://legendsofcountry.bandcamp.com/album/its-a-long-way-back-from-a-dream', - imageUrl: 'https://f4.bcbits.com/img/a188771935_9.jpg', + imageUrl: 'https://f4.bcbits.com/img/a188771935_16.jpg', + artist: { + name: 'Legends Of Country', + url: 'https://legendsofcountry.bandcamp.com' + }, featuredTrack: { position: 2, name: 'From St George To Snowflake', artist: 'Legends Of Country', duration: 176.226, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3662968907&ts=1656102397&t=afe712f39436b6899f40c80547fa9a1d9f2debf8' - }, - artist: { - name: 'Legends Of Country', - url: 'https://legendsofcountry.bandcamp.com' + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3662968907&ts=1686408281&t=00d7cd3da0eb5a5c94a3a928bd61f94159f84bee' } }, { type: 'track', name: 'Jelly And Jam', url: 'https://legendsofcountry.bandcamp.com/track/jelly-and-jam-2', - imageUrl: 'https://f4.bcbits.com/img/a3471635391_9.jpg', - featuredTrack: { - position: null, - name: 'Jelly And Jam', - artist: 'Legends Of Country', - duration: 207.547, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=893906699&ts=1656102397&t=ef1312083115e54209f44faa9136df307b6c74c4' - }, + imageUrl: 'https://f4.bcbits.com/img/a3471635391_16.jpg', artist: { name: 'Legends Of Country', url: 'https://legendsofcountry.bandcamp.com' - } + }, + duration: 207.547, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=893906699&ts=1686408281&t=fae605db0e46bfd33bbdc4a5c5216f25b81c6541' }, { type: 'album', name: "That's What We Talk About When We Talk About Country", url: 'https://legendsofcountry.bandcamp.com/album/thats-what-we-talk-about-when-we-talk-about-country', - imageUrl: 'https://f4.bcbits.com/img/a2267413619_9.jpg', + imageUrl: 'https://f4.bcbits.com/img/a2267413619_16.jpg', + artist: { + name: 'Legends Of Country', + url: 'https://legendsofcountry.bandcamp.com' + }, featuredTrack: { position: 1, name: "That's What We Talk About When We Talk About Country", artist: 'Legends Of Country', duration: 223.487, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=316972935&ts=1656102397&t=118257273dabec684a1d2c980b62e1ba20aa1b7d' - }, - artist: { - name: 'Legends Of Country', - url: 'https://legendsofcountry.bandcamp.com' - } - }, - { - type: 'album', - name: 'Talk About Country', - url: 'https://legendsofcountry.bandcamp.com/album/talk-about-country', - imageUrl: 'https://f4.bcbits.com/img/a819173494_9.jpg', - featuredTrack: { - position: 4, - name: 'Old Guns', - artist: 'Legends Of Country', - duration: 145.467, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=990888666&ts=1656102397&t=cc7af85f8ad4ffeaf28b766e7114d6779545d403' - }, - artist: { - name: 'Legends Of Country', - url: 'https://legendsofcountry.bandcamp.com' - } - }, - { - type: 'track', - name: 'The Saturday Dads', - url: 'https://legendsofcountry.bandcamp.com/track/the-saturday-dads-2', - imageUrl: 'https://f4.bcbits.com/img/a4166120808_9.jpg', - featuredTrack: { - position: null, - name: 'The Saturday Dads', - artist: 'Legends Of Country', - duration: 276.893, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=2447825189&ts=1656102397&t=da799506df7430cc1a3ed3c1cdc2d93c150f4a58' - }, - artist: { - name: 'Legends Of Country', - url: 'https://legendsofcountry.bandcamp.com' - } - }, - { - type: 'track', - name: 'Turn To Dolly (Acoustic Version)', - url: 'https://legendsofcountry.bandcamp.com/track/turn-to-dolly-acoustic-version', - imageUrl: 'https://f4.bcbits.com/img/a3739747720_9.jpg', - featuredTrack: { - position: null, - name: 'Turn To Dolly (Acoustic Version)', - artist: 'Legends Of Country', - duration: 195.138, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3488224347&ts=1656102397&t=ffa82c8094e7678ba17e270ad1d7fe1a9e4df3d5' - }, - artist: { - name: 'Legends Of Country', - url: 'https://legendsofcountry.bandcamp.com' - } - }, - { - type: 'track', - name: 'From St George To Snowflake (Single)', - url: 'https://legendsofcountry.bandcamp.com/track/from-st-george-to-snowflake-single', - imageUrl: 'https://f4.bcbits.com/img/a3008938389_9.jpg', - featuredTrack: { - position: null, - name: 'From St George To Snowflake (Single)', - artist: 'Legends Of Country', - duration: 176.226, - streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3669162333&ts=1656102397&t=8b5669f24b6c889ccad71ac15c2e27f27f009140' - }, - artist: { - name: 'Legends Of Country', - url: 'https://legendsofcountry.bandcamp.com' + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=316972935&ts=1686408281&t=c301d9b7e2441d3ff59add71c3096cc00ec8080b' } } ], - continuationToken: { fanId: 2723895, token: '1588615844:3669162333:t::' } + continuation: { fanId: 2723895, token: '1588615844:1758940940:a::' } } diff --git a/examples/fan/getFollowingArtistsAndLabels.ts b/examples/fan/getFollowingArtistsAndLabels.ts new file mode 100644 index 0000000..ce00277 --- /dev/null +++ b/examples/fan/getFollowingArtistsAndLabels.ts @@ -0,0 +1,23 @@ +import bcfetch from '../../'; +import util from 'util'; + +const username = 'patrickkfkan'; + +const params = { + target: username, + imageFormat: 'bio_featured' +}; + +bcfetch.fan.getFollowingArtistsAndLabels(params).then(async (results) => { + console.log(util.inspect(results, false, null, false)); + + if (results.continuation) { + console.log('Fetching more by continuation...'); + + const moreResults = await bcfetch.fan.getFollowingArtistsAndLabels({ + ...params, + target: results.continuation + }); + console.log(util.inspect(moreResults, false, null, false)); + } +}); diff --git a/examples/getFanFollowingArtistsAndLabels_output.txt b/examples/fan/getFollowingArtistsAndLabels_output.txt similarity index 84% rename from examples/getFanFollowingArtistsAndLabels_output.txt rename to examples/fan/getFollowingArtistsAndLabels_output.txt index 90e73c8..2f16d96 100644 --- a/examples/getFanFollowingArtistsAndLabels_output.txt +++ b/examples/fan/getFollowingArtistsAndLabels_output.txt @@ -1,402 +1,402 @@ { items: [ + { + name: 'Cœur de pirate', + location: 'Montréal, Québec', + url: 'https://coeurdepirate.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/26415167_28.jpg' + }, { name: 'M. Ward', - url: 'https://m-ward.bandcamp.com', location: 'Portland, Oregon', + url: 'https://m-ward.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/18177684_28.jpg' }, { name: 'Allison Crutchfield', - url: 'https://allisoncrutchfield.bandcamp.com', location: 'Philadelphia, Pennsylvania', + url: 'https://allisoncrutchfield.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/8598378_28.jpg' }, { name: 'Coco Hames', - url: 'https://cocohames.bandcamp.com', location: 'Memphis, Tennessee', + url: 'https://cocohames.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/9197807_28.jpg' }, { name: 'Neutral Milk Hotel', - url: 'https://neutralmilkhotel.bandcamp.com', location: '', + url: 'https://neutralmilkhotel.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/13466114_28.jpg' }, { name: 'Spoon', - url: 'https://spoontheband.bandcamp.com', location: 'Austin, Texas', + url: 'https://spoontheband.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/21448647_28.jpg' }, { name: 'Crooked Fingers', - url: 'https://crookedfingers.bandcamp.com', location: 'North Carolina', + url: 'https://crookedfingers.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/13404323_28.jpg' }, { name: 'The Rock*A*Teens', - url: 'https://therockateens.bandcamp.com', location: 'Atlanta, Georgia', + url: 'https://therockateens.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/13223966_28.jpg' }, { name: 'The Essex Green', - url: 'https://theessexgreen.bandcamp.com', location: 'Brooklyn, New York', + url: 'https://theessexgreen.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/13167432_28.jpg' }, { name: 'She & Him', - url: 'https://sheandhim.bandcamp.com', location: 'Los Angeles, California', + url: 'https://sheandhim.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/28704726_28.jpg' }, { name: 'Ought', - url: 'https://ought.bandcamp.com', location: 'Montréal, Québec', + url: 'https://ought.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/11617874_28.jpg' }, { name: 'Spider Bags', - url: 'https://spiderbags.bandcamp.com', location: 'Chapel Hill, North Carolina', + url: 'https://spiderbags.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/6370483_28.jpg' }, { name: 'Tracyanne & Danny', - url: 'https://tracyanneanddanny.bandcamp.com', location: 'UK', + url: 'https://tracyanneanddanny.bandcamp.com', 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' + url: 'https://fuckedup.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/29719426_28.jpg' }, { name: "Swearin'", - url: 'https://swearin.bandcamp.com', location: 'Philadelphia, Pennsylvania', + url: 'https://swearin.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/19671909_28.jpg' }, { name: 'The Spinanes', - url: 'https://thespinanes.bandcamp.com', location: 'Portland, Oregon', + url: 'https://thespinanes.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/14533028_28.jpg' }, { name: 'Telekinesis', - url: 'https://telekinesis.bandcamp.com', location: 'Seattle, Washington', + url: 'https://telekinesis.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/14904916_28.jpg' }, { name: 'The Broken West', - url: 'https://thebrokenwest.bandcamp.com', location: 'Los Angeles, California', + url: 'https://thebrokenwest.bandcamp.com', 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' + url: 'https://titusandronicus.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/29288983_28.jpg' }, { name: 'SACRED//PAWS', - url: 'https://sacredpaws.bandcamp.com', location: 'Glasgow, UK', + url: 'https://sacredpaws.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/41645_28.jpg' }, { name: 'Imperial Teen', - url: 'https://imperialteen.bandcamp.com', location: 'San Francisco, California', + url: 'https://imperialteen.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/16255829_28.jpg' }, { name: 'Ex Hex', - url: 'https://exhexband.bandcamp.com', location: 'Washington, D.C.', + url: 'https://exhexband.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/15244159_28.jpg' }, { name: 'Apex Manor', - url: 'https://apexmanor.bandcamp.com', location: 'Los Angeles, California', + url: 'https://apexmanor.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/15921186_28.jpg' }, { name: 'David Kilgour', - url: 'https://davidkilgour.bandcamp.com', location: 'Dunedin', + url: 'https://davidkilgour.bandcamp.com', 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.', + url: 'https://g-a-u-c-h-e.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/16153963_28.jpg' }, { name: 'Eric Bachmann', - url: 'https://ericbachmann.bandcamp.com', location: 'North Carolina', + url: 'https://ericbachmann.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/17551127_28.jpg' }, { name: 'Little Scream', - url: 'https://littlescream.bandcamp.com', location: 'Montréal, Québec', + url: 'https://littlescream.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/5272073_28.jpg' }, { name: 'The Love Language', - url: 'https://thelovelanguage.bandcamp.com', location: 'Chapel Hill, North Carolina', + url: 'https://thelovelanguage.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/13444279_28.jpg' }, { name: 'Richard Buckner', - url: 'https://richardbuckner.bandcamp.com', location: 'New York, New York', + url: 'https://richardbuckner.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/17863325_28.jpg' }, { name: 'Polvo', - url: 'https://polvonc.bandcamp.com', location: 'Chapel Hill, North Carolina', + url: 'https://polvonc.bandcamp.com', 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' + url: 'https://martinfrawley.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/31110755_28.jpg' }, { name: 'The Music Tapes', - url: 'https://themusictapes.bandcamp.com', location: 'Athens, Georgia', + url: 'https://themusictapes.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/17776773_28.jpg' }, { name: 'Mt. Wilson Repeater', - url: 'https://mtwilsonrptr.bandcamp.com', location: '', + url: 'https://mtwilsonrptr.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/19621062_28.jpg' }, { name: 'Sweet Spirit', - url: 'https://sweetspirittheband.bandcamp.com', location: 'Austin, Texas', + url: 'https://sweetspirittheband.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/19349627_28.jpg' }, { name: 'Jade Hairpins', - url: 'https://jadehairpins.bandcamp.com', location: '', + url: 'https://jadehairpins.bandcamp.com', 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' + url: 'https://archersofloaf.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/29225752_28.jpg' }, { name: 'Mikal Cronin', - url: 'https://mikalcronin.bandcamp.com', location: 'Los Angeles, California', + url: 'https://mikalcronin.bandcamp.com', 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' + url: 'https://loubarlow.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/29880016_28.jpg' }, { name: 'Sneaks', - url: 'https://sneaks.bandcamp.com', location: 'Washington, D.C.', + url: 'https://sneaks.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/19714164_28.jpg' }, { name: 'H.C. McEntire', - url: 'https://hcmcentire.bandcamp.com', location: 'Durham, North Carolina', + url: 'https://hcmcentire.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/20946952_28.jpg' }, { name: 'William Tyler', - url: 'https://williamtyler.bandcamp.com', location: 'Los Angeles, California', + url: 'https://williamtyler.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/18844022_28.jpg' }, { name: 'The Clientele', - url: 'https://theclientele.bandcamp.com', location: 'London, UK', + url: 'https://theclientele.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/10580560_28.jpg' }, { name: 'Bob Mould', - url: 'https://bobmould.bandcamp.com', location: 'San Francisco, California', + url: 'https://bobmould.bandcamp.com', imageUrl: 'https://f4.bcbits.com/img/20299197_28.jpg' }, { name: 'Mike Krol', - url: 'https://mikekrol.bandcamp.com', location: 'Los Angeles, California', + url: 'https://mikekrol.bandcamp.com', 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' + url: 'https://fruit-bats.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/31581619_28.jpg' } ], - total: 68, - continuationToken: { fanId: 8563805, token: '1655975469:2121405720' } + total: 69, + continuation: { fanId: 8563805, token: '1655975474:2565104658' } } -Fetching more with continuation token... +Fetching more by continuation... { items: [ + { + name: 'The Clean', + location: 'New Zealand', + url: 'https://theclean.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/23754516_28.jpg' + }, { name: 'Will Butler', - url: 'https://willbutler.bandcamp.com', location: 'Brooklyn, New York', - imageUrl: 'https://f4.bcbits.com/img/24918652_21.jpg' + url: 'https://willbutler.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/24918652_28.jpg' }, { name: 'Teenage Fanclub', - url: 'https://teenage-fanclub.bandcamp.com', location: 'Glasgow, UK', - imageUrl: 'https://f4.bcbits.com/img/24101151_21.jpg' + url: 'https://teenage-fanclub.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/24101151_28.jpg' }, { name: 'Cable Ties', - url: 'https://cableties.bandcamp.com', location: 'Melbourne, Australia', - imageUrl: 'https://f4.bcbits.com/img/8448159_21.jpg' + url: 'https://cableties.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/8448159_28.jpg' }, { name: 'Reigning Sound', - url: 'https://reigningsound.bandcamp.com', location: 'Asheville, North Carolina', - imageUrl: 'https://f4.bcbits.com/img/23915882_21.jpg' + url: 'https://reigningsound.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/23915882_28.jpg' }, { name: 'Hiss Golden Messenger', - url: 'https://hissgoldenmessenger.bandcamp.com', location: 'North Carolina', - imageUrl: 'https://f4.bcbits.com/img/24016536_21.jpg' + url: 'https://hissgoldenmessenger.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/24016536_28.jpg' }, { name: 'Torres', - url: 'https://torrestorrestorres.bandcamp.com', location: 'New York, New York', - imageUrl: 'https://f4.bcbits.com/img/24864649_21.jpg' + url: 'https://torrestorrestorres.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/24864649_28.jpg' }, { name: 'DAWN', - url: 'https://dawnrichard.bandcamp.com', location: 'Los Angeles, California', - imageUrl: 'https://f4.bcbits.com/img/23711418_21.jpg' + url: 'https://dawnrichard.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/30483218_28.jpg' }, { name: 'Caribou', - url: 'https://caribouband.bandcamp.com', location: 'London, UK', - imageUrl: 'https://f4.bcbits.com/img/18041279_21.jpg' + url: 'https://caribouband.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/18041279_28.jpg' }, { name: 'A Giant Dog', - url: 'https://agiantdog.bandcamp.com', location: 'Austin, Texas', - imageUrl: 'https://f4.bcbits.com/img/651146_21.jpg' + url: 'https://agiantdog.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/651146_28.jpg' }, { name: 'Mac McCaughan', - url: 'https://macmccaughan.bandcamp.com', location: 'Chapel Hill, North Carolina', - imageUrl: 'https://f4.bcbits.com/img/4961611_21.jpg' + url: 'https://macmccaughan.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/4961611_28.jpg' }, { name: 'The Magnetic Fields', - url: 'https://themagneticfields.bandcamp.com', location: 'New York, New York', - imageUrl: 'https://f4.bcbits.com/img/14275749_21.jpg' + url: 'https://themagneticfields.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/14275749_28.jpg' }, { name: 'Carson McHone', - url: 'https://carsonmchone.bandcamp.com', location: 'Austin, Texas', - imageUrl: 'https://f4.bcbits.com/img/26448389_21.jpg' + url: 'https://carsonmchone.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/26448389_28.jpg' }, { name: 'Destroyer', - url: 'https://destroyer.bandcamp.com', location: 'Vancouver, British Columbia', - imageUrl: 'https://f4.bcbits.com/img/27319221_21.jpg' + url: 'https://destroyer.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/27319221_28.jpg' }, { name: 'Ibibio Sound Machine', - url: 'https://ibibiosoundmachine.bandcamp.com', location: 'UK', - imageUrl: 'https://f4.bcbits.com/img/27398052_21.jpg' + url: 'https://ibibiosoundmachine.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/27398052_28.jpg' }, { name: 'Superchunk', - url: 'https://superchunk.bandcamp.com', location: 'Chapel Hill, North Carolina', - imageUrl: 'https://f4.bcbits.com/img/26971728_21.jpg' + url: 'https://superchunk.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/26971728_28.jpg' }, { name: 'Hollie Cook', - url: 'https://holliecook.bandcamp.com', location: 'London, UK', - imageUrl: 'https://f4.bcbits.com/img/28056444_21.jpg' + url: 'https://holliecook.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/28056444_28.jpg' }, { name: 'Redd Kross', - url: 'https://reddkross.bandcamp.com', location: 'Hawthorne, California', - imageUrl: 'https://f4.bcbits.com/img/28555577_21.jpg' + url: 'https://reddkross.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/28555577_28.jpg' }, { name: 'Friendship', - url: 'https://friendshipphl.bandcamp.com', location: 'Philadelphia, Pennsylvania', - imageUrl: 'https://f4.bcbits.com/img/28727388_21.jpg' + url: 'https://friendshipphl.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/28727388_28.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' + url: 'https://talldwarfs.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/28914236_28.jpg' } ], - continuationToken: { fanId: 8563805, token: '1655975366:3856108936' } + continuation: { fanId: 8563805, token: '1655975373:1269582496' } } diff --git a/examples/fan/getFollowingGenres.ts b/examples/fan/getFollowingGenres.ts new file mode 100644 index 0000000..084552a --- /dev/null +++ b/examples/fan/getFollowingGenres.ts @@ -0,0 +1,23 @@ +import bcfetch from '../../'; +import util from 'util'; + +const username = 'patrickkfkan'; + +const params = { + target: username, + imageFormat: 'art_tags_large' +}; + +bcfetch.fan.getFollowingGenres(params).then(async (results) => { + console.log(util.inspect(results, false, null, false)); + + if (results.continuation) { + console.log('Fetching more by continuation...'); + + const moreResults = await bcfetch.fan.getFollowingGenres({ + ...params, + target: results.continuation + }); + console.log(util.inspect(moreResults, false, null, false)); + } +}); diff --git a/examples/fan/getFollowingGenres_output.txt b/examples/fan/getFollowingGenres_output.txt new file mode 100644 index 0000000..ceb886a --- /dev/null +++ b/examples/fan/getFollowingGenres_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/a1088493100_9.jpg', + 'https://f4.bcbits.com/img/a2492588706_9.jpg', + 'https://f4.bcbits.com/img/a3877893013_9.jpg', + 'https://f4.bcbits.com/img/a1422711571_9.jpg' + ] + }, + { + type: 'tag', + name: 'alternative', + value: 'alternative', + url: 'http://bandcamp.com/tag/alternative', + imageUrls: [ + 'https://f4.bcbits.com/img/a1582129034_9.jpg', + 'https://f4.bcbits.com/img/a2805471381_9.jpg', + 'https://f4.bcbits.com/img/a3614941049_9.jpg', + 'https://f4.bcbits.com/img/a2826938217_9.jpg' + ] + }, + { + type: 'tag', + name: 'americana', + value: 'americana', + url: 'http://bandcamp.com/tag/americana', + imageUrls: [ + 'https://f4.bcbits.com/img/a3877893013_9.jpg', + 'https://f4.bcbits.com/img/a2619353814_9.jpg', + 'https://f4.bcbits.com/img/a2195510910_9.jpg', + 'https://f4.bcbits.com/img/a2817080928_9.jpg' + ] + }, + { + type: 'tag', + name: 'bluegrass', + value: 'bluegrass', + url: 'http://bandcamp.com/tag/bluegrass', + imageUrls: [ + 'https://f4.bcbits.com/img/a3608146003_9.jpg', + 'https://f4.bcbits.com/img/a2142168000_9.jpg', + 'https://f4.bcbits.com/img/a232061954_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/a4046637477_9.jpg', + 'https://f4.bcbits.com/img/a2566562841_9.jpg', + 'https://f4.bcbits.com/img/a2619353814_9.jpg', + 'https://f4.bcbits.com/img/a2273713252_9.jpg' + ] + }, + { + type: 'tag', + name: 'blues rock', + value: 'blues-rock', + url: 'http://bandcamp.com/tag/blues-rock', + imageUrls: [ + 'https://f4.bcbits.com/img/a4046637477_9.jpg', + 'https://f4.bcbits.com/img/a2741817544_9.jpg', + 'https://f4.bcbits.com/img/a1872531270_9.jpg', + 'https://f4.bcbits.com/img/a1077903383_9.jpg' + ] + }, + { + type: 'tag', + name: 'britpop', + value: 'britpop', + url: 'http://bandcamp.com/tag/britpop', + imageUrls: [ + 'https://f4.bcbits.com/img/a2644607321_9.jpg', + 'https://f4.bcbits.com/img/a2246614367_9.jpg', + 'https://f4.bcbits.com/img/a3047591274_9.jpg', + 'https://f4.bcbits.com/img/a1326886180_9.jpg' + ] + }, + { + type: 'tag', + name: 'comedy', + value: 'comedy', + url: 'http://bandcamp.com/tag/comedy', + imageUrls: [ + 'https://f4.bcbits.com/img/a801096041_9.jpg', + 'https://f4.bcbits.com/img/a4252831534_9.jpg', + 'https://f4.bcbits.com/img/a4045115679_9.jpg', + 'https://f4.bcbits.com/img/a1463519266_9.jpg' + ] + }, + { + type: 'tag', + name: 'country', + value: 'country', + url: 'http://bandcamp.com/tag/country', + imageUrls: [ + 'https://f4.bcbits.com/img/a2508874210_9.jpg', + 'https://f4.bcbits.com/img/a753936695_9.jpg', + 'https://f4.bcbits.com/img/a358855715_9.jpg', + 'https://f4.bcbits.com/img/a3877893013_9.jpg' + ] + }, + { + type: 'tag', + name: 'country blues', + value: 'country-blues', + url: 'http://bandcamp.com/tag/country-blues', + imageUrls: [ + 'https://f4.bcbits.com/img/a2162984684_9.jpg', + 'https://f4.bcbits.com/img/a710531889_9.jpg', + 'https://f4.bcbits.com/img/a1810293291_9.jpg', + 'https://f4.bcbits.com/img/a664596119_9.jpg' + ] + }, + { + type: 'tag', + name: 'country folk', + value: 'country-folk', + url: 'http://bandcamp.com/tag/country-folk', + imageUrls: [ + 'https://f4.bcbits.com/img/a4161606062_9.jpg', + 'https://f4.bcbits.com/img/a1957088592_9.jpg', + 'https://f4.bcbits.com/img/a3875199563_9.jpg', + 'https://f4.bcbits.com/img/a3984029276_9.jpg' + ] + }, + { + type: 'tag', + name: 'country rock', + value: 'country-rock', + url: 'http://bandcamp.com/tag/country-rock', + imageUrls: [ + 'https://f4.bcbits.com/img/a3800142330_9.jpg', + 'https://f4.bcbits.com/img/a3877893013_9.jpg', + 'https://f4.bcbits.com/img/a1011049353_9.jpg', + 'https://f4.bcbits.com/img/a2986877684_9.jpg' + ] + }, + { + type: 'tag', + name: 'crust punk', + value: 'crust-punk', + url: 'http://bandcamp.com/tag/crust-punk', + imageUrls: [ + 'https://f4.bcbits.com/img/a3798266242_9.jpg', + 'https://f4.bcbits.com/img/a1480219309_9.jpg', + 'https://f4.bcbits.com/img/a982888230_9.jpg', + 'https://f4.bcbits.com/img/a1106624462_9.jpg' + ] + }, + { + type: 'tag', + name: 'dream pop', + value: 'dream-pop', + url: 'http://bandcamp.com/tag/dream-pop', + imageUrls: [ + 'https://f4.bcbits.com/img/a689770525_9.jpg', + 'https://f4.bcbits.com/img/a2500124018_9.jpg', + 'https://f4.bcbits.com/img/a1960250349_9.jpg', + 'https://f4.bcbits.com/img/a373089653_9.jpg' + ] + }, + { + type: 'tag', + name: 'electric blues', + value: 'electric-blues', + url: 'http://bandcamp.com/tag/electric-blues', + imageUrls: [ + 'https://f4.bcbits.com/img/a217632910_9.jpg', + 'https://f4.bcbits.com/img/a877309670_9.jpg', + 'https://f4.bcbits.com/img/a1971350532_9.jpg', + 'https://f4.bcbits.com/img/a3261738963_9.jpg' + ] + }, + { + type: 'tag', + name: 'electronic', + value: 'electronic', + url: 'http://bandcamp.com/tag/electronic', + imageUrls: [ + 'https://f4.bcbits.com/img/a3169804240_9.jpg', + 'https://f4.bcbits.com/img/a726547643_9.jpg', + 'https://f4.bcbits.com/img/a1105531483_9.jpg', + 'https://f4.bcbits.com/img/a3026412259_9.jpg' + ] + }, + { + type: 'tag', + name: 'emo', + value: 'emo', + url: 'http://bandcamp.com/tag/emo', + imageUrls: [ + 'https://f4.bcbits.com/img/a2051877986_9.jpg', + 'https://f4.bcbits.com/img/a3727718257_9.jpg', + 'https://f4.bcbits.com/img/a1734195178_9.jpg', + 'https://f4.bcbits.com/img/a2206880402_9.jpg' + ] + }, + { + type: 'tag', + name: 'film music', + value: 'film-music', + url: 'http://bandcamp.com/tag/film-music', + imageUrls: [ + 'https://f4.bcbits.com/img/a3023426993_9.jpg', + 'https://f4.bcbits.com/img/a943508013_9.jpg', + 'https://f4.bcbits.com/img/a2017272394_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/a1188487146_9.jpg', + 'https://f4.bcbits.com/img/a3153091166_9.jpg', + 'https://f4.bcbits.com/img/a511097443_9.jpg', + 'https://f4.bcbits.com/img/a3030619517_9.jpg' + ] + }, + { + type: 'tag', + name: 'folk punk', + value: 'folk-punk', + url: 'http://bandcamp.com/tag/folk-punk', + imageUrls: [ + 'https://f4.bcbits.com/img/a290900589_9.jpg', + 'https://f4.bcbits.com/img/a1242157223_9.jpg', + 'https://f4.bcbits.com/img/a248584901_9.jpg', + 'https://f4.bcbits.com/img/a263406906_9.jpg' + ] + } + ], + total: 45, + continuation: { fanId: 8563805, token: 'folk-punk' } +} +Fetching more by continuation... +{ + items: [ + { + type: 'tag', + name: 'grunge', + value: 'grunge', + url: 'http://bandcamp.com/tag/grunge', + imageUrls: [ + 'https://f4.bcbits.com/img/a2800872591_9.jpg', + 'https://f4.bcbits.com/img/a2274713872_9.jpg', + 'https://f4.bcbits.com/img/a2079129748_9.jpg', + 'https://f4.bcbits.com/img/a1746083018_9.jpg' + ] + }, + { + type: 'tag', + name: 'hardcore punk', + value: 'hardcore-punk', + url: 'http://bandcamp.com/tag/hardcore-punk', + imageUrls: [ + 'https://f4.bcbits.com/img/a2827566954_9.jpg', + 'https://f4.bcbits.com/img/a3821032612_9.jpg', + 'https://f4.bcbits.com/img/a991804388_9.jpg', + 'https://f4.bcbits.com/img/a3500882266_9.jpg' + ] + }, + { + type: 'tag', + name: 'hillbilly', + value: 'hillbilly', + url: 'http://bandcamp.com/tag/hillbilly', + imageUrls: [ + 'https://f4.bcbits.com/img/a409590750_9.jpg', + 'https://f4.bcbits.com/img/a2514955679_9.jpg', + 'https://f4.bcbits.com/img/a3284755110_9.jpg', + 'https://f4.bcbits.com/img/a430701006_9.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/a1719883614_9.jpg', + 'https://f4.bcbits.com/img/a1189871621_9.jpg', + 'https://f4.bcbits.com/img/a2814443310_9.jpg', + 'https://f4.bcbits.com/img/a3040101576_9.jpg' + ] + }, + { + type: 'tag', + name: 'honky tonk', + value: 'honky-tonk', + url: 'http://bandcamp.com/tag/honky-tonk', + imageUrls: [ + 'https://f4.bcbits.com/img/a3684412696_9.jpg', + 'https://f4.bcbits.com/img/a3848457107_9.jpg', + 'https://f4.bcbits.com/img/a669083024_9.jpg', + 'https://f4.bcbits.com/img/a2514955679_9.jpg' + ] + }, + { + type: 'tag', + name: 'jangle pop', + value: 'jangle-pop', + url: 'http://bandcamp.com/tag/jangle-pop', + imageUrls: [ + 'https://f4.bcbits.com/img/a2512132838_9.jpg', + 'https://f4.bcbits.com/img/a3364748197_9.jpg', + 'https://f4.bcbits.com/img/a339919856_9.jpg', + 'https://f4.bcbits.com/img/a1412408107_9.jpg' + ] + }, + { + type: 'tag', + name: 'jazz', + value: 'jazz', + url: 'http://bandcamp.com/tag/jazz', + imageUrls: [ + 'https://f4.bcbits.com/img/a238854844_9.jpg', + 'https://f4.bcbits.com/img/a3682544187_9.jpg', + 'https://f4.bcbits.com/img/a393988977_9.jpg', + 'https://f4.bcbits.com/img/a1813903201_9.jpg' + ] + }, + { + type: 'tag', + name: 'latin', + value: 'latin', + url: 'http://bandcamp.com/tag/latin', + imageUrls: [ + 'https://f4.bcbits.com/img/a1305350093_9.jpg', + 'https://f4.bcbits.com/img/a2501872911_9.jpg', + 'https://f4.bcbits.com/img/a3420246211_9.jpg', + 'https://f4.bcbits.com/img/a3749225480_9.jpg' + ] + }, + { + type: 'tag', + name: 'math rock', + value: 'math-rock', + url: 'http://bandcamp.com/tag/math-rock', + imageUrls: [ + 'https://f4.bcbits.com/img/a1636727364_9.jpg', + 'https://f4.bcbits.com/img/a2879242907_9.jpg', + 'https://f4.bcbits.com/img/a906096958_9.jpg', + 'https://f4.bcbits.com/img/a3004707553_9.jpg' + ] + }, + { + type: 'tag', + name: 'no wave', + value: 'no-wave', + url: 'http://bandcamp.com/tag/no-wave', + imageUrls: [ + 'https://f4.bcbits.com/img/a943925525_9.jpg', + 'https://f4.bcbits.com/img/a2645246593_9.jpg', + 'https://f4.bcbits.com/img/a3947743808_9.jpg', + 'https://f4.bcbits.com/img/a4199454128_9.jpg' + ] + }, + { + type: 'tag', + name: 'outlaw', + value: 'outlaw', + url: 'http://bandcamp.com/tag/outlaw', + imageUrls: [ + 'https://f4.bcbits.com/img/a3305307372_9.jpg', + 'https://f4.bcbits.com/img/a4046261081_9.jpg', + 'https://f4.bcbits.com/img/a2066205343_9.jpg', + 'https://f4.bcbits.com/img/a938678320_9.jpg' + ] + }, + { + type: 'tag', + name: 'pop', + value: 'pop', + url: 'http://bandcamp.com/tag/pop', + imageUrls: [ + 'https://f4.bcbits.com/img/a939812546_9.jpg', + 'https://f4.bcbits.com/img/a1933717260_9.jpg', + 'https://f4.bcbits.com/img/a102963930_9.jpg', + 'https://f4.bcbits.com/img/a4133899070_9.jpg' + ] + }, + { + type: 'tag', + name: 'pop punk', + value: 'pop-punk', + url: 'http://bandcamp.com/tag/pop-punk', + imageUrls: [ + 'https://f4.bcbits.com/img/a3024024363_9.jpg', + 'https://f4.bcbits.com/img/a248584901_9.jpg', + 'https://f4.bcbits.com/img/a3064329848_9.jpg', + 'https://f4.bcbits.com/img/a4221322706_9.jpg' + ] + }, + { + type: 'tag', + name: 'punk', + value: 'punk', + url: 'http://bandcamp.com/tag/punk', + imageUrls: [ + 'https://f4.bcbits.com/img/a283018396_9.jpg', + 'https://f4.bcbits.com/img/a943925525_9.jpg', + 'https://f4.bcbits.com/img/a3871705357_9.jpg', + 'https://f4.bcbits.com/img/a2473398667_9.jpg' + ] + }, + { + type: 'tag', + name: 'punk rock', + value: 'punk-rock', + url: 'http://bandcamp.com/tag/punk-rock', + imageUrls: [ + 'https://f4.bcbits.com/img/a943925525_9.jpg', + 'https://f4.bcbits.com/img/a3572652569_9.jpg', + 'https://f4.bcbits.com/img/a1727143980_9.jpg', + 'https://f4.bcbits.com/img/a2473398667_9.jpg' + ] + }, + { + type: 'tag', + name: 'reggaeton', + value: 'reggaeton', + url: 'http://bandcamp.com/tag/reggaeton', + imageUrls: [ + 'https://f4.bcbits.com/img/a3251573540_9.jpg', + 'https://f4.bcbits.com/img/a3374603102_9.jpg', + 'https://f4.bcbits.com/img/a3420246211_9.jpg', + 'https://f4.bcbits.com/img/a4079672677_9.jpg' + ] + }, + { + type: 'tag', + name: 'roots', + value: 'roots', + url: 'http://bandcamp.com/tag/roots', + imageUrls: [ + 'https://f4.bcbits.com/img/a4161606062_9.jpg', + 'https://f4.bcbits.com/img/a3618786284_9.jpg', + 'https://f4.bcbits.com/img/a3169250635_9.jpg', + 'https://f4.bcbits.com/img/a2817080928_9.jpg' + ] + }, + { + type: 'tag', + name: 'salsa', + value: 'salsa', + url: 'http://bandcamp.com/tag/salsa', + imageUrls: [ + 'https://f4.bcbits.com/img/a2171696772_9.jpg', + 'https://f4.bcbits.com/img/a176259398_9.jpg', + 'https://f4.bcbits.com/img/a434253250_9.jpg', + 'https://f4.bcbits.com/img/a1826029679_9.jpg' + ] + }, + { + type: 'tag', + name: 'singer-songwriter', + value: 'singer-songwriter', + url: 'http://bandcamp.com/tag/singer-songwriter', + imageUrls: [ + 'https://f4.bcbits.com/img/a3334101455_9.jpg', + 'https://f4.bcbits.com/img/a683248070_9.jpg', + 'https://f4.bcbits.com/img/a1721922472_9.jpg', + 'https://f4.bcbits.com/img/a263406906_9.jpg' + ] + }, + { + type: 'tag', + name: 'soundtrack', + value: 'soundtrack', + url: 'http://bandcamp.com/tag/soundtrack', + imageUrls: [ + 'https://f4.bcbits.com/img/a2002083581_9.jpg', + 'https://f4.bcbits.com/img/a3327114363_9.jpg', + 'https://f4.bcbits.com/img/a3510234536_9.jpg', + 'https://f4.bcbits.com/img/a1067302165_9.jpg' + ] + } + ], + continuation: { fanId: 8563805, token: 'soundtrack' } +} diff --git a/examples/fan/getInfo.ts b/examples/fan/getInfo.ts new file mode 100644 index 0000000..a9bdd94 --- /dev/null +++ b/examples/fan/getInfo.ts @@ -0,0 +1,13 @@ +import bcfetch from '../../'; +import util from 'util'; + +const username = 'patrickkfkan'; + +const params = { + username, + imageFormat: 'bio_screen' +}; + +bcfetch.fan.getInfo(params).then((results) => { + console.log(util.inspect(results, false, null, false)); +}); diff --git a/examples/getFanInfo_output.txt b/examples/fan/getInfo_output.txt similarity index 70% rename from examples/getFanInfo_output.txt rename to examples/fan/getInfo_output.txt index 5f19191..37b29c1 100644 --- a/examples/getFanInfo_output.txt +++ b/examples/fan/getInfo_output.txt @@ -1,6 +1,6 @@ { type: 'fan', - name: 'patrickkfkan', + name: 'Patrick Kan', username: 'patrickkfkan', url: 'https://bandcamp.com/patrickkfkan', description: 'Hello there!', @@ -8,6 +8,7 @@ websiteUrl: 'http://www.google.com', imageUrl: 'https://f4.bcbits.com/img/29024471_20.jpg', followingGenresCount: 45, - followingArtistsAndLabelsCount: 68, - wishlistItemCount: 24 + followingArtistsAndLabelsCount: 69, + collectionItemCount: 0, + wishlistItemCount: 25 } diff --git a/examples/fan/getWishlist.ts b/examples/fan/getWishlist.ts new file mode 100644 index 0000000..6f49b89 --- /dev/null +++ b/examples/fan/getWishlist.ts @@ -0,0 +1,23 @@ +import bcfetch from '../../'; +import util from 'util'; + +const username = 'patrickkfkan'; + +const params = { + target: username, + imageFormat: 'art_app_large' +}; + +bcfetch.fan.getWishlist(params).then(async (results) => { + console.log(util.inspect(results, false, null, false)); + + if (results.continuation) { + console.log('Fetching more by continuation...'); + + const moreResults = await bcfetch.fan.getWishlist({ + ...params, + target: results.continuation + }); + console.log(util.inspect(moreResults, false, null, false)); + } +}); diff --git a/examples/getFanWishlist_output.txt b/examples/fan/getWishlist_output.txt similarity index 75% rename from examples/getFanWishlist_output.txt rename to examples/fan/getWishlist_output.txt index a3f21fa..4bf5405 100644 --- a/examples/getFanWishlist_output.txt +++ b/examples/fan/getWishlist_output.txt @@ -1,20 +1,51 @@ { items: [ + { + type: 'album', + name: 'FINE', + url: 'https://stellatalpo.bandcamp.com/album/fine', + imageUrl: 'https://f4.bcbits.com/img/a827347154_16.jpg', + artist: { name: 'Stella Talpo', url: 'https://stellatalpo.bandcamp.com' }, + featuredTrack: { + position: 1, + name: 'Water', + artist: 'Stella Talpo', + duration: 184.444, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1241318221&ts=1686408354&t=b35fa3a0b92bfcd4630764fcd6cafae7bec3f7d1' + } + }, + { + type: 'album', + name: 'Smoke Detector', + url: 'https://filthybroke.bandcamp.com/album/smoke-detector', + imageUrl: 'https://f4.bcbits.com/img/a3554952543_16.jpg', + artist: { + name: 'Rich Jones & Iceberg Theory', + url: 'https://filthybroke.bandcamp.com' + }, + featuredTrack: { + position: 1, + name: 'Chilly', + artist: 'Rich Jones & Iceberg Theory', + duration: 117.512, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=4145502542&ts=1686408354&t=69b53096aa16133e25144dc393a5a3ca3fbf6980' + } + }, { type: 'album', name: 'Silver Sash', url: 'https://glitterhouserecords.bandcamp.com/album/silver-sash', imageUrl: 'https://f4.bcbits.com/img/a1068361756_16.jpg', + artist: { + name: 'Wovenhand', + url: 'https://glitterhouserecords.bandcamp.com' + }, 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' + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1665518602&ts=1686408354&t=0924797cadf7c8489570e7bbd640b3b461e4d177' } }, { @@ -22,44 +53,44 @@ name: 'Sylvie (2022)', url: 'https://sylvie-music.bandcamp.com/album/sylvie-2022-2', imageUrl: 'https://f4.bcbits.com/img/a2220580996_16.jpg', + artist: { name: 'Sylvie', url: 'https://sylvie-music.bandcamp.com' }, 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' } + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1840839102&ts=1686408354&t=d917ab4b3b8b6e5a9239aa7c726d384a818b8982' + } }, { 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', + artist: { name: 'Margo Cilker', url: 'https://margocilker.bandcamp.com' }, 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' } + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=2599779818&ts=1686408354&t=661c76ed74cbfc23224f21ea3a4b691994fcdf5c' + } }, { type: 'album', name: 'Chicamacomico', url: 'https://americanaquarium.bandcamp.com/album/chicamacomico', imageUrl: 'https://f4.bcbits.com/img/a3942062080_16.jpg', + artist: { + name: 'American Aquarium', + url: 'https://americanaquarium.bandcamp.com' + }, 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' + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1867095022&ts=1686408354&t=dfed9270f8f603dd1628649c779d44ddae81c54e' } }, { @@ -67,16 +98,16 @@ name: 'Heavy Denim', url: 'https://nickdittmeier.bandcamp.com/album/heavy-denim', imageUrl: 'https://f4.bcbits.com/img/a392062975_16.jpg', + artist: { + name: 'Nick Dittmeier & the Sawdusters', + url: 'https://nickdittmeier.bandcamp.com' + }, 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' + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=291756392&ts=1686408354&t=090fcb2f9e1556afe0676aa9ce95bd32e1387c20' } }, { @@ -84,16 +115,16 @@ name: 'Highway Sounds', url: 'https://kassivalazza.bandcamp.com/album/highway-sounds', imageUrl: 'https://f4.bcbits.com/img/a3803986383_16.jpg', + artist: { + name: 'Kassi Valazza', + url: 'https://kassivalazza.bandcamp.com' + }, 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' + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3662293078&ts=1686408354&t=29f02e4a5f76b479b7a42dc5a47015b37db68dbc' } }, { @@ -101,72 +132,72 @@ 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', + artist: { name: 'Markus K', url: 'https://markus-k.bandcamp.com' }, 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' } + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3271952930&ts=1686408354&t=a83b0b17ccc9eae4589ec42fef810acf77066ea8' + } }, { 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', + artist: { name: 'Robbie Basho', url: 'https://gnomelife.bandcamp.com' }, 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' } + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=2254598059&ts=1686408354&t=1e2600a8a5e62afc7fb6f0f26164ef5370d6453b' + } }, { type: 'album', name: 'Pohorylle', url: 'https://margocilker.bandcamp.com/album/pohorylle', imageUrl: 'https://f4.bcbits.com/img/a880076195_16.jpg', + artist: { name: 'Margo Cilker', url: 'https://margocilker.bandcamp.com' }, 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' } + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=895429532&ts=1686408354&t=bc81c4ab94ddbf6279e470abca77c63ff13d816d' + } }, { type: 'album', name: 'Cruel Country', url: 'https://wilcohq.bandcamp.com/album/cruel-country', imageUrl: 'https://f4.bcbits.com/img/a452127673_16.jpg', + artist: { name: 'Wilco', url: 'https://wilcohq.bandcamp.com' }, 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' } + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3385090483&ts=1686408354&t=a071b8db842eeb6bc149ee5e30dcdc7682c6581d' + } }, { 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', + artist: { + name: 'Jason Isbell and the 400 Unit', + url: 'https://jasonisbell.bandcamp.com' + }, 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' + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=2971595025&ts=1686408354&t=9adaa148e16135b63cdae2e1da440caecc70260a' } }, { @@ -174,16 +205,16 @@ name: 'Wild Creatures', url: 'https://nekocaseofficial.bandcamp.com/album/wild-creatures', imageUrl: 'https://f4.bcbits.com/img/a3745965981_16.jpg', + artist: { + name: 'Neko Case', + url: 'https://nekocaseofficial.bandcamp.com' + }, 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' + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=535802350&ts=1686408354&t=765df653f8052a93cbf445f37318bfedd9414e71' } }, { @@ -191,30 +222,30 @@ 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', + artist: { name: 'Cover Charge', url: 'https://covercharge.bandcamp.com' }, 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' } + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=206849315&ts=1686408354&t=04ca97941427ebf22944929b5affd7e050b6a89d' + } }, { type: 'album', name: 'Anything But Country', url: 'https://legendsofcountry.bandcamp.com/album/anything-but-country', imageUrl: 'https://f4.bcbits.com/img/a1262555298_16.jpg', + artist: { + name: 'Legends Of Country', + url: 'https://legendsofcountry.bandcamp.com' + }, 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' + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=2932389502&ts=1686408354&t=ed23d07a2cba6c3aac94104c20574476f662d85d' } }, { @@ -222,44 +253,44 @@ name: 'Outsiders', url: 'https://annativel.bandcamp.com/album/outsiders', imageUrl: 'https://f4.bcbits.com/img/a420888783_16.jpg', + artist: { name: 'Anna Tivel', url: 'https://annativel.bandcamp.com' }, 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' } + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=157662870&ts=1686408354&t=a7ad8ef316eb2f995e05dc9da7d21d499931707e' + } }, { 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', + artist: { name: 'Buick Audra', url: 'https://buickaudra.bandcamp.com' }, featuredTrack: { - position: 3, - name: 'Afraid of Flying', + position: 2, + name: 'Pocket-Sized Friend', 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' } + duration: 185.178, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1707027958&ts=1686408354&t=fa21317c689d0bdde99f017cb46bfabd11cee825' + } }, { type: 'album', name: 'The New Faith', url: 'https://jakeblountmusic.bandcamp.com/album/the-new-faith', imageUrl: 'https://f4.bcbits.com/img/a1096304773_16.jpg', + artist: { + name: 'Jake Blount', + url: 'https://jakeblountmusic.bandcamp.com' + }, 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' + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=4237361807&ts=1686408354&t=e848a33dd1d5eeb51eeee664e572c0acf2ddebbd' } }, { @@ -267,33 +298,40 @@ name: "Crow's Pine", url: 'https://lobbyartrecs.bandcamp.com/album/crows-pine-2', imageUrl: 'https://f4.bcbits.com/img/a2270982070_16.jpg', + artist: { + name: 'Bob Driftwood', + url: 'https://lobbyartrecs.bandcamp.com' + }, 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' + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=4191030128&ts=1686408354&t=162ce3c30872311d74a506f252071ee4b25af6ae' } - }, + } + ], + total: 25, + continuation: { fanId: 8563805, token: '1655929845:1810326445:a::' } +} +Fetching more by continuation... +{ + items: [ { type: 'album', name: 'Family', url: 'https://thesilosofficial.bandcamp.com/album/family', imageUrl: 'https://f4.bcbits.com/img/a877614212_16.jpg', + artist: { + name: 'The Silos', + url: 'https://thesilosofficial.bandcamp.com' + }, 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' + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=918323024&ts=1686408355&t=f42cea2010cb697fdafc9562cb5487f0ec68b923' } }, { @@ -301,81 +339,55 @@ name: 'Raging in the Dark', url: 'https://jrcarroll.bandcamp.com/album/raging-in-the-dark', imageUrl: 'https://f4.bcbits.com/img/a1293600_16.jpg', + artist: { name: 'J.R. Carroll', url: 'https://jrcarroll.bandcamp.com' }, featuredTrack: { - position: 4, - name: 'Red Fern', + position: 1, + name: 'Other Than That', 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: [ + duration: 258.48, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1155799776&ts=1686408355&t=4ed5ea588b74f48e69beeda504b2d7e6749f87f2' + } + }, { type: 'album', name: 'Live from Boston', url: 'https://davehause.bandcamp.com/album/live-from-boston', - imageUrl: 'https://f4.bcbits.com/img/a2436594456_9.jpg', + imageUrl: 'https://f4.bcbits.com/img/a2436594456_16.jpg', + artist: { name: 'Dave Hause', url: 'https://davehause.bandcamp.com' }, 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' } + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3843751680&ts=1686408355&t=4dca98b2eae973cfc6e91e54e778347f7002d202' + } }, { 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' - }, + imageUrl: 'https://f4.bcbits.com/img/a774650359_16.jpg', 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' } + duration: 176.373, + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=3387079907&ts=1686408355&t=ea7951b45ecb600ac5d94ea4e91258de8923725c' }, { type: 'album', name: 'False Light', url: 'https://whiteward.bandcamp.com/album/false-light', - imageUrl: 'https://f4.bcbits.com/img/a2447867317_9.jpg', + imageUrl: 'https://f4.bcbits.com/img/a2447867317_16.jpg', + artist: { name: 'White Ward', url: 'https://whiteward.bandcamp.com' }, 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' } + streamUrl: 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=2599650567&ts=1686408355&t=eb75c606ab5b1854d801fa50711cad5c49e591a8' + } } ], - continuationToken: null + continuation: null } diff --git a/examples/getAlbumHighlightsByTag.js b/examples/getAlbumHighlightsByTag.js deleted file mode 100644 index 11985e6..0000000 --- a/examples/getAlbumHighlightsByTag.js +++ /dev/null @@ -1,12 +0,0 @@ -const bcfetch = require('../'); -const util = require('util'); - -const tagUrl = 'https://bandcamp.com/tag/noise'; - -const options = { - imageFormat: 'art_app_large', -} - -bcfetch.getAlbumHighlightsByTag(tagUrl, options).then( results => { - console.log(util.inspect(results, false, null, false)); -}); \ No newline at end of file diff --git a/examples/getAlbumHighlightsByTag_output.txt b/examples/getAlbumHighlightsByTag_output.txt deleted file mode 100644 index 267ed99..0000000 --- a/examples/getAlbumHighlightsByTag_output.txt +++ /dev/null @@ -1,565 +0,0 @@ -[ { name: 'new_releases', - title: 'new and notable', - items: - [ { type: 'album', - name: 'The Dignity and Hope Fund Mixtape', - url: 'https://baladnayouth.bandcamp.com/album/the-dignity-and-hope-fund-mixtape', - imageUrl: 'https://f4.bcbits.com/img/a1241178698_16.jpg', - genre: 'alternative', - artist: - { name: 'Various Artists', - url: 'https://baladnayouth.bandcamp.com' }, - featuredTrack: - { name: 'Bout that Life - Son of Nun', - streamUrl: 'https://t4.bcbits.com/stream/8cc61c334511f5e7e418470950ab2c0f/mp3-128/1546927222?p=0&ts=1634706552&t=65a6c31def36677ee0f468729c85250fd055d7a0&token=1634706552_e8b64ca308fe7b0ec830a65e6159330b195c1d4a' } }, - { type: 'album', - name: 'Caprichos', - url: 'https://triptickstapes.bandcamp.com/album/caprichos', - imageUrl: 'https://f4.bcbits.com/img/a803979472_16.jpg', - genre: 'experimental', - artist: - { name: 'Cecilia Lopez / Joe Moffett', - url: 'https://triptickstapes.bandcamp.com' }, - featuredTrack: - { name: 'minor ruination', - streamUrl: 'https://t4.bcbits.com/stream/a1bcd48b158a3f70e6b22734e26c4278/mp3-128/1180541363?p=0&ts=1634706552&t=5bf755a56c605051b85442a6192faa5cbbb58ac7&token=1634706552_388b8f52560b55b57ca4ebcde5de9a99d4464e32' } }, - { type: 'album', - name: 'Disintegration Dubs', - url: 'https://g36vsjkflesh.bandcamp.com/album/disintegration-dubs', - imageUrl: 'https://f4.bcbits.com/img/a902189604_16.jpg', - genre: 'electronic', - artist: - { name: 'G36 Vs JK Flesh', - url: 'https://g36vsjkflesh.bandcamp.com' }, - featuredTrack: - { name: 'G36 - Wrong Place, wrong time', - streamUrl: 'https://t4.bcbits.com/stream/a27d2254be608a743bca96c1a0b68786/mp3-128/192606521?p=0&ts=1634706552&t=489d2b8144bf7ec0c3d4c9e91350c80c688e7706&token=1634706552_30aa15b7df81c0f875716b940d56c9a06900b8e1' } }, - { type: 'album', - name: 'LOTTO - LIVE AT TEATR KANA bootleg', - url: 'https://syfrecords.bandcamp.com/album/lotto-live-at-teatr-kana-bootleg', - imageUrl: 'https://f4.bcbits.com/img/a508553859_16.jpg', - genre: 'punk', - artist: { name: 'Syf Records', url: 'https://syfrecords.bandcamp.com' }, - featuredTrack: - { name: 'VERGE', - streamUrl: 'https://t4.bcbits.com/stream/98469bf406e7af19749073b59acdb206/mp3-128/1150757833?p=0&ts=1634706552&t=3c67bc47c0f7dee154e6bbe96c9998f75be94451&token=1634706552_55aaa44eb8bf159aeae5ea11926d8de1085a8c93' } }, - { type: 'album', - name: 'Chasin\' Dragons ( and occasionally catching one )', - url: 'https://sturmunddrang.bandcamp.com/album/chasin-dragons-and-occasionally-catching-one', - imageUrl: 'https://f4.bcbits.com/img/a2382525231_16.jpg', - genre: 'experimental', - artist: - { name: 'Sturm und Drang GmbH', - url: 'https://sturmunddrang.bandcamp.com' }, - featuredTrack: - { name: 'Krok Drug Make Brain Mush', - streamUrl: 'https://t4.bcbits.com/stream/426173ae416b70e4fcda1e2fae7ca1fc/mp3-128/4252042569?p=0&ts=1634706552&t=6a58f2805708f4424995f45afef68bc44762fc57&token=1634706552_5bcb2a7ff6fcf3d7da64490fef74407dbd5eb7e9' } }, - { type: 'album', - name: 'Selected Sound Works (1981-2021)', - url: 'https://pentiments.bandcamp.com/album/selected-sound-works-1981-2021', - imageUrl: 'https://f4.bcbits.com/img/a1583667348_16.jpg', - genre: 'experimental', - artist: - { name: 'Joseph Nechvatal', - url: 'https://pentiments.bandcamp.com' }, - featuredTrack: - { name: 'Crown of Thorns (1981)', - streamUrl: 'https://t4.bcbits.com/stream/1d97fb7a782fb4924c54fa3b4519f2bb/mp3-128/3184728653?p=0&ts=1634706552&t=e2a60cb862bbd58c5cc07c1c2282bbdb8bf1dce5&token=1634706552_562081aaa712a49805857c2a286ed6f68b965116' } }, - { type: 'album', - name: 'Old Air', - url: 'https://ujbala.bandcamp.com/album/old-air', - imageUrl: 'https://f4.bcbits.com/img/a792740166_16.jpg', - genre: 'experimental', - artist: { name: 'Új Bála', url: 'https://ujbala.bandcamp.com' }, - featuredTrack: - { name: 'Old Air', - streamUrl: 'https://t4.bcbits.com/stream/93c07e4f54c0912d7f857df8731a9953/mp3-128/2122444224?p=0&ts=1634706552&t=e6d402430275c9ff0bd94eaa694edb5da00bf60f&token=1634706552_ea2032ca2392714df1a5b648cc17629c4cfd3d16' } }, - { type: 'album', - name: 'Quim De La Quim', - url: 'https://industrialcoast.bandcamp.com/album/quim-de-la-quim', - imageUrl: 'https://f4.bcbits.com/img/a2166570436_16.jpg', - genre: 'experimental', - artist: - { name: 'Smell & Quim', - url: 'https://industrialcoast.bandcamp.com' }, - featuredTrack: - { name: 'F*ck Factory', - streamUrl: 'https://t4.bcbits.com/stream/df052f945e69426fa615e0022f59c947/mp3-128/1988107883?p=0&ts=1634706552&t=c9ac26e8f459fcf0277f54d12d31b10865514a55&token=1634706552_4e22c0ef1ae1151fa18352cfb9e395ef97b5e873' } }, - { type: 'album', - name: 'The Great Divide', - url: 'https://malignantrecs.bandcamp.com/album/the-great-divide', - imageUrl: 'https://f4.bcbits.com/img/a172539436_16.jpg', - genre: 'ambient', - artist: { name: 'Control', url: 'https://malignantrecs.bandcamp.com' }, - featuredTrack: - { name: 'For the Ashes', - streamUrl: 'https://t4.bcbits.com/stream/0b7d60748b52c8e55f05910aefc451ea/mp3-128/1202755517?p=0&ts=1634706552&t=847696e2af148347822ac28d297cb73e2286d0c9&token=1634706552_d8ddadccb09af1600a1b72e197328cf8a2dc7c9a' } }, - { type: 'album', - name: 'Thundering', - url: 'https://tribetapes.bandcamp.com/album/thundering', - imageUrl: 'https://f4.bcbits.com/img/a3804966726_16.jpg', - genre: 'experimental', - artist: - { name: 'Breathe Heavy', - url: 'https://tribetapes.bandcamp.com' }, - featuredTrack: - { name: 'Sometimes You Sit And Think', - streamUrl: 'https://t4.bcbits.com/stream/46fe96c3575455df2f909277edddbf4a/mp3-128/1382856592?p=0&ts=1634706552&t=888a1a731cdc5a7902c642155310c5f2c0bbb9d7&token=1634706552_d93157faf34d1f81a1fb6bc6d18244d5230050fa' } }, - { type: 'album', - name: 'Ancient Smoke Ascending', - url: 'https://ul-th-nekrolatria.bandcamp.com/album/ancient-smoke-ascending', - imageUrl: 'https://f4.bcbits.com/img/a3804869585_16.jpg', - genre: 'experimental', - artist: - { name: 'UL.TH.Nekrolatria', - url: 'https://ul-th-nekrolatria.bandcamp.com' }, - featuredTrack: - { name: 'Ancient Smoke Ascending', - streamUrl: 'https://t4.bcbits.com/stream/5a6dc252a864b9b1f6cfce263ff10be5/mp3-128/117658014?p=0&ts=1634706552&t=89a77a17e2484018b1071f99b4d5991625f35e4f&token=1634706552_78427fbbfe3c25620d289acce6b1184e465b62a2' } }, - { type: 'album', - name: 'Problem Child', - url: 'https://tribetapes.bandcamp.com/album/problem-child', - imageUrl: 'https://f4.bcbits.com/img/a3797430080_16.jpg', - genre: 'experimental', - artist: { name: 'Genophobia', url: 'https://tribetapes.bandcamp.com' }, - featuredTrack: - { name: 'Side A', - streamUrl: 'https://t4.bcbits.com/stream/8d9a85254ed8db3078c465c48360662f/mp3-128/2958205348?p=0&ts=1634706552&t=e775ec025371560a6b899e9bcc95884ecea8b970&token=1634706552_fab7d3255fd3c4c3bf086445e20393b60ce942b6' } }, - { type: 'album', - name: 'The Future Disintegrates', - url: 'https://thejewelgarden.bandcamp.com/album/the-future-disintegrates', - imageUrl: 'https://f4.bcbits.com/img/a714831043_16.jpg', - genre: 'experimental', - artist: - { name: 'Various Artists', - url: 'https://thejewelgarden.bandcamp.com' }, - featuredTrack: - { name: 'Passerine Tools', - streamUrl: 'https://t4.bcbits.com/stream/749b8677ce0d3ecc23a64a0496389d61/mp3-128/144421477?p=0&ts=1634706552&t=52960a120cc12a69b4cdb47a97b1364f9b667361&token=1634706552_7339b02b018c5fd9b2694c6d286cfda68a643384' } }, - { type: 'album', - name: 'Atonal Vol 2 - Encyclopedia of Obscure Aural Wonders - 80´s/90´s', - url: 'https://sphmusic.bandcamp.com/album/atonal-vol-2-encyclopedia-of-obscure-aural-wonders-80-s-90-s', - imageUrl: 'https://f4.bcbits.com/img/a944379528_16.jpg', - genre: 'experimental', - artist: - { name: 'Ah Cama Sotz, Das Synthetische Mischgewebe, PBK,Emil Beaulieau,Randy Greif, among others', - url: 'https://sphmusic.bandcamp.com' }, - featuredTrack: - { name: 'Mr. Ebu - Your Black Burnin\' Diamond Eyes', - streamUrl: 'https://t4.bcbits.com/stream/cbf60b0fb4e29ee4866741735b37a959/mp3-128/2048886490?p=0&ts=1634706552&t=c84efc8e3d1e0f9a2da0950df5abc9607f5446fc&token=1634706552_28ac6031944a50b6b34be7a70e35f3855fc17af1' } }, - { type: 'album', - name: 'Mindscape From The 7th Level', - url: 'https://grahamdunning.bandcamp.com/album/mindscape-from-the-7th-level', - imageUrl: 'https://f4.bcbits.com/img/a1802456321_16.jpg', - genre: 'experimental', - artist: - { name: 'Graham Dunning', - url: 'https://grahamdunning.bandcamp.com' }, - featuredTrack: - { name: 'Sensors Indicate That There Is A Lifeform Present', - streamUrl: 'https://t4.bcbits.com/stream/3ea2363316e902eb3f994f725b9f87d4/mp3-128/61289408?p=0&ts=1634706552&t=dc977361633e92e51602329fe1fbfbccb397b14c&token=1634706552_d4ca8448f91dfe7beae561ef006a2b6e7ec269ea' } } ] }, - { name: 'trending_vinyl', - title: 'featured noise vinyl', - items: - [ { type: 'album', - name: 'Old Air', - url: 'https://ujbala.bandcamp.com/album/old-air', - imageUrl: 'https://f4.bcbits.com/img/a792740166_16.jpg', - genre: 'experimental', - artist: { name: 'Új Bála', url: 'https://ujbala.bandcamp.com' }, - featuredTrack: - { name: 'Old Air', - streamUrl: 'https://t4.bcbits.com/stream/93c07e4f54c0912d7f857df8731a9953/mp3-128/2122444224?p=0&ts=1634706552&t=e6d402430275c9ff0bd94eaa694edb5da00bf60f&token=1634706552_ea2032ca2392714df1a5b648cc17629c4cfd3d16' } }, - { type: 'album', - name: 'Dua Naga', - url: 'https://rinuwat.bandcamp.com/album/dua-naga-2', - imageUrl: 'https://f4.bcbits.com/img/a2612993230_16.jpg', - genre: 'metal', - artist: { name: 'RINUWAT', url: 'https://rinuwat.bandcamp.com' }, - featuredTrack: - { name: 'Taring Emas', - streamUrl: 'https://t4.bcbits.com/stream/50c80a9175a8d0c5a38f3fe5cd745a76/mp3-128/3944126948?p=0&ts=1634706552&t=f2d9fb9d6435e02ff3018ae18825ec5b7065bdf9&token=1634706552_6d395eda972a148c14ea6cc49d1158155f4b0a49' } }, - { type: 'album', - name: 'NCM003: NOOD5', - url: 'https://noodsradio.bandcamp.com/album/ncm003-nood5', - imageUrl: 'https://f4.bcbits.com/img/a100417523_16.jpg', - genre: 'experimental', - artist: { name: 'V/A', url: 'https://noodsradio.bandcamp.com' }, - featuredTrack: - { name: 'Cleyra - Twined', - streamUrl: 'https://t4.bcbits.com/stream/82bc75bb3ff9b9c12f33caf383f0a80f/mp3-128/313494957?p=0&ts=1634706552&t=cf83a7fb125c640f4c6b820dda64bd80e722d5e6&token=1634706552_3c53d3d6bcf6becfa4677cbb37cd352ca8bcfcb1' } }, - { type: 'album', - name: 'Despair In The Gutter II', - url: 'https://blackringrituals.com/album/despair-in-the-gutter-ii', - imageUrl: 'https://f4.bcbits.com/img/a977487754_16.jpg', - genre: 'experimental', - artist: { name: 'Death Glaze', url: 'https://blackringrituals.com' }, - featuredTrack: - { name: 'Despair In The Gutter', - streamUrl: 'https://t4.bcbits.com/stream/dd989c5afd811bb55f1b305c254bcc64/mp3-128/3097593658?p=0&ts=1634706552&t=f9e5190c8b736c3f0f8ea055fac9dd2192ed1bc3&token=1634706552_2a8af370bd8ca249bd5abb7b8cf36c26185b2c98' } }, - { type: 'album', - name: 'Time War Time', - url: 'https://blackringrituals.com/album/time-war-time', - imageUrl: 'https://f4.bcbits.com/img/a3335582121_16.jpg', - genre: 'experimental', - artist: { name: 'Willowbrook', url: 'https://blackringrituals.com' }, - featuredTrack: - { name: 'Time War Time', - streamUrl: 'https://t4.bcbits.com/stream/aa12a4e1ea630524f117572ced4fca12/mp3-128/107505565?p=0&ts=1634706552&t=2dc8c56878799936008eafe7681b212ba67e31ba&token=1634706552_5d67c5725e8c5ab74bc778ebdef9f989c9142891' } }, - { type: 'album', - name: 'Inherited', - url: 'https://blackringrituals.com/album/inherited', - imageUrl: 'https://f4.bcbits.com/img/a3608886756_16.jpg', - genre: 'experimental', - artist: { name: 'Maltreatment', url: 'https://blackringrituals.com' }, - featuredTrack: - { name: 'Love And Nurturing', - streamUrl: 'https://t4.bcbits.com/stream/8c314610881f8574fc218324085a3721/mp3-128/3399526914?p=0&ts=1634706552&t=1137fbe07f43303976fc31ed7287fc3926a12fe3&token=1634706552_bd7d46af55d6c4e219e5bfe469251c2d5022729b' } }, - { type: 'album', - name: 'Ghosts Of Decay', - url: 'https://blackringrituals.com/album/ghosts-of-decay', - imageUrl: 'https://f4.bcbits.com/img/a1593926277_16.jpg', - genre: 'experimental', - artist: - { name: 'Tissa Mawartyassari', - url: 'https://blackringrituals.com' }, - featuredTrack: - { name: 'Ghosts Of Decay', - streamUrl: 'https://t4.bcbits.com/stream/4a978217ab28a6e6d13bc658301b4d44/mp3-128/549894865?p=0&ts=1634706552&t=2381f83c4ad09f6a042b86e139fc9e9a3c819e06&token=1634706552_7214eaf231bcfd9df9ece22955f8abee649993e3' } }, - { type: 'album', - name: 'Entomophagy', - url: 'https://blackringrituals.com/album/entomophagy', - imageUrl: 'https://f4.bcbits.com/img/a3453181460_16.jpg', - genre: 'experimental', - artist: { name: 'Nursing Death', url: 'https://blackringrituals.com' }, - featuredTrack: - { name: 'Entomophagy', - streamUrl: 'https://t4.bcbits.com/stream/adc40b25156d5c0220930d67e6a96fa9/mp3-128/4126755864?p=0&ts=1634706552&t=ee8dc3d1dc98a6ddd4ce41f7fe8727afca1fce4d&token=1634706552_a46085f84c20c5b3ba46156c18ce61105d8b5126' } }, - { type: 'album', - name: 'Une Saison En Enfer', - url: 'https://blackringrituals.com/album/une-saison-en-enfer', - imageUrl: 'https://f4.bcbits.com/img/a669191833_16.jpg', - genre: 'experimental', - artist: { name: 'J.S.H.', url: 'https://blackringrituals.com' }, - featuredTrack: - { name: 'I Have Withered Within Me All Human Hope', - streamUrl: 'https://t4.bcbits.com/stream/4673a3d6b4a7b856fc3a38177fac474c/mp3-128/465229502?p=0&ts=1634706552&t=4cf17743903f51dbb63d1fc1134c7003faea1110&token=1634706552_c33b06b60596949cfc54dfb67b6b7266edd58f5c' } }, - { type: 'album', - name: 'the unreal that shivers in each...', - url: 'https://buriedtreasure.bandcamp.com/album/the-unreal-that-shivers-in-each', - imageUrl: 'https://f4.bcbits.com/img/a3968852872_16.jpg', - genre: 'electronic', - artist: - { name: 'PHILIPPE PETIT', - url: 'https://buriedtreasure.bandcamp.com' }, - featuredTrack: - { name: 'l\'irreel qui frissonne...', - streamUrl: 'https://t4.bcbits.com/stream/9cf71a114313856ec75e27bb9b9ea1c0/mp3-128/2629178334?p=0&ts=1634706552&t=53ca08c9454dfb38728cf3abe07ae944761a56bd&token=1634706552_3dcbe70598331256470e2a547bb98f3ec8f1e05e' } }, - { type: 'album', - name: 'dreaming of a dead girl', - url: 'https://himukalt.bandcamp.com/album/dreaming-of-a-dead-girl', - imageUrl: 'https://f4.bcbits.com/img/a3053761842_16.jpg', - genre: 'experimental', - artist: { name: 'himukalt', url: 'https://himukalt.bandcamp.com' }, - featuredTrack: - { name: 'hysterical', - streamUrl: 'https://t4.bcbits.com/stream/7f72bb642b09253c436b564e42982dc6/mp3-128/847469528?p=0&ts=1634706552&t=4992c5e7589e659a06f310c1f0786d912fa6cecf&token=1634706552_6d02f25f6cf7824e978ceeb5c1af8a506307cb0e' } }, - { type: 'album', - name: 'Not There / Here', - url: 'https://distantbombs.bandcamp.com/album/not-there-here', - imageUrl: 'https://f4.bcbits.com/img/a2882936540_16.jpg', - genre: 'experimental', - artist: - { name: 'The Escalation', - url: 'https://distantbombs.bandcamp.com' }, - featuredTrack: - { name: 'Not There', - streamUrl: 'https://t4.bcbits.com/stream/b72bb4771cbd023c943917109336067f/mp3-128/2845800418?p=0&ts=1634706552&t=56e70d7e215ff22b8aa908cb64aa963ac39a964b&token=1634706552_73c8676e23ab00d5385305ed56149ad3b59de0c8' } }, - { type: 'album', - name: 'Acoustic Shadows (RSD 2021 Repress)', - url: 'https://leabertucci.bandcamp.com/album/acoustic-shadows-rsd-2021-repress', - imageUrl: 'https://f4.bcbits.com/img/a2724059433_16.jpg', - genre: 'experimental', - artist: - { name: 'Lea Bertucci', - url: 'https://leabertucci.bandcamp.com' }, - featuredTrack: - { name: 'Brass (II)', - streamUrl: 'https://t4.bcbits.com/stream/672cb65883c645fd4eb79f1d4d3a134a/mp3-128/642341575?p=0&ts=1634706552&t=22f024e2883565c3b26b3c4651f46f6dd091d852&token=1634706552_679edf889cf8cd6f388f65d36b03f90c512d932e' } }, - { type: 'album', - name: 'VS028 The Plain Truth (1983)', - url: 'https://verlagsystem.bandcamp.com/album/vs028-the-plain-truth-1983', - imageUrl: 'https://f4.bcbits.com/img/a1020505479_16.jpg', - genre: 'experimental', - artist: - { name: 'Maurizio Bianchi', - url: 'https://verlagsystem.bandcamp.com' }, - featuredTrack: - { name: 'The Plain Truth', - streamUrl: 'https://t4.bcbits.com/stream/0e478aaacd7762a9b002429b00161274/mp3-128/2768578286?p=0&ts=1634706552&t=5b7e3a07dfde8817a6feb9613f7bf8dc58080d9e&token=1634706552_3bbc7126719f8030a9b14fc7c23137f10895f7f5' } }, - { type: 'album', - name: 'Humanize / Dehumanzie', - url: 'https://onefivesix.bandcamp.com/album/humanize-dehumanzie', - imageUrl: 'https://f4.bcbits.com/img/a2086137653_16.jpg', - genre: 'experimental', - artist: { name: '156', url: 'https://onefivesix.bandcamp.com' }, - featuredTrack: - { name: 'Humanize (5" version)', - streamUrl: 'https://t4.bcbits.com/stream/1ce81afa03c6e517c8bfeb3a13068e13/mp3-128/2925391041?p=0&ts=1634706552&t=6d987d6fe1e415f68de0dcc0215b6d432802ad40&token=1634706552_089d68eaf73209c6693f712ba6175a1198fe65f0' } } ] }, - { name: 'top_sellers', - title: 'all-time best selling noise', - items: - [ { type: 'album', - name: 'Peaceful as Hell', - url: 'https://blackdresses.bandcamp.com/album/peaceful-as-hell', - imageUrl: 'https://f4.bcbits.com/img/a3575771155_16.jpg', - genre: 'electronic', - artist: - { name: 'Black Dresses', - url: 'https://blackdresses.bandcamp.com' }, - featuredTrack: - { name: 'BEAUTIFUL FRIENDSHIP', - streamUrl: 'https://t4.bcbits.com/stream/8d5064f6c97778093c531428ca3efa06/mp3-128/1285558574?p=0&ts=1634706552&t=930fc9e4a92ce4891098613ff03a2e574ff42dff&token=1634706552_d97f521f8bbed70414f7f9d4eee0549e9beee386' } }, - { type: 'album', - name: 'Sounds Of France | France Sound Effects Library', - url: 'https://freetousesounds.bandcamp.com/album/sounds-of-france-france-sound-effects-library', - imageUrl: 'https://f4.bcbits.com/img/a2198695097_16.jpg', - genre: 'ambient', - artist: - { name: 'freetousesounds', - url: 'https://freetousesounds.bandcamp.com' }, - featuredTrack: - { name: 'Sound Compilation Sounds Of France', - streamUrl: 'https://t4.bcbits.com/stream/c6ea66c07c69bff1b0aa0a8b92439ec4/mp3-128/2634037464?p=0&ts=1634706552&t=8cf535f277bbbaea5c106c85334da18d317ab262&token=1634706552_ee3403f91e26010927b660fd82cc6289f09e238f' } }, - { type: 'album', - name: 'Fire', - url: 'https://thebugmusic.bandcamp.com/album/fire-2', - imageUrl: 'https://f4.bcbits.com/img/a1432344310_16.jpg', - genre: 'experimental', - artist: { name: 'The Bug', url: 'https://thebugmusic.bandcamp.com' }, - featuredTrack: - { name: 'Vexed (feat. Moor Mother)', - streamUrl: 'https://t4.bcbits.com/stream/e2bee7bc56e98858e824b8c4f1e40ab3/mp3-128/247739879?p=0&ts=1634706552&t=64bb0e769a42f12314f239606b0d8a8ccff0b71c&token=1634706552_f0bcc8909ae008c5cf77ddf841a1eeb8bb6a4a20' } }, - { type: 'album', - name: '1995', - url: 'https://music.businesscasual.biz/album/1995', - imageUrl: 'https://f4.bcbits.com/img/a1681009058_16.jpg', - genre: 'electronic', - artist: { name: '☒', url: 'https://music.businesscasual.biz' }, - featuredTrack: - { name: 'Moon Way (feat. Chemical Hypnotist)', - streamUrl: 'https://t4.bcbits.com/stream/f796fd140e5e99346511e207225800f3/mp3-128/2165960685?p=0&ts=1634706552&t=c58217198e5280d83c8a04681469c07d01230895&token=1634706552_1ed26c0020837f03d07c0fd812c8c16da6121515' } }, - { type: 'album', - name: 'Black Encyclopedia of the Air', - url: 'https://moormother.bandcamp.com/album/black-encyclopedia-of-the-air', - imageUrl: 'https://f4.bcbits.com/img/a441373844_16.jpg', - genre: 'experimental', - artist: { name: 'Moor Mother', url: 'https://moormother.bandcamp.com' }, - featuredTrack: - { name: 'Temporal Control Of Light Echoes', - streamUrl: 'https://t4.bcbits.com/stream/19b3d2999c590a9ba246311ef2f4fc15/mp3-128/2484282357?p=0&ts=1634706552&t=d4af3f1bdb5d36a3dcef1c6a5cfb921ca74166b7&token=1634706552_bed6e46cdbd7725e51dfb0b68a4e00bad04cce40' } }, - { type: 'album', - name: 'Losing Grip on Reality', - url: 'https://geometriclullaby.bandcamp.com/album/losing-grip-on-reality', - imageUrl: 'https://f4.bcbits.com/img/a1041180441_16.jpg', - genre: 'electronic', - artist: - { name: 'Fiebertraum', - url: 'https://geometriclullaby.bandcamp.com' }, - featuredTrack: - { name: 'Dancing in the Crypt of Sorrow', - streamUrl: 'https://t4.bcbits.com/stream/49264f911c3f9b2841b2d852e820a053/mp3-128/4271360547?p=0&ts=1634706552&t=914825459a4ffce1662192ec0739d5dff75a13bc&token=1634706552_3515b3e9bbf12665109406f9518c0dc3f1c11edf' } }, - { type: 'album', - name: 'AGNUS DEI', - url: 'https://linguaignota.bandcamp.com/album/agnus-dei', - imageUrl: 'https://f4.bcbits.com/img/a4233092693_16.jpg', - genre: 'experimental', - artist: - { name: 'LINGUA IGNOTA', - url: 'https://linguaignota.bandcamp.com' }, - featuredTrack: - { name: 'SEXLESS // NO SEX (IRON LUNG)', - streamUrl: 'https://t4.bcbits.com/stream/8e6b0f4f358151eaa2c4cb81f5e68c8b/mp3-128/836195888?p=0&ts=1634706552&t=c1d1a91bfc8304323805df09ace07d8335877ae7&token=1634706552_671adcb2bb499e1fc1693ed75f5acd68d5eeaa89' } }, - { type: 'album', - name: 'Physically Sick 3', - url: 'https://physicallysick3.bandcamp.com/album/physically-sick-3', - imageUrl: 'https://f4.bcbits.com/img/a3764976661_16.jpg', - genre: 'electronic', - artist: - { name: 'Physically Sick 3', - url: 'https://physicallysick3.bandcamp.com' }, - featuredTrack: - { name: 'Body + Mind', - streamUrl: 'https://t4.bcbits.com/stream/a0b0d150e95c01e3abab2f67337dd9d0/mp3-128/2387878535?p=0&ts=1634706552&t=296f8f01c30edb6cd3477cae4d7cd9d7d4b28ec4&token=1634706552_bb061e79c0986fbc647a3d41dafe998b75cb9be8' } }, - { type: 'album', - name: 'artless', - url: 'https://ifidieinmississippi.bandcamp.com/album/artless-2', - imageUrl: 'https://f4.bcbits.com/img/a3831177236_16.jpg', - genre: 'acoustic', - artist: - { name: 'if i die in mississippi', - url: 'https://ifidieinmississippi.bandcamp.com' }, - featuredTrack: - { name: 'this could be us but u playin\'', - streamUrl: 'https://t4.bcbits.com/stream/ef39ec6bbd8da6d9a875825d34614ea4/mp3-128/4056986611?p=0&ts=1634706552&t=c23b713f664be370cbd19413c5532e424d5bda1b&token=1634706552_b4b6a5289d3842382c79b648610968eca2de4ae9' } }, - { type: 'album', - name: '흰색 죽음', - url: 'https://father.2006.kr/album/-', - imageUrl: 'https://f4.bcbits.com/img/a3416030663_16.jpg', - genre: 'electronic', - artist: { name: '아버지', url: 'https://father.2006.kr' }, - featuredTrack: - { name: '감기', - streamUrl: 'https://t4.bcbits.com/stream/9bda8bfeec355c5ae146e74c7f4ff55a/mp3-128/3033712644?p=0&ts=1634706552&t=792621d85f3cf7ace1cd4c4d642403f2ede239be&token=1634706552_c55779e4a1224527c5ac25151f53bcd5543535a1' } }, - { type: 'album', - name: 'Careful', - url: 'https://boyharsher.bandcamp.com/album/careful', - imageUrl: 'https://f4.bcbits.com/img/a1131250781_16.jpg', - genre: 'electronic', - artist: { name: 'Boy Harsher', url: 'https://boyharsher.bandcamp.com' }, - featuredTrack: - { name: 'Fate', - streamUrl: 'https://t4.bcbits.com/stream/a3333d562264f54384205d89fb4d4dc0/mp3-128/1162083825?p=0&ts=1634706552&t=ae31f9c0cb04d29a73d39c0cec6853cda57944a0&token=1634706552_ba75ee806a8cd39d8158466dacfa949287dbc90b' } }, - { type: 'album', - name: 'Negative Infinity', - url: 'https://theflyingluttenbachers.bandcamp.com/album/negative-infinity', - imageUrl: 'https://f4.bcbits.com/img/a3022512134_16.jpg', - genre: 'experimental', - artist: - { name: 'The Flying Luttenbachers', - url: 'https://theflyingluttenbachers.bandcamp.com' }, - featuredTrack: - { name: 'Fury Of The Delusion', - streamUrl: 'https://t4.bcbits.com/stream/4702aa2da8eb820997b7d1871554009a/mp3-128/96095058?p=0&ts=1634706552&t=8ffeace3640d1c66251748ba3431913a92a48a2f&token=1634706552_aceffb63f5c8b830ad347ab867f42ab17fb8f547' } }, - { type: 'album', - name: 'Oxidized', - url: 'https://frontierer.bandcamp.com/album/oxidized', - imageUrl: 'https://f4.bcbits.com/img/a3907809601_16.jpg', - genre: 'metal', - artist: { name: 'Frontierer', url: 'https://frontierer.bandcamp.com' }, - featuredTrack: - { name: 'Glacial Plasma', - streamUrl: 'https://t4.bcbits.com/stream/a252c8e84aca1ec422b074119413774b/mp3-128/1703104182?p=0&ts=1634706552&t=8baff94d1e9c72e70716107169dfd6ea5cbd5a34&token=1634706552_eef1bf14177652f8ce299ef0f9c2ab36466d612c' } }, - { type: 'album', - name: 'Titanic Rising', - url: 'https://weyesblood.bandcamp.com/album/titanic-rising', - imageUrl: 'https://f4.bcbits.com/img/a1673180911_16.jpg', - genre: 'alternative', - artist: { name: 'Weyes Blood', url: 'https://weyesblood.bandcamp.com' }, - featuredTrack: - { name: 'Movies', - streamUrl: 'https://t4.bcbits.com/stream/3ae2ba489833cad68c403a645df65ebd/mp3-128/2267082642?p=0&ts=1634706552&t=fb2190207b419ed89a1a023ad87cf24b8f7615e0&token=1634706552_95da8c77bb0fad7837c58144f914898b1c2e8f5b' } }, - { type: 'album', - name: 'Content That Makes You Feel Good', - url: 'https://stuckchi.bandcamp.com/album/content-that-makes-you-feel-good', - imageUrl: 'https://f4.bcbits.com/img/a1186573877_16.jpg', - genre: 'punk', - artist: { name: 'Stuck', url: 'https://stuckchi.bandcamp.com' }, - featuredTrack: - { name: 'City of Police', - streamUrl: 'https://t4.bcbits.com/stream/4424e9d97287f6fd02105eaa05d86dd4/mp3-128/2444619088?p=0&ts=1634706552&t=60c2b05b40415fbb9d31360884ae2f7d4bd834df&token=1634706552_d0fddebd2956d5aa1120572b3e0ab6c5a33c8778' } }, - { type: 'album', - name: 'Avow', - url: 'https://profoundlorerecords.bandcamp.com/album/avow', - imageUrl: 'https://f4.bcbits.com/img/a34779670_16.jpg', - genre: '', - artist: - { name: 'PORTAL', - url: 'https://profoundlorerecords.bandcamp.com' }, - featuredTrack: - { name: 'Catafalque', - streamUrl: 'https://t4.bcbits.com/stream/73f2bd3ce7ef5dd1f70693ec6ee445ef/mp3-128/3651912696?p=0&ts=1634706552&t=52394af7f6bfd531f2c8822d005082d0c6161dea&token=1634706552_d1e40efdb9a4fc5634621f0634dc3e9d498e2f8a' } } ] }, - { name: 'fan_reviews', - title: 'recommendations from fans', - items: - [ { type: 'album', - name: 'Molten Vasts', - url: 'https://malignantrecs.bandcamp.com/album/molten-vasts', - imageUrl: 'https://f4.bcbits.com/img/a3752393217_16.jpg', - genre: 'ambient', - artist: - { name: 'Mass Ejection', - url: 'https://malignantrecs.bandcamp.com' }, - featuredTrack: - { name: 'Torrid Core', - streamUrl: 'https://t4.bcbits.com/stream/b9f5fa0d84f3bf417e7c73cf53944040/mp3-128/928356178?p=0&ts=1634706552&t=b0a805841d7b1f86eede363c274c0d415701ca5d&token=1634706552_285bbc6768e162793578010856c75e5e150d630b' } }, - { type: 'album', - name: 'K I L L S H O T', - url: 'https://astro-pup.bandcamp.com/album/k-i-l-l-s-h-o-t', - imageUrl: 'https://f4.bcbits.com/img/a3936983097_16.jpg', - genre: 'electronic', - artist: { name: 'ASTRO☆PUP', url: 'https://astro-pup.bandcamp.com' }, - featuredTrack: - { name: 'YEAH R!GHT', - streamUrl: 'https://t4.bcbits.com/stream/df88fcce75c4bc71d340f2da61873322/mp3-128/3000689716?p=0&ts=1634706552&t=a6cbd510dd27625030834ca4df17678c3a78eaf8&token=1634706552_92a668d441f4022ceef9f66c225fea4e8ced46aa' } }, - { type: 'album', - name: 'Disintegration Dubs', - url: 'https://g36vsjkflesh.bandcamp.com/album/disintegration-dubs', - imageUrl: 'https://f4.bcbits.com/img/a902189604_16.jpg', - genre: 'electronic', - artist: - { name: 'G36 Vs JK Flesh', - url: 'https://g36vsjkflesh.bandcamp.com' }, - featuredTrack: - { name: 'G36 - Wrong Place, wrong time', - streamUrl: 'https://t4.bcbits.com/stream/a27d2254be608a743bca96c1a0b68786/mp3-128/192606521?p=0&ts=1634706552&t=489d2b8144bf7ec0c3d4c9e91350c80c688e7706&token=1634706552_30aa15b7df81c0f875716b940d56c9a06900b8e1' } }, - { type: 'album', - name: 'Guitars On Drugs - "Spies and Dub"', - url: 'https://forbiddenplacerecords.bandcamp.com/album/guitars-on-drugs-spies-and-dub', - imageUrl: 'https://f4.bcbits.com/img/a2940633228_16.jpg', - genre: 'rock', - artist: - { name: 'Forbidden Place Records', - url: 'https://forbiddenplacerecords.bandcamp.com' }, - featuredTrack: - { name: 'Theme', - streamUrl: 'https://t4.bcbits.com/stream/61e058d807aa4eb561789047c1cd6eab/mp3-128/3094516110?p=0&ts=1634706552&t=7978e331041b7ad875724c8851cbe2d738eb6864&token=1634706552_2cdd134e846a47ccd1d0162e33fc3e1518fd0582' } }, - { type: 'album', - name: 'violent for being sexually desired', - url: 'https://himukalt.bandcamp.com/album/violent-for-being-sexually-desired', - imageUrl: 'https://f4.bcbits.com/img/a2984309076_16.jpg', - genre: 'experimental', - artist: { name: 'scopophilia', url: 'https://himukalt.bandcamp.com' }, - featuredTrack: - { name: 'i would die for you', - streamUrl: 'https://t4.bcbits.com/stream/0ab1f33a89b67dc3b57519b5e91ec993/mp3-128/3335138429?p=0&ts=1634706552&t=05608a1479d0d62467e40abed11753e213006fdb&token=1634706552_bed2a38225448d1929b337176fbff5f7c9082924' } }, - { type: 'album', - name: 'Loose Jewels B-Side', - url: 'https://diarrheaplanet.bandcamp.com/album/loose-jewels-b-side-2', - imageUrl: 'https://f4.bcbits.com/img/a3179428613_16.jpg', - genre: 'alternative', - artist: - { name: 'Diarrhea Planet', - url: 'https://diarrheaplanet.bandcamp.com' }, - featuredTrack: - { name: 'Miami Hearts', - streamUrl: 'https://t4.bcbits.com/stream/726a6d61b8bab746b6a17a71a1a02b4c/mp3-128/2153827649?p=0&ts=1634706552&t=acd63ce3e663df62455025189bd5c5feacaebfbc&token=1634706552_9f299f30717ff85b824665a68f7dfdec3b6f291d' } }, - { type: 'album', - name: 'So Bless Us Now...', - url: 'https://m00nlit.bandcamp.com/album/so-bless-us-now', - imageUrl: 'https://f4.bcbits.com/img/a851991037_16.jpg', - genre: 'rock', - artist: { name: 'Moonlit', url: 'https://m00nlit.bandcamp.com' }, - featuredTrack: - { name: 'And We Stood Still Until We Became, Invisible', - streamUrl: 'https://t4.bcbits.com/stream/1af7fed562f0da1d609cd4c3e3d64d1b/mp3-128/1804186391?p=0&ts=1634706552&t=d82b17c78b8a05d35c4ee3e37511b6284cb9ae2c&token=1634706552_0ed944be08e5947193f478e7cfabc282716e88bf' } }, - { type: 'album', - name: 'What Makes Us', - url: 'https://6660mhz.bandcamp.com/album/what-makes-us', - imageUrl: 'https://f4.bcbits.com/img/a1859405546_16.jpg', - genre: 'ambient', - artist: { name: '6660MHz', url: 'https://6660mhz.bandcamp.com' }, - featuredTrack: - { name: 'Nostalgic Trauma', - streamUrl: 'https://t4.bcbits.com/stream/4f66a746ac4d6b7709c649db92babcb2/mp3-128/2592367994?p=0&ts=1634706552&t=45c8f12a035be02de680022f7237b292ce8e3259&token=1634706552_d94be06c3badabd268a051d664b53807a723d074' } }, - { type: 'album', - name: 'What Shall I Lay Before the Altar of Time?', - url: 'https://primitivepropaganda.bandcamp.com/album/what-shall-i-lay-before-the-altar-of-time', - imageUrl: 'https://f4.bcbits.com/img/a1089050962_16.jpg', - genre: 'electronic', - artist: - { name: 'Various Artists', - url: 'https://primitivepropaganda.bandcamp.com' }, - featuredTrack: - { name: 'What We Know to Be True', - streamUrl: 'https://t4.bcbits.com/stream/5e4f2f86a6836cf19ceff3d0551c1f1b/mp3-128/2741726606?p=0&ts=1634706552&t=e8c84f694c81c3efb36db543271b20362312f8e1&token=1634706552_1a797342a7ad76ae7463702d95eea2c63ea74578' } } ] } ] diff --git a/examples/getAlbumInfo.js b/examples/getAlbumInfo.js deleted file mode 100644 index 6662642..0000000 --- a/examples/getAlbumInfo.js +++ /dev/null @@ -1,14 +0,0 @@ -const bcfetch = require('../'); -const util = require('util'); - -const albumUrl = 'https://musique.coeurdepirate.com/album/blonde'; - -const options = { - albumImageFormat: 'art_app_large', - artistImageFormat: 'bio_featured', - includeRawData: false -} - -bcfetch.getAlbumInfo(albumUrl, options).then( results => { - console.log(util.inspect(results, false, null, false)); -}); \ No newline at end of file diff --git a/examples/getAlbumInfo_output.txt b/examples/getAlbumInfo_output.txt deleted file mode 100644 index b40ccb9..0000000 --- a/examples/getAlbumInfo_output.txt +++ /dev/null @@ -1,107 +0,0 @@ -{ type: 'album', - name: 'Blonde', - url: 'https://musique.coeurdepirate.com/album/blonde', - numTracks: 12, - imageUrl: 'https://f4.bcbits.com/img/a1328452291_16.jpg', - keywords: - [ 'Pop', - 'amour', - 'coeur de pirate', - 'french', - 'french pop', - 'grosse boîte', - 'montreal', - 'piano pop', - 'Montréal' ], - description: 'Après avoir immortalisé tout un pan de son adolescence dans les chansons pop douces-amères d\'un premier album homonyme, Coeur de pirate s\'attaque aux différentes étapes de la relation amoureuse, d\'où le titre de l\'album, en référence avant tout, à la copine, à l\'amoureuse. L\'album a été enregistré à l\'été 2011 à l\'Hotel2Tango sous la gouverne d\'Howard Bilerman, coréalisateur avec Béatrice Martin.', - releaseDate: '07 Nov 2011 00:00:00 GMT', - artist: - { name: 'Cœur de pirate', - url: 'https://musique.coeurdepirate.com', - description: 'Plus d’une décennie s’est écoulée depuis que Béatrice Martin s’est incrustée dans le paysage sous le pseudonyme désormais coutumier de Cœur de pirate. Armée d’un talent digne de l’orfèvrerie, d’une poésie tantôt raffinée, tantôt subversive, et d’une aura insaisissable, elle séduit comme elle surprend, jaillissant là où on ne l’attend pas.', - imageUrl: 'https://f4.bcbits.com/img/0026415167_28.jpg' }, - publisher: - { name: 'Cœur de pirate', - url: 'https://musique.coeurdepirate.com', - description: 'Plus d’une décennie s’est écoulée depuis que Béatrice Martin s’est incrustée dans le paysage sous le pseudonyme désormais coutumier de Cœur de pirate. Armée d’un talent digne de l’orfèvrerie, d’une poésie tantôt raffinée, tantôt subversive, et d’une aura insaisissable, elle séduit comme elle surprend, jaillissant là où on ne l’attend pas.', - imageUrl: 'https://f4.bcbits.com/img/0026415167_28.jpg' }, - label: - { name: 'Bravo musique', - url: 'https://bravomusique.bandcamp.com' }, - releases: - [ { name: 'Blonde', - url: 'https://musique.coeurdepirate.com/album/blonde#a838845289', - format: 'DigitalFormat', - description: 'Includes high-quality download in MP3, FLAC and more. Paying supporters also get unlimited streaming via the free Bandcamp app.', - imageUrl: 'https://f4.bcbits.com/img/a1328452291_10.jpg' }, - { name: 'Blonde - CD', - url: 'https://musique.coeurdepirate.com/album/blonde#p3309945537', - format: 'CDFormat', - description: 'Blonde - Édition régulière comprend le téléchargement gratuit de l\'album digital. Veuillez allouer 3 jours ouvrables pour que la version physique vous soit postée.\nPour les commandes hors-Canada, vous devez compter de 2 à 4 semaines pour recevoir votre achat.', - imageUrl: 'https://f4.bcbits.com/img/0000167149_10.jpg' }, - { name: 'Blonde - Vinyle', - url: 'https://musique.coeurdepirate.com/album/blonde#p3172983276', - format: 'VinylFormat', - description: 'L\'édition vinyle de Blonde comprend les 12 pistes de l\'édition régulière, ainsi qu\'une affichette 12x24 po. L\'achat comprend le téléchargement gratuit de l\'album en version régulière.\nVeuillez allouer 3 jours ouvrables pour que la version physique vous soit postée.', - imageUrl: 'https://f4.bcbits.com/img/0012274631_10.jpg' } ], - tracks: - [ { position: 1, - name: 'Lève les voiles', - url: 'https://musique.coeurdepirate.com/track/l-ve-les-voiles', - duration: 72.7333, - streamUrl: 'https://t4.bcbits.com/stream/4ec5267c795d34d71d84b8d9e6373477/mp3-128/4183427253?p=0&ts=1634747875&t=288f27f09cfbe6093fd84a5aebf0457110128c41&token=1634747875_0def50336fd55bbbd354b0417a23f4dc27f80cde' }, - { position: 2, - name: 'Adieu', - url: 'https://musique.coeurdepirate.com/track/adieu', - duration: 147.627, - streamUrl: 'https://t4.bcbits.com/stream/bdf4f2f59952f78effcbbf3ff63300db/mp3-128/3647767496?p=0&ts=1634747875&t=15dc907570493613c5541335f0e87d4db75dd3c3&token=1634747875_c09159e14180f5b4169c43a465bd4788ec525f66' }, - { position: 3, - name: 'Danse et danse', - url: 'https://musique.coeurdepirate.com/track/danse-et-danse', - duration: 190.413, - streamUrl: 'https://t4.bcbits.com/stream/8d4d755d9f351caf16529342096c3de6/mp3-128/1276790164?p=0&ts=1634747875&t=da92541d58478f9f8e91224095b6c694ee99b262&token=1634747875_43da7337570df4b291b7973278a79248ed4321ab' }, - { position: 4, - name: 'Golden Baby', - url: 'https://musique.coeurdepirate.com/track/golden-baby', - duration: 187.027, - streamUrl: 'https://t4.bcbits.com/stream/af5533da023515d8c6c28dd834b7872f/mp3-128/365646139?p=0&ts=1634747875&t=49841fd6caf999eef5f1cbbd7b6b4cb23eb5bfc7&token=1634747875_fbcc936cb9773dde16c52fc05951da6dbd752176' }, - { position: 5, - name: 'Ava', - url: 'https://musique.coeurdepirate.com/track/ava', - duration: 196.88, - streamUrl: 'https://t4.bcbits.com/stream/5133f1cab7fa8da200f1392690aca50e/mp3-128/1796143680?p=0&ts=1634747875&t=419773a55a88457d1f1480955050c46ae8a27e11&token=1634747875_7f89d0a4faba5e2a9f5039c4506216a584226738' }, - { position: 6, - name: 'Loin d\'ici', - url: 'https://musique.coeurdepirate.com/track/loin-dici', - duration: 163.827, - streamUrl: 'https://t4.bcbits.com/stream/8a3166238fe996999f457459c592f7c7/mp3-128/260996437?p=0&ts=1634747875&t=896cbdd34f1de0390d0d3ecf1587e9589f32772b&token=1634747875_703b6139370381c87eec87b4b15d90c1b3fd984d' }, - { position: 7, - name: 'Les amours dévouées', - url: 'https://musique.coeurdepirate.com/track/les-amours-d-vou-es', - duration: 147.947, - streamUrl: 'https://t4.bcbits.com/stream/a62a0cd6f5b871e7559627b34f996552/mp3-128/4086570201?p=0&ts=1634747875&t=8a782eb7d7019bfea081486acaadc58423dd2a26&token=1634747875_d365b536b44b642af0ce132525983a89aa5ce775' }, - { position: 8, - name: 'Place de la République', - url: 'https://musique.coeurdepirate.com/track/place-de-la-r-publique', - duration: 251.227, - streamUrl: 'https://t4.bcbits.com/stream/f7f5df6de9007b9415e120d8083cd973/mp3-128/1239774913?p=0&ts=1634747875&t=413e9b11ea3f6f40f496578f5b2fc074d6bb1071&token=1634747875_379b1940d0dea74a349a9b91d69741b74b09b81c' }, - { position: 9, - name: 'Cap Diamant', - url: 'https://musique.coeurdepirate.com/track/cap-diamant', - duration: 163.293, - streamUrl: 'https://t4.bcbits.com/stream/732fc4dbcd8ca3cc2cc477d773f99206/mp3-128/3259278294?p=0&ts=1634747875&t=03befa3bf75a803236c720cf2f37733a16b0a481&token=1634747875_632d826cd8156a2b6e860beca7cfeabdf84d4ee7' }, - { position: 10, - name: 'Verseau', - url: 'https://musique.coeurdepirate.com/track/verseau', - duration: 233.813, - streamUrl: 'https://t4.bcbits.com/stream/a4e97e8b436b8e2ad1a2c598f5cd0a48/mp3-128/496320675?p=0&ts=1634747875&t=ae1d714e30d863807ecc59f331c163fc80e58847&token=1634747875_b9e28c41cb41711293f316e70c6472dbe76d0523' }, - { position: 11, - name: 'Saint-Laurent', - url: 'https://musique.coeurdepirate.com/track/saint-laurent', - duration: 194.787, - streamUrl: 'https://t4.bcbits.com/stream/5f87e431eaaf515564c006de4c888e91/mp3-128/2437695881?p=0&ts=1634747875&t=7de937b37b9f0321eb1fcaf6f13afb11d6c4fc4d&token=1634747875_b514123b40142ee114af1f7b18a3b48aaabc1f8c' }, - { position: 12, - name: 'La petite mort', - url: 'https://musique.coeurdepirate.com/track/la-petite-mort', - duration: 229.627, - streamUrl: 'https://t4.bcbits.com/stream/b257ccb8aca3ea864cbb2774e9d8d2be/mp3-128/525064765?p=0&ts=1634747875&t=4294568d28958eccb6088613b410067d062df830&token=1634747875_8921fbc52961212a6763a120b388272f338cc3cd' } ] } diff --git a/examples/getAllShows.js b/examples/getAllShows.js deleted file mode 100644 index 5941d3c..0000000 --- a/examples/getAllShows.js +++ /dev/null @@ -1,6 +0,0 @@ -const bcfetch = require('../'); -const util = require('util'); - -bcfetch.getAllShows().then( results => { - console.log(util.inspect(results, false, null, false)); -}); \ No newline at end of file diff --git a/examples/getAllShows_output.txt b/examples/getAllShows_output.txt deleted file mode 100644 index 23a3bfb..0000000 --- a/examples/getAllShows_output.txt +++ /dev/null @@ -1,901 +0,0 @@ -[ { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=494', - publishedDate: '15 Oct 2021 00:00:00 GMT', - description: 'Shantel Amundson talks about the new Illudium LP and the California fire seasons that inspired it.', - imageCaption: 'Hosted by Brad Sanders. Illustration of Illudium by Natalie Foss.', - subtitle: 'Out of the Ashes', - imageUrl: 'https://f4.bcbits.com/img/26420119_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/26420119_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=493', - publishedDate: '12 Oct 2021 00:00:00 GMT', - description: 'Vanishing Twin, Scrimshire and International Anthem stop by to talk about their new projects.', - imageCaption: 'Hosted by Aly Gillani. Illustration of Vanishing Twin by Noopur Choksi.', - subtitle: 'Out Of Phase', - imageUrl: 'https://f4.bcbits.com/img/26387105_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/26387105_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=492', - publishedDate: '08 Oct 2021 00:00:00 GMT', - description: 'Injury Reserve drop in with their amazing album, plus Yung Morpheus, Eli Don and more.', - imageCaption: 'Hosted by Stoney Creation. Illustration of Injury Reserve by McKay Felt.', - subtitle: 'Arriving in Phoenix', - imageUrl: 'https://f4.bcbits.com/img/26354142_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/26354142_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=491', - publishedDate: '05 Oct 2021 00:00:00 GMT', - description: 'Vels Trio stop by to talk about prog-jazz, recording in Kate Bush\'s studio and their new LP Celestial Greens.\n', - imageCaption: 'Hosted by Aly Gillani. Illustration of Vels Trio by Paul Grelet.', - subtitle: 'Eat Your Greens', - imageUrl: 'https://f4.bcbits.com/img/26319878_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/26319878_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=490', - publishedDate: '01 Oct 2021 00:00:00 GMT', - description: 'Jake Dieffenbach stops by to discuss the new Rivers of Nihil album and his life as a deaf heavy metal singer.', - imageCaption: 'Hosted by Brad Sanders. Illustration of Rivers of Nihil by Natalie Foss.', - subtitle: 'Do the Work', - imageUrl: 'https://f4.bcbits.com/img/26264876_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/26264876_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=489', - publishedDate: '28 Sep 2021 00:00:00 GMT', - description: 'Guests BadBadNotGood, Uffe, and Kit Sebastian provide a triple threat musical meltdown.', - imageCaption: 'Andrew Jervis hosts. Illustration of BADBADNOTGOOD. by Hsiao-Ron Cheng.', - subtitle: 'Signal From The Noise', - imageUrl: 'https://f4.bcbits.com/img/26239785_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/26239785_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=488', - publishedDate: '24 Sep 2021 00:00:00 GMT', - description: 'Damu The Fudgemunk joins the show to share his recent collab with Def Pressé, plus tunes by Marrice Anthony and Common ', - imageCaption: 'Hosted by Stoney Creation. Illustration of Damu The Fudgemunk by McKay Felt.', - subtitle: 'Digging Through The Times', - imageUrl: 'https://f4.bcbits.com/img/26204489_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/26204489_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=487', - publishedDate: '21 Sep 2021 00:00:00 GMT', - description: 'The inimitable Jose Gonzalez and border-smashing Kondi Band prove that music is a universal language.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of José González by Noopur Choksi.', - subtitle: 'The Universal Language', - imageUrl: 'https://f4.bcbits.com/img/26173325_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/26173325_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=486', - publishedDate: '17 Sep 2021 00:00:00 GMT', - description: 'Matt Tluchowski and Steve Lehocky from Doctor Smoke pop by to talk about their new album of big-tent heavy metal. \n', - imageCaption: 'Hosted by Brad Sanders. Illustration of Doctor Smoke by Louise Pomeroy.', - subtitle: 'Reborn Into Darkness', - imageUrl: 'https://f4.bcbits.com/img/26134341_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/26134341_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=485', - publishedDate: '14 Sep 2021 00:00:00 GMT', - description: 'The legendary Don Letts and NYC\'s Musclecars guest, plus new gems by Mild High Club, Theon Cross, and Kalabrese.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Don Letts by Paul Grelet.', - subtitle: 'Rebel Tunes', - imageUrl: 'https://f4.bcbits.com/img/26104342_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/26104342_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=484', - publishedDate: '10 Sep 2021 00:00:00 GMT', - description: 'Appearances from Injury Reserve and Damu The Fudgemunk, plus Bluestaeb joins the show to discuss his recent release, "GISEKE" \n', - imageCaption: 'Hosted by Stoney Creation. Illustration of Bluestaeb by McKay Felt.', - subtitle: 'Leon Giseke', - imageUrl: 'https://f4.bcbits.com/img/26068378_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/26068378_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=483', - publishedDate: '07 Sep 2021 00:00:00 GMT', - description: 'Danalogue and Betamax talk Soccer96, Maston drops by, plus new Nightmares on Wax, Brkn Record, and Josi Dias.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Soccer96 by Hsiao-Ron Cheng.', - subtitle: 'Interplanetary Improvisations', - imageUrl: 'https://f4.bcbits.com/img/26032563_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/26032563_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=482', - publishedDate: '03 Sep 2021 00:00:00 GMT', - description: 'Lisa Mungo from Seattle\'s Filth Is Eternal stops by, and Botch headlines a hardcore-heavy back half of the show', - imageCaption: 'Hosted by Brad Sanders. Illustration of Filth Is Eternal by Natalie Foss.', - subtitle: 'Scream and Writhe', - imageUrl: 'https://f4.bcbits.com/img/25996030_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25996030_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=481', - publishedDate: '31 Aug 2021 00:00:00 GMT', - description: 'Lady Blackbird guests with her highly anticipated LP, plus music by Lee Scratch Perry, Cleo Sol, James Rushworth, and Carwyn Ellis.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Lady Blackbird by Noopur Choksi.', - subtitle: 'Black Acid Soul', - imageUrl: 'https://f4.bcbits.com/img/25963088_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25963088_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=480', - publishedDate: '27 Aug 2021 00:00:00 GMT', - description: 'WesWill shares his recent release, "Open 24 Hours", plus appearances from Cartiercal and Myst Milano', - imageCaption: 'Hosted by Stoney Creation. Illustration of WesWill by McKay Felt.', - subtitle: 'Friday', - imageUrl: 'https://f4.bcbits.com/img/25929081_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25929081_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=479', - publishedDate: '20 Aug 2021 00:00:00 GMT', - description: 'Underdark\'s Abi Vasquez stops by to chat about their debut record, plus a Pick from the Crypt from Dawn Ray\'d.', - imageCaption: 'Hosted by Brad Sanders. Illustration of Underdark by Louise Pomeroy', - subtitle: 'Burning Bright', - imageUrl: 'https://f4.bcbits.com/img/25864651_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25864651_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=478', - publishedDate: '17 Aug 2021 00:00:00 GMT', - description: 'Super summertime tunes plus guests Eblis of Meridian Brothers and Arjuna Oakes & Serebii. ', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Meridian Brothers by Hsiao-Ron Cheng.', - subtitle: 'Tunes of Paradise', - imageUrl: 'https://f4.bcbits.com/img/25831752_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25831752_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=477', - publishedDate: '13 Aug 2021 00:00:00 GMT', - description: 'MIKE joins the show to discuss his recent release, "Disco!". Featuring tunes by Quadry, Reaper Mook, and more.', - imageCaption: 'Hosted by Stoney Creation. Illustration of MIKE by McKay Felt.', - subtitle: 'Big Love', - imageUrl: 'https://f4.bcbits.com/img/25799465_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25799465_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=476', - publishedDate: '10 Aug 2021 00:00:00 GMT', - description: 'Sampology stops by to talk about his new record Regrowth plus new music from Swindle, Jungle, Emmavie and more.', - imageCaption: 'Hosted by Aly Gillani. Illustration of Sampology by Noopur Choksi.', - subtitle: 'Memories In Flight', - imageUrl: 'https://f4.bcbits.com/img/25766863_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25766863_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=475', - publishedDate: '06 Aug 2021 00:00:00 GMT', - description: 'Kris Esfandiari stops by to discuss the new King Woman LP on a special "heavy-but-not-metal" episode of the show.', - imageCaption: 'Hosted by Brad Sanders. Illustration of Kingwoman by Natalie Foss', - subtitle: 'Psychic Wounds', - imageUrl: 'https://f4.bcbits.com/img/25727862_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25727862_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=474', - publishedDate: '03 Aug 2021 00:00:00 GMT', - description: 'Homeboy Sandman drops off his latest project, plus new releases from Pink Siifu, MMYYKK, and Drimia.', - imageCaption: 'Hosted by Stoney Creation. Illustration by Hsiao-Ron Cheng.', - subtitle: 'Mr. Sandman', - imageUrl: 'https://f4.bcbits.com/img/25687222_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25687222_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=473', - publishedDate: '30 Jul 2021 00:00:00 GMT', - description: 'Wiki joins the show to discuss his recent release, "Telephonebooth", plus some grooves by Kari Faux, Bluestaeb, and more.', - imageCaption: 'Hosted by Stoney Creation. Illustration of Wiki by McKay Felt', - subtitle: 'Wiki No Pedia', - imageUrl: 'https://f4.bcbits.com/img/25651747_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25651747_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=472', - publishedDate: '27 Jul 2021 00:00:00 GMT', - description: 'With Guest Hosts Local Talk And Ishmael Ensemble', - imageCaption: 'Hosted by Local Talk Records and Ishmael Ensemble . Illustration of Soulphiction by Noopur Choksi.', - subtitle: 'With Guest Hosts Local Talk And Ishmael Ensemble', - imageUrl: 'https://f4.bcbits.com/img/25619018_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25619018_0' }, - { type: 'show', - name: 'The Metal Show ', - url: 'https://bandcamp.com/?show=471', - publishedDate: '23 Jul 2021 00:00:00 GMT', - description: 'Cavan and Brighid Wagner of Felled talk nature and metal, and indigenous folk metallers Nechochwen are our Pick from the Crypt.', - imageCaption: 'Brad Sanders. Illustration of Felled by Louise Pomeroy', - subtitle: 'Leave No Trace', - imageUrl: 'https://f4.bcbits.com/img/25587230_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25587230_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=470', - publishedDate: '20 Jul 2021 00:00:00 GMT', - description: 'Guest host Coco Maria brings her unique selection of sounds to the Bandcamp Weekly with a focus on Guess What.', - imageCaption: 'Hosted by Coco Maria. Illustration of  Guess What by Paul Grelet.', - subtitle: 'With Guest Host Coco Maria', - imageUrl: 'https://f4.bcbits.com/img/25556063_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25556063_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=469', - publishedDate: '16 Jul 2021 00:00:00 GMT', - description: 'Pacific Yew joins the show with his newest release, "Modest ((( ))) Odyssey". Plus some tunes by WesWill and Rejjie Snow.', - imageCaption: 'Hosted by Stoney Creation. Illustration of Pacific Yew by McKay Felt', - subtitle: 'Tree Meets Human ', - imageUrl: 'https://f4.bcbits.com/img/25524662_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25524662_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=468', - publishedDate: '13 Jul 2021 00:00:00 GMT', - description: 'Portland\'s quickly quickly and Detroit\'s The Lasso stop by to talk about their new records. ', - imageCaption: 'Hosted by Aly Gillani. Illustration of Quickly Quickly by Hsiao-Ron Cheng.', - subtitle: 'Perfect Phases', - imageUrl: 'https://f4.bcbits.com/img/25492755_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25492755_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=467', - publishedDate: '09 Jul 2021 00:00:00 GMT', - description: 'UK trio Mountain Caller drop by for a chat, plus instru-metal classics from Kenoma, Pelican, and Russian Circles', - imageCaption: 'Hosted by Brad Sanders. Illustration of Mountain Caller by Natalie Foss', - subtitle: 'The Truthseekers', - imageUrl: 'https://f4.bcbits.com/img/25459559_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25459559_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=466', - publishedDate: '06 Jul 2021 00:00:00 GMT', - description: 'Jazz musician and composer Emma-Jean Thackray talks about her new record \'Yellow\'', - imageCaption: 'Hosted by Aly Gillani. Illustration of Emma-Jean Thackray by Noopur Choksi.', - subtitle: 'The Next Movement', - imageUrl: 'https://f4.bcbits.com/img/25425995_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25425995_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=465', - publishedDate: '02 Jul 2021 00:00:00 GMT', - description: 'bbymutha joins the show to discuss her latest release "bastard tapes vol.3." Plus featured sounds by Mick Jenkins and Jordan Rakei.', - imageCaption: 'Hosted by Stoney Creation. Illustration of bbymutha by McKay Felt.', - subtitle: 'BIGMUTHA', - imageUrl: 'https://f4.bcbits.com/img/25391554_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25391554_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=464', - publishedDate: '29 Jun 2021 00:00:00 GMT', - description: 'Fresh new cuts by Brittany Howard, SAULT, Homeboy Sandman, Hypnotic Brass Ensemble, plus Rodrigo Amarante guests.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Rodrigo Amarante by Paul Grelet.', - subtitle: 'Soul Searching', - imageUrl: 'https://f4.bcbits.com/img/25356194_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25356194_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=463', - publishedDate: '25 Jun 2021 00:00:00 GMT', - description: 'James Kirn of Philly\'s Blazon Rite talks fantasy, role-playing, and true metal, plus a block of old-school Cirith Ungol classics.', - imageCaption: 'Hosted by Brad Sanders. Illustration of Blazon Rite by Paul Grelet', - subtitle: 'Into Endless Halls', - imageUrl: 'https://f4.bcbits.com/img/25323558_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25323558_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=462', - publishedDate: '22 Jun 2021 00:00:00 GMT', - description: 'A 3-hour special featuring sunshine sounds and interviews with the mighty Hiatus Kaiyote and genre-busting L\'Rain. ', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Hiatus Kaiyote by Hsiao-Ron Cheng.', - subtitle: 'No Formula Required', - imageUrl: 'https://f4.bcbits.com/img/25290837_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25290837_0' }, - { type: 'show', - name: 'The Hip Hop Show', - url: 'https://bandcamp.com/?show=461', - publishedDate: '18 Jun 2021 00:00:00 GMT', - description: 'Featured sounds by K.Spark and WIZDUMB, plus Basi joins the show to discuss his latest release "Save The Day". ', - imageCaption: 'Hosted by Stoney Creation. Illustration of Basi by McKay Felt', - subtitle: 'Hello', - imageUrl: 'https://f4.bcbits.com/img/25255189_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25255189_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=460', - publishedDate: '15 Jun 2021 00:00:00 GMT', - description: 'UK jazz musician and band-leader Rosie Turton stops by to talk about her new record Expansions and Transformations.', - imageCaption: 'Hosted by Aly Gillani. Illustration of Rosie Turton by Noopur Choksi.', - subtitle: 'Expand Your Mind', - imageUrl: 'https://f4.bcbits.com/img/25218616_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25218616_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=459', - publishedDate: '11 Jun 2021 00:00:00 GMT', - description: 'Serena Cherry debuts her Skyrim-inspired black metal project Noctule, plus selections from her post-hardcore band, Svalbard.', - imageCaption: 'Hosted by Brad Sanders. Illustration of Noctule by Natalie Foss', - subtitle: 'Unrelenting Force', - imageUrl: 'https://f4.bcbits.com/img/25184066_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25184066_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=458', - publishedDate: '08 Jun 2021 00:00:00 GMT', - description: 'Lil\' Louie Vega and Kenny Dope Gonzales drop some serious club music history, plus Rebecca Vasmant spotlights Glasgow\'s finest.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Masters at Work by Paul Grelet.', - subtitle: 'Our Time Is Coming', - imageUrl: 'https://f4.bcbits.com/img/25150716_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25150716_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=457', - publishedDate: '04 Jun 2021 00:00:00 GMT', - description: 'Patrick Paige II drops off his latest release "If I Fail, Are We Still Cool?". Plus featured sounds by Rochelle Jordan, and Wiki. ', - imageCaption: 'Hosted by Stoney Creation. Illustration of Patrick Page II by McKay Felt', - subtitle: 'Stay Obsessed', - imageUrl: 'https://f4.bcbits.com/img/25111897_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25111897_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=456', - publishedDate: '01 Jun 2021 00:00:00 GMT', - description: 'Guests Mndsgn and Jaubi talk about their new albums, plus new sounds from Mansur Brown, Children Of Zeus and more.', - imageCaption: 'Hosted by Aly Gillani. Illustration of Mndsgn. by Hsiao-Ron Cheng.', - subtitle: 'Music For Pleasure', - imageUrl: 'https://f4.bcbits.com/img/25075004_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25075004_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=455', - publishedDate: '28 May 2021 00:00:00 GMT', - description: 'A vibrant playlist highlighting the thriving metal scene in India, plus an interview with Shasank Venkat from Against Evil. ', - imageCaption: 'Hosted by Brad Sanders. Illustration of Against Evil by Louise Pomeroy.', - subtitle: 'Metal in India', - imageUrl: 'https://f4.bcbits.com/img/25037808_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25037808_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=454', - publishedDate: '25 May 2021 00:00:00 GMT', - description: 'Stubborn Heart defeat writer\'s block and Portico Quartet chart a different path, plus new Four Tet, Muwosi, and Lifafa.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Stubborn Heart by Noopur Choksi.', - subtitle: 'Hiding Music In Everything', - imageUrl: 'https://f4.bcbits.com/img/25001359_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/25001359_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=453', - publishedDate: '21 May 2021 00:00:00 GMT', - description: 'Georgia Anne Muldrow drops off her latest release \'VWETO III\'. Plus featured sounds from Akai Solo, Jamee Cornelia, & TYKGO.', - imageCaption: 'Hosted by Stoney Creation. Illustration of Georgia Anne Muldrow by McKay Felt', - subtitle: 'Georgia The Goat', - imageUrl: 'https://f4.bcbits.com/img/24964909_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24964909_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=452', - publishedDate: '18 May 2021 00:00:00 GMT', - description: 'Take a fantastic voyage with Coco Maria and Anchorsong who stop by with their tropical and transportive new LPs.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Coco Maria by Paul Grelet.', - subtitle: 'Fantastic Voyage', - imageUrl: 'https://f4.bcbits.com/img/24926468_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24926468_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=451', - publishedDate: '14 May 2021 00:00:00 GMT', - description: 'Featuring a journey through left-field, outré black metal, with a Victory Over the Sun interview and music from Liturgy and Jute Gyte.', - imageCaption: 'Hosted by Brad Sanders. Illustration of Vivian Tylinska by Louise Pomeroy', - subtitle: 'Transcending Boundaries', - imageUrl: 'https://f4.bcbits.com/img/24888745_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24888745_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=450', - publishedDate: '11 May 2021 00:00:00 GMT', - description: 'A deep and wide dive into essential new jazz sounds with Kiefer, Maya Dunietz, and James Brandon Lewis.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Maya Dunietz by Hsiao-Ron Cheng.', - subtitle: 'We Got The Jazz', - imageUrl: 'https://f4.bcbits.com/img/24851594_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24851594_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=449', - publishedDate: '07 May 2021 00:00:00 GMT', - description: 'Mother Nature joins the show to discuss their latest release \'SZNS\' with featured sounds from FlyLo, Ahmed Idris Hussein, and Amaarae', - imageCaption: 'Hosted by Stoney Creation. Illustration of Mother Nature by McKay Felt', - subtitle: 'Tis The SZN', - imageUrl: 'https://f4.bcbits.com/img/24802241_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24802241_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=448', - publishedDate: '04 May 2021 00:00:00 GMT', - description: 'Brazil\'s Lucas Santtana and Italian producer Khalab guide us through their latest hybrid sounds. Plus new Flylo, Jaimie Branch, and Mocky. ', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Lucas Santtana by Noopur Choksi.', - subtitle: 'Meeting of Musical Minds', - imageUrl: 'https://f4.bcbits.com/img/24736160_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24736160_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=447', - publishedDate: '30 Apr 2021 00:00:00 GMT', - description: 'Spectral Wound\'s Jonah Campbell discusses his band\'s debauched black metal, plus classic tunes from Immortal on this week\'s episode.', - imageCaption: 'Hosted by Brad Sanders. Illustration of Spectral Wound by Louise Pomeroy.', - subtitle: 'Decadence and Decay', - imageUrl: 'https://f4.bcbits.com/img/24694436_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24694436_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=446', - publishedDate: '27 Apr 2021 00:00:00 GMT', - description: 'The mysterious Sven Wunder pulls back the curtain on his epic music, plus fresh cuts by Emma-Jean Thackray and Mndsgn.', - imageCaption: 'Hosted by Aly Gillani. Illustration of Sven Wunder by Paul Grelet.', - subtitle: 'Escape Plan', - imageUrl: 'https://f4.bcbits.com/img/24655670_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24655670_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=445', - publishedDate: '23 Apr 2021 00:00:00 GMT', - description: 'HENO. joins the show to discuss his latest release \'Death Ain\'t That Bad\' plus featured sounds from Georgia Anne Muldrow and Yons', - imageCaption: 'Hosted by Stoney Creation. Illustration of Heno by McKay Felt', - subtitle: 'C\'est La Vie', - imageUrl: 'https://f4.bcbits.com/img/24616190_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24616190_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=444', - publishedDate: '20 Apr 2021 00:00:00 GMT', - description: 'Polish band Bloto and French act NCY Milky Band guest, plus new Joao Donato, Natalie Bergman, and King Britt. ', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Bloto by Hsiao-Ron Cheng.', - subtitle: 'Action Reaction', - imageUrl: 'https://f4.bcbits.com/img/24576281_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24576281_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=443', - publishedDate: '16 Apr 2021 00:00:00 GMT', - description: 'Cannibal Corpse\'s George "Corpsegrinder" Fisher stops by for an interview and picks some of his favorite tracks on Bandcamp', - imageCaption: ' Brad Sanders. Illustration of George "Corpsegrinder" Fisher by Louise Pomeroy', - subtitle: 'Vileness Unimagined', - imageUrl: 'https://f4.bcbits.com/img/24533563_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24533563_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=442', - publishedDate: '13 Apr 2021 00:00:00 GMT', - description: 'Guest host Aly Gillani talks to Benji B about his new compilation ‘Deviation Classics’ plus new sounds from Masters At Work and more', - imageCaption: 'Hosted by Aly Gillani. Illustration of Benji B by Noopur Choksi.', - subtitle: 'London Underground', - imageUrl: 'https://f4.bcbits.com/img/24496083_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24496083_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=441', - publishedDate: '09 Apr 2021 00:00:00 GMT', - description: 'Baghead & Jules join the show to discuss their latest collaboration plus featured sounds by Raiza Biza, Patrick Paige II, and Niña Dioz', - imageCaption: 'Hosted by Stoney Creation. Illustration of Baghead by McKay Felt', - subtitle: 'Dedicated To Those Who', - imageUrl: 'https://f4.bcbits.com/img/24444034_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24444034_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=440', - publishedDate: '06 Apr 2021 00:00:00 GMT', - description: 'Featuring interviews with harpist Amanda Whiting and Big Dada signing Yaya Bey, plus an essential reissue round-up.', - imageCaption: ' Hosted by Andrew Jervis. Illustration of Amanda Whiting by Paul Grelet.', - subtitle: 'After Dark', - imageUrl: 'https://f4.bcbits.com/img/24401041_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24401041_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=439', - publishedDate: '02 Apr 2021 00:00:00 GMT', - description: 'Moral Collapse stop by to talk about collaborating remotely, plus a block of classic Death, and new doom by Scald and Wheel.', - imageCaption: 'Hosted by Brad Sanders. Illustration of Moral Collapse by Louise Pomeroy', - subtitle: 'Trans-Continental Death Metal', - imageUrl: 'https://f4.bcbits.com/img/24352735_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24352735_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=438', - publishedDate: '30 Mar 2021 00:00:00 GMT', - description: 'Brazilian musicians Joao Selva and Jadsa drop essential new LPs, plus Ebo Taylor, Overmono, and Tune-Yards.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Joao Selva by Hsiao-Ron Cheng.', - subtitle: 'Sonór Tropicàl', - imageUrl: 'https://f4.bcbits.com/img/24294498_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24294498_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=437', - publishedDate: '26 Mar 2021 00:00:00 GMT', - description: 'FlySiifu joins the show to discuss their latest release \'$mokebreak\' alongside sounds from Kiefer, GiTori, and King Lutendo.\n', - imageCaption: 'Hosted by Stoney Creation. Illustration of FlySiifu by McKay Felt', - subtitle: 'Pink Anakin ', - imageUrl: 'https://f4.bcbits.com/img/24250259_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24250259_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=436', - publishedDate: '23 Mar 2021 00:00:00 GMT', - description: 'Incognito\'s Jean-Paul “Bluey” Maunick talks Britfunk, Str4ta, and recording with longtime friend Gilles Peterson.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Bluey from Str4ta by Noopur Choksi.', - subtitle: 'Full Circle', - imageUrl: 'https://f4.bcbits.com/img/24205538_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24205538_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=435', - publishedDate: '19 Mar 2021 00:00:00 GMT', - description: 'Kate Davies of UK mathcore savants Pupil Slicer stops by, plus a tribute to the late, great LG Petrov of Entombed.\n', - imageCaption: 'Hosted by Brad Sanders. Illustration of Pupil Slicer by Louise Pomeroy', - subtitle: 'Mathcore for the Masses', - imageUrl: 'https://f4.bcbits.com/img/24159806_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24159806_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=434', - publishedDate: '16 Mar 2021 00:00:00 GMT', - description: 'Essential tunes by Jimi Tenor, plus new Hiatus Kaiyote, Gretchen Parlato, and Fimber Bravo.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Paul Grelet.', - subtitle: 'Travel The Spaceways', - imageUrl: 'https://f4.bcbits.com/img/24116052_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24116052_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=433', - publishedDate: '12 Mar 2021 00:00:00 GMT', - description: 'THERAVADA drops off his latest release \'XENOPHON\', plus featured sounds from Noname, Haviah Mighty, and BIGMUTHA', - imageCaption: 'Hosted by Stoney Creation. Illustration of Theravada by McKay Felt', - subtitle: 'Xen', - imageUrl: 'https://f4.bcbits.com/img/24075517_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24075517_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=432', - publishedDate: '09 Mar 2021 00:00:00 GMT', - description: 'Two tons of new tunes plus guests Emanative & Liz Elensky, and Tom Brenneck of The Menahan Street Band.', - imageCaption: 'Andrew Jervis hosts. Emanative & Liz Elensky by Hsiao-Ron Cheng.', - subtitle: 'Love and Light', - imageUrl: 'https://f4.bcbits.com/img/24075541_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/24075541_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=431', - publishedDate: '05 Mar 2021 00:00:00 GMT', - description: 'Gravesend drops off their new LP, plus a classic Suffocation block and hot new power metal from Crystal Viper and Warrior Path.', - imageCaption: 'Hosted by Brad Sanders. Illustration of Gravesend by Louise Pomeroy', - subtitle: 'A Real Rain Will Come ', - imageUrl: 'https://f4.bcbits.com/img/23971755_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/23971755_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=430', - publishedDate: '02 Mar 2021 00:00:00 GMT', - description: 'Visual artist and musician Lonnie Holley and Spacebomb\'s Matthew E. White talk collaboration. ', - imageCaption: 'Hosted by Andrew Jervis. Lonnie Holley & Matthew E White by Noopur Choksi.', - subtitle: 'Come Walk With Me', - imageUrl: 'https://f4.bcbits.com/img/23907847_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/23907847_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=429', - publishedDate: '26 Feb 2021 00:00:00 GMT', - description: 'AJ, the One talks about her latest release "Real In The Field". Plus new sounds from Mavi, Ivy Sole, and Moderator.', - imageCaption: 'Hosted by Stoney Creation. Illustration of AJ, the One by McKay Felt.', - subtitle: 'Numero Uno', - imageUrl: 'https://f4.bcbits.com/img/23860221_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/23860221_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=428', - publishedDate: '23 Feb 2021 00:00:00 GMT', - description: 'On the latest Bandcamp Weekly FYI Chris talks about community, paranoia, optimism and their debut LP ‘Earth Scum’', - imageCaption: 'Hosted by Aly Gillani. Illustration of FYI Chris by Paul Grelet.', - subtitle: 'Make Some Space', - imageUrl: 'https://f4.bcbits.com/img/23814526_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/23814526_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=427', - publishedDate: '19 Feb 2021 00:00:00 GMT', - description: 'Spanish occult rock band Kabbalah discusses their new album. Plus our latest Pick from the Crypt, stoner doom legends Sleep.', - imageCaption: 'Brad Sanders. Illustration of Kabbalah by Louise Pomeroy', - subtitle: 'Light & Shadow', - imageUrl: 'https://f4.bcbits.com/img/23765790_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/23765790_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=426', - publishedDate: '16 Feb 2021 00:00:00 GMT', - description: 'Get to know multi-talented Antti Vaukhonen of Oira Pena and Soft Power, plus Tom Excell checks in with a new Nubiyan Twist LP.', - imageCaption: ' Hosted by Andrew Jervis. Illustration of Oira Pena by Hsiao-Ron Cheng.', - subtitle: 'Freedom Sounds', - imageUrl: 'https://f4.bcbits.com/img/23720067_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/23720067_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=425', - publishedDate: '12 Feb 2021 00:00:00 GMT', - description: 'Chris Crack drops off his new album, Might Delete Later, for Album of the Week, plus appearances from Fat Tony, El Individuo, and Lukah. ', - imageCaption: 'Hosted by Stoney Creation. Illustration of Chris Crack by McKay Felt', - subtitle: 'Addictive Flows', - imageUrl: 'https://f4.bcbits.com/img/23672106_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/23672106_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=424', - publishedDate: '09 Feb 2021 00:00:00 GMT', - description: 'A Jamaican-influenced Reggae-flavored special with the queen of lovers rock Janet Kay and a focus on the Pressure Sounds label.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Janet Kay by Noopur Choksi.', - subtitle: 'Love Forever', - imageUrl: 'https://f4.bcbits.com/img/23627415_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/23627415_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=423', - publishedDate: '05 Feb 2021 00:00:00 GMT', - description: 'Portrayal of Guilt\'s Matt King pays the show a visit, plus an old-school Converge block, and a dose of some Japanese speed metal.', - imageCaption: 'Hosted by Brad Sanders. Illustration of Matt King by Louise PomeroyAndrew Jervis. Illustration of Arlo Parks by Paul Grelet.', - subtitle: 'Opening Doors', - imageUrl: 'https://f4.bcbits.com/img/23496983_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/23496983_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=421', - publishedDate: '29 Jan 2021 00:00:00 GMT', - description: 'Ovrkast joins the show to discuss his latest album, Try Again. Plus sounds from Potatohead People, Isaiah Mclane, and Boom Baptist.', - imageCaption: 'Hosted by Stoney Creation. Illustration of Ovrkast by McKay Felt', - subtitle: '55 Degree Weather', - imageUrl: 'https://f4.bcbits.com/img/23443998_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/23443998_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=420', - publishedDate: '26 Jan 2021 00:00:00 GMT', - description: 'We dive into eclectic South African sounds via Indaba Is and highlight favorite new reissues.', - imageCaption: 'Andrew Jervis hosts. Thandi Nthuli and Siyabonga Mthembu by Hsiao-Ron Cheng.', - subtitle: 'On The Shoulders of Giants', - imageUrl: 'https://f4.bcbits.com/img/23399435_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/23399435_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=419', - publishedDate: '22 Jan 2021 00:00:00 GMT', - description: 'Rising L.A true metal stars Saber join the show, plus jams from the latest Gatecreeper LP, and our Pick from the Crypt, Haunt.', - imageCaption: 'Hosted by Brad Sanders. Steve Villa and Joel Dominguez by Louise Pomeroy.', - subtitle: 'Saber Comes Without Warning', - imageUrl: 'https://f4.bcbits.com/img/23349301_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/23349301_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=418', - publishedDate: '15 Jan 2021 00:00:00 GMT', - description: 'The late MF DOOM is given his flowers, plus an interview with MC Jada Imani, and appearances from Stanley Ipkuss and Dirty Art Club. \n', - imageCaption: 'Hosted by Stoney Creation. Illustration of Jada Imani by McKay Felt', - subtitle: 'The Tales Of Doom & Ms. Imani', - imageUrl: 'https://f4.bcbits.com/img/23256365_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/23256365_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=416', - publishedDate: '12 Jan 2021 00:00:00 GMT', - description: 'New sounds for 2021, plus guests Guedra Guedra, Salami Rose Joe Louis, and Tommy Guerrero.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Guedra Guedra by Noopur Choksi.', - subtitle: 'New Year, New Faces, New Sounds', - imageUrl: 'https://f4.bcbits.com/img/23214457_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/23214457_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=415', - publishedDate: '08 Jan 2021 00:00:00 GMT', - description: 'Jackie Perez Gratz of Grayceon stops by to discuss their new LP, and her old band Giant Squid is the show\'s first Pick from the Crypt', - imageCaption: 'Hosted by Brad Sanders. Illustration of Jackie Perez Gratz by Louise Pomeroy', - subtitle: 'Metal for a Burning Planet', - imageUrl: 'https://f4.bcbits.com/img/23166745_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/23166745_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=414', - publishedDate: '22 Dec 2020 00:00:00 GMT', - description: 'A super-sized episode, with guests galore, highlighting favorite artists, albums, and tunes of 2020.', - imageCaption: 'Hosted by Andrew Jervis. Illustration by Paul Grelet.', - subtitle: 'Lockdown Loves', - imageUrl: 'https://f4.bcbits.com/img/22958948_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/22958948_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=413', - publishedDate: '18 Dec 2020 00:00:00 GMT', - description: 'Denise Chaila joins the show from Ireland to discuss her latest release, with additional appearances from AroMa and AJ the One.', - imageCaption: 'Hosted by Stoney Creation Illustration of Denise Chaila by McKay Felt', - subtitle: 'Ri Ra Rita', - imageUrl: 'https://f4.bcbits.com/img/22920109_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/22920109_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=412', - publishedDate: '15 Dec 2020 00:00:00 GMT', - description: 'Reflect and escape to the sound of 2020, featuring Alabaster DePlume, Nicolas Jaar, Angel Bat Dawid, and Gavsborg.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Alabaster De Plume by Hsiao-Ron Cheng.', - subtitle: 'Reflections Eternal', - imageUrl: 'https://f4.bcbits.com/img/22876592_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/22876592_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=411', - publishedDate: '11 Dec 2020 00:00:00 GMT', - description: 'Looking back on a deeply strange year with Jonas Renkse of Katatonia, plus the best metal songs of 2020.', - imageCaption: 'Hosted by Brad Sanders. Illustration of Jonas Renkse by Louise Pomeroy', - subtitle: '2020 In Review', - imageUrl: 'https://f4.bcbits.com/img/22817132_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/22817132_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=410', - publishedDate: '08 Dec 2020 00:00:00 GMT', - description: 'Karriem Riggins and Quin Kirchner guest, plus new music by Arlo Parks, Aunty Rayzor, and Yazmin Lacey.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Karriem Riggins by Noopur Choksi.', - subtitle: 'Dance to the Drummer\'s Beat', - imageUrl: 'https://f4.bcbits.com/img/22781302_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/22781302_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=409', - publishedDate: '04 Dec 2020 00:00:00 GMT', - description: 'YUNGMORPHEUS joins the show to introduce his latest project, plus appearances from Michael Sneed, Navy Blue and Nappy Nana.', - imageCaption: 'Hosted by Stoney Creation. Illustration of YOUNGMORPHEUS by McKay Felt.', - subtitle: 'The Purple Pill', - imageUrl: 'https://f4.bcbits.com/img/22717645_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/22717645_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=408', - publishedDate: '01 Dec 2020 00:00:00 GMT', - description: 'Featuring soulful trio Gabriels, plus Mark Pritchard walks us through his Warp Records faves.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Gabriels by Paul Grelet.', - subtitle: 'Love and Music', - imageUrl: 'https://f4.bcbits.com/img/22645633_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/22645633_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=406', - publishedDate: '27 Nov 2020 00:00:00 GMT', - description: 'Spirit Adrift’s Nate Garrett on his new album, plus new music by Pennsylvania traditional metal crew MindMaze.\n', - imageCaption: ' Hosted by Brad Sanders. Illustration of Spirit Adrift by Louise Pomeroy.', - subtitle: 'Heavy Enlightenment', - imageUrl: 'https://f4.bcbits.com/img/22594264_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/22594264_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=407', - publishedDate: '24 Nov 2020 00:00:00 GMT', - description: 'Guest host Aly Gillani chats to cktrl about heartbreak, belonging and his new record \'Robyn\'.', - imageCaption: 'Hosted by Aly Gillani. Illustration of cktrl by Hsiao-Ron Cheng.', - subtitle: 'In Control', - imageUrl: 'https://f4.bcbits.com/img/22552102_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/22552102_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=405', - publishedDate: '20 Nov 2020 00:00:00 GMT', - description: 'Ashia Karana guests, plus jams from across the globe by IAMDDB, Oliver Sudden, Mndsgn, Ovrkst, Queens D. Light and much more. ', - imageCaption: 'Hosted by Stoney Creation. Illustration of Ashia Karana by McKay Felt.', - subtitle: 'The 108 Karanas', - imageUrl: 'https://f4.bcbits.com/img/22503638_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/22503638_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=404', - publishedDate: '17 Nov 2020 00:00:00 GMT', - description: 'Charles Webster guests, our LP of the week is by Liam Bailey, plus new jams by Kokoroko, Rob Mazurek, and Soul Renegades. ', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Charles Webster by Noopur Choksi.', - subtitle: 'The Sounds We Play', - imageUrl: 'https://f4.bcbits.com/img/22455491_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/22455491_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=403', - publishedDate: '13 Nov 2020 00:00:00 GMT', - description: 'Featuring an interview with Annihilus\' Luca Cimarusti, plus the first new music by System of a Down in 15 years.', - imageCaption: ' Hosted by Brad Sanders. Illustration of Luca Cimarusti by Louise Pomeroy.', - subtitle: 'Into the Dark ', - imageUrl: 'https://f4.bcbits.com/img/22403178_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/22403178_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=402', - publishedDate: '10 Nov 2020 00:00:00 GMT', - description: 'Get to know Benin\'s Star Feminine Band and hear an interview with the Sun Ra Arkestra\'s Marshall Allen and Noelle Scott. ', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Star Feminine Band by Paul Grelet.', - subtitle: 'A Better Day Is Breaking', - imageUrl: 'https://f4.bcbits.com/img/22361754_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/22361754_0' }, - { type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=401', - publishedDate: '06 Nov 2020 00:00:00 GMT', - description: 'The inaugural show features an interview with Nappy Nina, plus expansive, soulful vibes from Knxledge, Jada Imani, and more.', - imageCaption: 'Hosted by Stoney Creation. Illustration of Nappy Nina by McKay Felt.', - subtitle: 'Freak It', - imageUrl: 'https://f4.bcbits.com/img/22268132_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/22268132_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=399', - publishedDate: '03 Nov 2020 00:00:00 GMT', - description: 'The Diabolical Liberties, Stephane San Juan, and Gilles Peterson guest on a 3-hour special. ', - imageCaption: 'Hosted by Andrew Jervis. The Diabolical Liberties by Hsiao-Ron Cheng', - subtitle: 'This Is Real', - imageUrl: 'https://f4.bcbits.com/img/22263735_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/22263735_0' }, - { type: 'show', - name: 'The Metal Show', - url: 'https://bandcamp.com/?show=398', - publishedDate: '30 Oct 2020 00:00:00 GMT', - description: 'Featuring Matt Harvey, frontman of death metal stalwarts Exhumed and Gruesome, plus Halloween jams. ', - imageCaption: 'Hosted by Brad Sanders. Illustration of Matt Harvey by Louise Pomeroy.', - subtitle: 'Halloween Special', - imageUrl: 'https://f4.bcbits.com/img/22188534_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/22188534_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=397', - publishedDate: '20 Oct 2020 00:00:00 GMT', - description: 'Guest host Aly Gillani chats to Drum\'n\'Bass pioneer Krust about his new album \'The Edge Of Everything\'', - imageCaption: 'Hosted by Aly Gillani. Illustration of Krust by Noopur Choksi.', - subtitle: 'On The Edge', - imageUrl: 'https://f4.bcbits.com/img/22073812_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/22073812_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=396', - publishedDate: '13 Oct 2020 00:00:00 GMT', - description: 'Looking for those perfect beats, Raul Monsalve mines Afro-Venezuelan sounds and Planet Battagon reaches outer space.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Raul Monsalves by Hsiao-Ron Cheng.', - subtitle: 'Bang on the Drum!', - imageUrl: 'https://f4.bcbits.com/img/21991804_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/21991804_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=393', - publishedDate: '06 Oct 2020 00:00:00 GMT', - description: 'Essential debut albums by Lex Amor and The Quiet Ones, plus JISR mash Munich and Morocco on our LP of the week.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Lex Amor by Paul Grelet.', - subtitle: 'London Calling', - imageUrl: 'https://f4.bcbits.com/img/21909902_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/21909902_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=392', - publishedDate: '29 Sep 2020 00:00:00 GMT', - description: 'Featuring guests Astrid Engberg and Bless The Mad, plus new Ana Frango Elétrico, Lex Amor, and Mama Odé.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Astrid Engberg by Noopur Choksi', - subtitle: 'Light in the Dark', - imageUrl: 'https://f4.bcbits.com/img/21796615_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/21796615_0' }, - { type: 'show', - name: 'Bandcamp Weekly', - url: 'https://bandcamp.com/?show=391', - publishedDate: '22 Sep 2020 00:00:00 GMT', - description: 'Genre-blurring Butcher Brown and Slauson Malone guest, plus an LP of the week by the unstoppable SAULT.', - imageCaption: 'Hosted by Andrew Jervis. Illustration of Butcher Brown by Hsiao-Ron Cheng.', - subtitle: 'Music is The Message', - imageUrl: 'https://f4.bcbits.com/img/21710614_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/21710614_0' }, - ... 371 more items ] diff --git a/examples/getArticle.js b/examples/getArticle.js deleted file mode 100644 index 9aab022..0000000 --- a/examples/getArticle.js +++ /dev/null @@ -1,12 +0,0 @@ -const bcfetch = require('../'); -const util = require('util'); - -const articleUrl = 'https://daily.bandcamp.com/best-ambient/best-new-ambient-march-2018'; - -const options = { - includeRawData: false -}; - -bcfetch.getArticle(articleUrl, options).then( results => { - console.log(util.inspect(results, false, null, false)); -}); \ No newline at end of file diff --git a/examples/getArticleCategories.js b/examples/getArticleCategories.js deleted file mode 100644 index 18cc17c..0000000 --- a/examples/getArticleCategories.js +++ /dev/null @@ -1,6 +0,0 @@ -const bcfetch = require('../'); -const util = require('util'); - -bcfetch.getArticleCategories().then( results => { - console.log(util.inspect(results, false, null, false)); -}); \ No newline at end of file diff --git a/examples/getArticleCategories_output.txt b/examples/getArticleCategories_output.txt deleted file mode 100644 index ce0b292..0000000 --- a/examples/getArticleCategories_output.txt +++ /dev/null @@ -1,126 +0,0 @@ -[ { name: 'featured', - title: '', - sections: - [ { name: 'best-of-year', - title: '', - categories: - [ { url: 'https://daily.bandcamp.com/best-of-2021', - name: 'Best of 2021' }, - { url: 'https://daily.bandcamp.com/best-of-2020', - name: 'Best of 2020' }, - { url: 'https://daily.bandcamp.com/best-of-2019', - name: 'Best of 2019' }, - { url: 'https://daily.bandcamp.com/best-of-2018', - name: 'Best of 2018' }, - { url: 'https://daily.bandcamp.com/best-of-2017', - name: 'Best of 2017' }, - { url: 'https://daily.bandcamp.com/best-of-2016', - name: 'Best of 2016' } ] }, - { name: 'best-of', - title: '', - categories: - [ { url: 'https://daily.bandcamp.com/best-ambient', - name: 'Best Ambient' }, - { url: 'https://daily.bandcamp.com/best-beat-tapes', - name: 'Best Beat Tapes' }, - { url: 'https://daily.bandcamp.com/best-dance-12s', - name: 'Best Dance 12”s' }, - { url: 'https://daily.bandcamp.com/best-contemporary-classical', - name: 'Best Contemporary Classical' }, - { url: 'https://daily.bandcamp.com/best-electronic', - name: 'Best Electronic' }, - { url: 'https://daily.bandcamp.com/best-experimental', - name: 'Best Experimental' }, - { url: 'https://daily.bandcamp.com/best-hip-hop', - name: 'Best Hip-Hop' }, - { url: 'https://daily.bandcamp.com/best-jazz', - name: 'Best Jazz' }, - { url: 'https://daily.bandcamp.com/best-metal', - name: 'Best Metal' }, - { url: 'https://daily.bandcamp.com/best-punk', - name: 'Best Punk' }, - { url: 'https://daily.bandcamp.com/best-reissues', - name: 'Best Reissues' }, - { url: 'https://daily.bandcamp.com/best-soul', - name: 'Best Soul' } ] } ] }, - { name: 'franchises', - title: 'Franchises', - categories: - [ { url: 'https://daily.bandcamp.com/lists', name: 'Lists' }, - { url: 'https://daily.bandcamp.com/features', name: 'Features' }, - { url: 'https://daily.bandcamp.com/album-of-the-day', - name: 'Album of the Day' }, - { url: 'https://daily.bandcamp.com/acid-test', - name: 'Acid Test' }, - { url: 'https://daily.bandcamp.com/bandcamp-navigator', - name: 'Bandcamp Navigator' }, - { url: 'https://daily.bandcamp.com/big-ups', name: 'Big Ups' }, - { url: 'https://daily.bandcamp.com/certified', - name: 'Certified' }, - { url: 'https://daily.bandcamp.com/gallery', name: 'Gallery' }, - { url: 'https://daily.bandcamp.com/hidden-gems', - name: 'Hidden Gems' }, - { url: 'https://daily.bandcamp.com/high-scores', - name: 'High Scores' }, - { url: 'https://daily.bandcamp.com/label-profile', - name: 'Label Profile' }, - { url: 'https://daily.bandcamp.com/lifetime-achievement', - name: 'Lifetime Achievement' }, - { url: 'https://daily.bandcamp.com/resonance', - name: 'Resonance' }, - { url: 'https://daily.bandcamp.com/scene-report', - name: 'Scene Report' }, - { url: 'https://daily.bandcamp.com/seven-essential-releases', - name: 'Seven Essential Releases' }, - { url: 'https://daily.bandcamp.com/shortlist', - name: 'Shortlist' }, - { url: 'https://daily.bandcamp.com/the-merch-table', - name: 'The Merch Table' } ] }, - { name: 'genres', - title: 'Genres', - categories: - [ { url: 'https://daily.bandcamp.com/genres/acoustic', - name: 'Acoustic' }, - { url: 'https://daily.bandcamp.com/genres/alternative', - name: 'Alternative' }, - { url: 'https://daily.bandcamp.com/genres/ambient', - name: 'Ambient' }, - { url: 'https://daily.bandcamp.com/genres/blues', - name: 'Blues' }, - { url: 'https://daily.bandcamp.com/genres/classical', - name: 'Classical' }, - { url: 'https://daily.bandcamp.com/genres/comedy', - name: 'Comedy' }, - { url: 'https://daily.bandcamp.com/genres/country', - name: 'Country' }, - { url: 'https://daily.bandcamp.com/genres/devotional', - name: 'Devotional' }, - { url: 'https://daily.bandcamp.com/genres/electronic', - name: 'Electronic' }, - { url: 'https://daily.bandcamp.com/genres/experimental', - name: 'Experimental' }, - { url: 'https://daily.bandcamp.com/genres/folk', name: 'Folk' }, - { url: 'https://daily.bandcamp.com/genres/funk', name: 'Funk' }, - { url: 'https://daily.bandcamp.com/genres/hip-hop-rap', - name: 'Hip-Hop/Rap' }, - { url: 'https://daily.bandcamp.com/genres/jazz', name: 'Jazz' }, - { url: 'https://daily.bandcamp.com/genres/kids', name: 'Kids' }, - { url: 'https://daily.bandcamp.com/genres/latin', - name: 'Latin' }, - { url: 'https://daily.bandcamp.com/genres/metal', - name: 'Metal' }, - { url: 'https://daily.bandcamp.com/genres/pop', name: 'Pop' }, - { url: 'https://daily.bandcamp.com/genres/punk', name: 'Punk' }, - { url: 'https://daily.bandcamp.com/genres/r-b-soul', - name: 'R&B/Soul' }, - { url: 'https://daily.bandcamp.com/genres/reggae', - name: 'Reggae' }, - { url: 'https://daily.bandcamp.com/genres/rock', name: 'Rock' }, - { url: 'https://daily.bandcamp.com/genres/soundtrack', - name: 'Soundtrack' }, - { url: 'https://daily.bandcamp.com/genres/spoken-word', - name: 'Spoken Word' }, - { url: 'https://daily.bandcamp.com/genres/world', - name: 'World' }, - { url: 'https://daily.bandcamp.com/genres/podcasts', - name: 'Podcasts' } ] } ] diff --git a/examples/getArticleList.js b/examples/getArticleList.js deleted file mode 100644 index 3158438..0000000 --- a/examples/getArticleList.js +++ /dev/null @@ -1,11 +0,0 @@ -const bcfetch = require('../'); -const util = require('util'); - -const params = { - categoryUrl: 'https://daily.bandcamp.com/best-ambient', - page: 2 -} - -bcfetch.getArticleList(params).then( results => { - console.log(util.inspect(results, false, null, false)); -}); \ No newline at end of file diff --git a/examples/getArticleList_output.txt b/examples/getArticleList_output.txt deleted file mode 100644 index adb4dfe..0000000 --- a/examples/getArticleList_output.txt +++ /dev/null @@ -1,95 +0,0 @@ -{ articles: - [ { url: 'https://daily.bandcamp.com/best-ambient/the-best-new-ambient-music-on-bandcamp-november-2018', - title: 'The Best New Ambient Music on Bandcamp, November 2018', - date: 'November 21, 2018', - imageUrl: 'https://f4.bcbits.com/img/0017911101_150.jpg', - category: - { url: 'https://daily.bandcamp.com/best-ambient', - name: 'BEST AMBIENT' } }, - { url: 'https://daily.bandcamp.com/best-ambient/best-ambient-october-2018', - title: 'The Best New Ambient Music on Bandcamp, October 2018', - date: 'October 18, 2018', - imageUrl: 'https://f4.bcbits.com/img/0017911002_150.jpg', - category: - { url: 'https://daily.bandcamp.com/best-ambient', - name: 'BEST AMBIENT' } }, - { url: 'https://daily.bandcamp.com/best-ambient/the-best-new-ambient-music-on-bandcamp-september-2018', - title: 'The Best New Ambient Music on Bandcamp, September 2018', - date: 'October 05, 2018', - imageUrl: 'https://f4.bcbits.com/img/0017910962_150.jpg', - category: - { url: 'https://daily.bandcamp.com/best-ambient', - name: 'BEST AMBIENT' } }, - { url: 'https://daily.bandcamp.com/best-ambient/the-best-new-ambient-music-on-bandcamp-july-2018', - title: 'The Best New Ambient Music on Bandcamp, July 2018', - date: 'August 01, 2018', - imageUrl: 'https://f4.bcbits.com/img/0017910788_150.jpg', - category: - { url: 'https://daily.bandcamp.com/best-ambient', - name: 'BEST AMBIENT' } }, - { url: 'https://daily.bandcamp.com/best-ambient/best-new-ambient-june-2018', - title: 'The Best New Ambient Music on Bandcamp, June 2018', - date: 'June 22, 2018', - imageUrl: 'https://f4.bcbits.com/img/0017910685_150.jpg', - category: - { url: 'https://daily.bandcamp.com/best-ambient', - name: 'BEST AMBIENT' } }, - { url: 'https://daily.bandcamp.com/best-ambient/best-new-ambient-may-2018', - title: 'The Best New Ambient Music on Bandcamp, May 2018', - date: 'May 23, 2018', - imageUrl: 'https://f4.bcbits.com/img/0017910601_150.jpg', - category: - { url: 'https://daily.bandcamp.com/best-ambient', - name: 'BEST AMBIENT' } }, - { url: 'https://daily.bandcamp.com/best-ambient/best-ambient-april-2018', - title: 'The Best New Ambient Music on Bandcamp, April 2018', - date: 'April 23, 2018', - imageUrl: 'https://f4.bcbits.com/img/0017912162_150.jpg', - category: - { url: 'https://daily.bandcamp.com/best-ambient', - name: 'BEST AMBIENT' } }, - { url: 'https://daily.bandcamp.com/best-ambient/best-new-ambient-march-2018', - title: 'The Best New Ambient Music on Bandcamp, March 2018', - date: 'March 27, 2018', - imageUrl: 'https://f4.bcbits.com/img/0017912164_150.jpg', - category: - { url: 'https://daily.bandcamp.com/best-ambient', - name: 'BEST AMBIENT' } }, - { url: 'https://daily.bandcamp.com/best-ambient/best-ambient-february-2018', - title: 'The Best New Ambient Music on Bandcamp, February 2018', - date: 'February 27, 2018', - imageUrl: 'https://f4.bcbits.com/img/0017910366_150.jpg', - category: - { url: 'https://daily.bandcamp.com/best-ambient', - name: 'BEST AMBIENT' } }, - { url: 'https://daily.bandcamp.com/best-ambient/the-best-new-ambient-january-2018', - title: 'The Best New Ambient Music on Bandcamp, January 2018', - date: 'January 24, 2018', - imageUrl: 'https://f4.bcbits.com/img/0017910284_150.jpg', - category: - { url: 'https://daily.bandcamp.com/best-ambient', - name: 'BEST AMBIENT' } }, - { url: 'https://daily.bandcamp.com/best-ambient/best-new-ambient-november-2017', - title: 'The Best New Ambient Music on Bandcamp, October/November 2017', - date: 'November 29, 2017', - imageUrl: 'https://f4.bcbits.com/img/0017910161_150.jpg', - category: - { url: 'https://daily.bandcamp.com/best-ambient', - name: 'BEST AMBIENT' } }, - { url: 'https://daily.bandcamp.com/best-ambient/best-new-ambient-september-2017', - title: 'The Best New Ambient Music on Bandcamp, September 2017', - date: 'September 28, 2017', - imageUrl: 'https://f4.bcbits.com/img/0011401744_150.jpg', - category: - { url: 'https://daily.bandcamp.com/best-ambient', - name: 'BEST AMBIENT' } }, - { url: 'https://daily.bandcamp.com/best-ambient/best-new-ambient-june-july-2017', - title: 'The Best New Ambient Music on Bandcamp, August 2017', - date: 'August 17, 2017', - imageUrl: 'https://f4.bcbits.com/img/0011070305_150.jpg', - category: - { url: 'https://daily.bandcamp.com/best-ambient', - name: 'BEST AMBIENT' } } ], - total: 43, - start: 31, - end: 43 } diff --git a/examples/getArticle_output.txt b/examples/getArticle_output.txt deleted file mode 100644 index a932a0a..0000000 --- a/examples/getArticle_output.txt +++ /dev/null @@ -1,361 +0,0 @@ -{ title: 'The Best New Ambient Music on Bandcamp, March 2018', - description: 'Music for snow days, reissued ‘80s Japanese ambient, and themes of loss and emptiness in this month’s roundup.', - url: 'https://daily.bandcamp.com/best-ambient/best-new-ambient-march-2018', - imageUrl: 'https://f4.bcbits.com/img/0017912164_0', - date: '2018-03-27T11:03:30Z', - category: - { name: 'Best Ambient', - url: 'https://daily.bandcamp.com/best-ambient' }, - genre: - { name: 'Ambient', - url: 'https://bandcamp.com/tag/ambient', - readMoreUrl: 'https://daily.bandcamp.com/genres/ambient' }, - author: - { name: 'Aurora Mitchell', - url: 'https://daily.bandcamp.com/contributors/aurora-mitchell' }, - mediaItems: - [ { type: 'album', - name: 'Aarde', - url: 'https://somta.bandcamp.com/album/aarde', - imageUrl: 'https://f4.bcbits.com/img/a2731445950_9.jpg', - featuredTrackPosition: 3, - artist: - { name: 'somta', - url: 'https://somta.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/8414332_21.jpg', - location: 'Leeds, UK' }, - tracks: - [ { position: 1, - name: 'Aiolus', - duration: 42, - streamUrl: 'https://t4.bcbits.com/stream/bba2dc752e4f02070f3b40b72abfdfda/mp3-128/3588455251?p=0&ts=1634714784&t=ce6692ba4c63c4e09d1f1562c9724a8ba6e5e1c5&token=1634714784_e5ba3de8f453d10a1a84a65b5ce325eef5ea0f07' }, - { position: 2, - name: 'Sophia', - duration: 343.521, - streamUrl: 'https://t4.bcbits.com/stream/5e904efaf5962cc83f8ed35cc49b6850/mp3-128/2347995441?p=0&ts=1634714784&t=52a3dff87f5f9f63a637b85af720f4ddd1a80469&token=1634714784_dde4c1a30bc15a8a7bfd6702aabd234425e56095' }, - { position: 3, - name: 'Khiōn', - duration: 226.396, - streamUrl: 'https://t4.bcbits.com/stream/4dcfdbfcecdccde6260a12fe37bb4277/mp3-128/2272710569?p=0&ts=1634714784&t=03caeb9d4d87af8afa5e48ad74b0c3b06b3ae84c&token=1634714784_25039557580a851166ae03f2e461b75dd929f132' }, - { position: 4, - name: 'Noos', - duration: 388.5, - streamUrl: 'https://t4.bcbits.com/stream/503e4b6e9d762fd172e36c33a0a5de57/mp3-128/3685166023?p=0&ts=1634714784&t=b4ba5c0d252a84e170d0d4173992118734a30eee&token=1634714784_440388f25d762bf38d321ad9b93f443eff138248' }, - { position: 5, - name: 'Trogo', - duration: 410.083, - streamUrl: 'https://t4.bcbits.com/stream/599b32fbe8b66002676cdaf77d3b6b06/mp3-128/717296085?p=0&ts=1634714784&t=158bb210996c5a4d6022d05ec6b132a96b36ac74&token=1634714784_bcf4bdf557bc8903cefbc480daf0d93868859ca4' }, - { position: 6, - name: 'Tuptō', - duration: 254.029, - streamUrl: 'https://t4.bcbits.com/stream/507d08c0c8edb321660d6305d1b13c5f/mp3-128/3228890418?p=0&ts=1634714784&t=50a2095b8d445ead60a702d355be3c22432a1195&token=1634714784_c180b78b6b7050245bf4d5b4f57dad4a243ce2a9' }, - { position: 7, - name: 'Onos', - duration: 98.2833, - streamUrl: 'https://t4.bcbits.com/stream/a0b6e9b5d2434bbcd74d6e99ddc2965e/mp3-128/3097234822?p=0&ts=1634714784&t=916134936c4c15ebbed0b4e863f9adb93c67d39b&token=1634714784_1323aeeeaf79a9ecc2b553fdbb75503dd532fe61' }, - { position: 8, - name: 'Skia', - duration: 140.104, - streamUrl: 'https://t4.bcbits.com/stream/f5c6320bf16ef3fb8ed3a54121898866/mp3-128/2952107736?p=0&ts=1634714784&t=79fc49fb6a20916f39b9eab30d53c13102b7b222&token=1634714784_97de8d1aa357acf3b99b51cc0f5284e25ac7634b' }, - { position: 9, - name: 'Hulē', - duration: 183.999, - streamUrl: 'https://t4.bcbits.com/stream/a61bb342e975ab95117826eb15dbea76/mp3-128/743360394?p=0&ts=1634714784&t=a88b66a6cc60c18641e3f4c8122f5215843fd6c8&token=1634714784_c0c9d2611a924b3a619e608ac1c3dbd76b9a1b98' }, - { position: 10, - name: 'Ganos', - duration: 423, - streamUrl: 'https://t4.bcbits.com/stream/2a200eed7fd2b130b64dcf0bdc6783c5/mp3-128/3511494308?p=0&ts=1634714784&t=fbe312c7761650d1955bd5410d4e5133468d51c2&token=1634714784_6a7159e56b625c9533a3c55df52e42cb4763d58c' }, - { position: 11, - name: 'Polos', - duration: 414, - streamUrl: 'https://t4.bcbits.com/stream/87ba803b563488675ee32fd7f281a70a/mp3-128/3723435045?p=0&ts=1634714784&t=2a3263d04d69b67b8e105573cf27e6aa8f7f2e90&token=1634714784_6c2e01e78ac430c7f828b73a424475fff201639f' }, - { position: 12, - name: 'Zophos', - duration: 91.0833, - streamUrl: 'https://t4.bcbits.com/stream/9ae9dc4469ca3b328ef96b7775589eaa/mp3-128/1373210950?p=0&ts=1634714784&t=7ed647d59a140c2e2e858dd3d69eb4261db618e1&token=1634714784_32b23ffb17c4ced2276ccbb65b05e171bc631ef7' }, - { position: 13, - name: 'Kruos', - duration: 308.729, - streamUrl: 'https://t4.bcbits.com/stream/85ac0ff689dcd7112a431d096d9d11c0/mp3-128/3200879908?p=0&ts=1634714784&t=9019a8d7ad9c4a0093d147f9c76c5014222fc1f3&token=1634714784_0bf38e052d67ab02ff763f83a8f86b685f0d58f5' }, - { position: 14, - name: 'Phōs', - duration: 188.271, - streamUrl: 'https://t4.bcbits.com/stream/bcdde2e8a7d8adb0d140804b9fbef632/mp3-128/575691204?p=0&ts=1634714784&t=ee274fb84b6add8df363c4eb64f250631a510b45&token=1634714784_d4cc7d1eb47cdbcfbe9d07658754ec4b489c5ca2' }, - { position: 15, - name: 'Arktos', - duration: 188, - streamUrl: 'https://t4.bcbits.com/stream/d0016651f3368112bed9367294db5c45/mp3-128/2863705519?p=0&ts=1634714784&t=0b48274e539272fba81f1051889a20999b89a330&token=1634714784_9730cd46268a9e46c1f9c0aeb5fa11951b3dbd0e' } ], - mediaItemRef: 't2272710569' }, - { type: 'album', - name: 'music for snow days', - url: 'https://damecook.bandcamp.com/album/music-for-snow-days', - imageUrl: 'https://f4.bcbits.com/img/a2627798194_9.jpg', - featuredTrackPosition: 1, - artist: - { name: 'Dame Cook', - url: 'https://damecook.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/13825272_21.jpg', - location: 'Toronto, Ontario' }, - tracks: - [ { position: 1, - name: 'glacial drift', - duration: 165.517, - streamUrl: 'https://t4.bcbits.com/stream/8fe55af7408165869c29c1dacd3438ef/mp3-128/1986717137?p=0&ts=1634714784&t=f4aa293ca245df2a0251e317f8f393522067a20b&token=1634714784_69eb8ec7e4ac4cfb97488caf928f025939672fcc' }, - { position: 2, - name: 'frosted panes', - duration: 128, - streamUrl: 'https://t4.bcbits.com/stream/0c79a983698347a100b5927208b7de6b/mp3-128/1139371646?p=0&ts=1634714784&t=49c71970c8844657c95e02e98d9b5f538243994f&token=1634714784_e2110207f53b960ad14f89417c716ddcdb5743ee' }, - { position: 3, - name: 'under the glass', - duration: 180, - streamUrl: 'https://t4.bcbits.com/stream/8fbcc9f3bdf66b067fa92f479840289f/mp3-128/2086342140?p=0&ts=1634714784&t=f1c08cbe9f273e3c5d9e73b50960090589c5694a&token=1634714784_b3c4bae0c4bd81f7acca700abfc37aac3778ec54' }, - { position: 4, - name: 'alpine train track', - duration: 188.723, - streamUrl: 'https://t4.bcbits.com/stream/c3fe330ef0d7081bcfdb1861746ce799/mp3-128/3789770489?p=0&ts=1634714784&t=86d6378457ae598b1a9b22116d76bc01753b5708&token=1634714784_c7f7f8050602c57dfbff1e908f79fef33cc56c96' } ], - mediaItemRef: 'a454040844' }, - { type: 'album', - name: 'Music for Nine Post Cards', - url: 'https://hiroshiyoshimura.bandcamp.com/album/music-for-nine-post-cards', - imageUrl: 'https://f4.bcbits.com/img/a1329705853_9.jpg', - featuredTrackPosition: 1, - artist: - { name: 'Hiroshi Yoshimura', - url: 'https://hiroshiyoshimura.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/19000214_21.jpg', - location: 'Japan' }, - tracks: - [ { position: 1, - name: 'Water Copy', - duration: 371.867, - streamUrl: 'https://t4.bcbits.com/stream/9ccc3a98463b14603e64163f367e40c5/mp3-128/2862079336?p=0&ts=1634714784&t=e81b8207d387089a7f9250fbb3d2110ea67a4f60&token=1634714784_337fe395a5944dff4a8d4ae1effb93b46b6962ca' }, - { position: 2, - name: 'Clouds', - duration: 354.693, - streamUrl: 'https://t4.bcbits.com/stream/0479a3b6fb0905034221425f3bcada0a/mp3-128/1445700587?p=0&ts=1634714784&t=c443596fbeef331e3feb4a9b4504e4e39c10b927&token=1634714784_d40b2dd6a0e31ac887a17c27a2ee0401992fe1d3' }, - { position: 3, - name: 'Blink', - duration: 282.013, - streamUrl: 'https://t4.bcbits.com/stream/0dbe234ebedd6ddd469b09a05ef317c0/mp3-128/3323651202?p=0&ts=1634714784&t=0e222620c8a40860b790d21650b247380d45edd9&token=1634714784_7351d9ac52f654384c93236b71461f4bccd25ee9' }, - { position: 4, - name: 'Dance PM', - duration: 392.32, - streamUrl: 'https://t4.bcbits.com/stream/07aae56284ad785d1bd1a5b8373dae6a/mp3-128/1063072521?p=0&ts=1634714784&t=400bd281dd01f454a7dd456b0aa136e607aefdfc&token=1634714784_c346287ba3da014bd6a8f34ce00edfceea02d15c' }, - { position: 5, - name: 'Ice Copy', - duration: 175.773, - streamUrl: 'https://t4.bcbits.com/stream/f496d40c5475e90e3ddd5fb07373211c/mp3-128/2066333379?p=0&ts=1634714784&t=d6c147303b6c577ae43863c501f47305a8bab662&token=1634714784_7b36633157be28f708e963b4673f71de06659242' }, - { position: 6, - name: 'Soto Wa Ame - Rain Out Of Window', - duration: 276.707, - streamUrl: 'https://t4.bcbits.com/stream/7279637e86f60e8f24815a41908cfe7b/mp3-128/3414351773?p=0&ts=1634714784&t=653a1695f48ec24547eff0b475c8c34e3a5169f5&token=1634714784_5741f74b2d0dbfdaa480aee5079baa665a9c8e81' }, - { position: 7, - name: 'View From My Window', - duration: 375.6, - streamUrl: 'https://t4.bcbits.com/stream/2caffd8268ef49a7d174ee3f8ce67ae9/mp3-128/546874861?p=0&ts=1634714784&t=3d8641ba7958e9c66d03a2fb7ca3db11342c2d27&token=1634714784_cb527d125f8d46a630a70ef491b9ae7ae2d8197f' }, - { position: 8, - name: 'Urban Snow', - duration: 285.773, - streamUrl: 'https://t4.bcbits.com/stream/9c2fa2599479ac941b7a2cd2a646c458/mp3-128/2047145341?p=0&ts=1634714784&t=8c2aed316a9a9f68f84671701bab5049e8b6605b&token=1634714784_c2bb65b19c380e068f794a0ac4a2fa74400723df' }, - { position: 9, - name: 'Dream', - duration: 334.96, - streamUrl: 'https://t4.bcbits.com/stream/59654dbc1a3856016056ecb713de2279/mp3-128/1844238301?p=0&ts=1634714784&t=c502bd7f228914716f457ada13f90e7b7ec92352&token=1634714784_24912f7f0a9125f49a80615e226b01e8de6152e8' } ], - mediaItemRef: 'a3834281211' }, - { type: 'album', - name: 'The Greys', - url: 'https://ilurecords.bandcamp.com/album/the-greys', - imageUrl: 'https://f4.bcbits.com/img/a2451461387_9.jpg', - featuredTrackPosition: 1, - artist: - { name: 'I Low You Records', - url: 'https://ilurecords.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/22708015_21.jpg', - location: 'Tokyo, Japan' }, - tracks: - [ { position: 1, - name: 'Don\'t Remember a Thing', - duration: 71.1318, - streamUrl: 'https://t4.bcbits.com/stream/9a1d8da36f4c48850e47ad5766fad27c/mp3-128/1849746451?p=0&ts=1634714784&t=4595f130a54912f10053d8c6229c1dd99f0ed143&token=1634714784_63634b3295e54ce6c1277278b48e19e67c61c1a3' }, - { position: 2, - name: 'Analog Man', - duration: 45.2042, - streamUrl: 'https://t4.bcbits.com/stream/57c460c29443951cd10cd0d908b0a394/mp3-128/1737948375?p=0&ts=1634714784&t=88682c3735a7afa71e9c73a82726b3ade5dc2bb7&token=1634714784_2e6e13bd7c5a563772bc27987cb0072bf015f00c' }, - { position: 3, - name: 'Coughing Death', - duration: 136.558, - streamUrl: 'https://t4.bcbits.com/stream/054ff3c71138117e95d9d2f442c7ba2f/mp3-128/3268827724?p=0&ts=1634714784&t=9758347eec513f30d8f8333057dc2475508f784e&token=1634714784_0e563e8924a8f70b8324ee7fb066837fc369ef43' }, - { position: 4, - name: 'Don\'t Go in the Woods Alone', - duration: 48.7698, - streamUrl: 'https://t4.bcbits.com/stream/b67e617c74892efde0bd9579d364e473/mp3-128/3557485835?p=0&ts=1634714784&t=a94be02d2e6e365412772bb725727d44503db147&token=1634714784_612044221e0df6805a06b08c4e3bd552526c8206' }, - { position: 5, - name: 'Fairy_Fungi', - duration: 148.588, - streamUrl: 'https://t4.bcbits.com/stream/bb6759be8fe535515d3d1e8a92cd8bcf/mp3-128/1079329015?p=0&ts=1634714784&t=231d9f950b4b67839118a94a8512811033f23a89&token=1634714784_96de14e045b47fb20991fdd8e9670a8dbf7bdfd8' }, - { position: 6, - name: 'Gray Ghosts', - duration: 56.1875, - streamUrl: 'https://t4.bcbits.com/stream/f949c20042a6d42671f587f761095503/mp3-128/2836999478?p=0&ts=1634714784&t=f4612c2c82374265cbdcf16f965f990739176520&token=1634714784_d2dec57fb0a686da9eaa91e8ae9727940b901645' }, - { position: 7, - name: 'Lost', - duration: 31.1641, - streamUrl: 'https://t4.bcbits.com/stream/4f2bbf6289460c0d1108f79b9dc49c11/mp3-128/1187225203?p=0&ts=1634714784&t=8ece7d3d6be596192df9ca07b318747d77a8612e&token=1634714784_84b69279dd6bec54de93ef1df7447f3457c89215' }, - { position: 8, - name: 'Melted Snow', - duration: 116.221, - streamUrl: 'https://t4.bcbits.com/stream/4ec3e010ad2a46e6b8b29f1f3b66f545/mp3-128/2296333718?p=0&ts=1634714784&t=0171e745678497a2a7e502bdddde7cb53d18dd76&token=1634714784_75a5529a05a3912f6158a9b7e98802eeda821f55' }, - { position: 9, - name: 'UUmox (vers. II)', - duration: 106.548, - streamUrl: 'https://t4.bcbits.com/stream/41d8070603d9957a54c47aac4b5efdf2/mp3-128/1800262023?p=0&ts=1634714784&t=ee0ac5441fa6519890f5f97205bbd91cb31d6736&token=1634714784_53b502fbe286f025369711efa2ce4d196f5d0ef1' }, - { position: 10, - name: 'White Ghosts', - duration: 93.9719, - streamUrl: 'https://t4.bcbits.com/stream/b2ae77e2ff74d6bac0873c0acf3622a7/mp3-128/1335292332?p=0&ts=1634714784&t=4bb48316139af36e5b09d9ec2867f5fa287a9471&token=1634714784_83b1b32b38afdaf9b68e7ce4364c42a4ee47380d' }, - { position: 11, - name: 'Warp Day', - duration: 33.8125, - streamUrl: 'https://t4.bcbits.com/stream/7d70e3b721d731c859ad39461a8d6351/mp3-128/180645096?p=0&ts=1634714784&t=c2bbd3f075a05009467f9543ff4e611359b5fc6e&token=1634714784_bf10ec6751f2a3c066e737b1db8d8680f93b0761' }, - { position: 12, - name: 'Old M.m.', - duration: 152.208, - streamUrl: 'https://t4.bcbits.com/stream/61015621f32f12a4afe23f2619c6d9b0/mp3-128/2286068125?p=0&ts=1634714784&t=f88c9f2777c0666b0e991f14990298091eb6de87&token=1634714784_9c023e71ff57f061cfff942ea1732c4e914bffcb' }, - { position: 13, - name: 'You Can be Lonely', - duration: 101.327, - streamUrl: 'https://t4.bcbits.com/stream/d9d5dd4f031f0a48f97bac056f405253/mp3-128/2325111113?p=0&ts=1634714784&t=040f3155c79860d9eda0d3f493751d86a02ef103&token=1634714784_7e2763d8ba351f81d54408ea2cd2946536473f61' }, - { position: 14, - name: 'Eyelid Smiles', - duration: 57.1423, - streamUrl: 'https://t4.bcbits.com/stream/2268e0c0f289573fb4d9741543f6245f/mp3-128/2054627365?p=0&ts=1634714784&t=a7c6ce61fc13ee1934db6dea608efc6a812702bc&token=1634714784_d52e027b55128d76885220ce0c14e48238306c0b' }, - { position: 15, - name: 'Saying Goodbye', - duration: 70.6005, - streamUrl: 'https://t4.bcbits.com/stream/088e77b99529ab2048218c3da8bf842b/mp3-128/82283410?p=0&ts=1634714784&t=627f42cc711a65183b3a2bf7de455882b2d4c94b&token=1634714784_9a1416b9153770c4af202b6c367f9398eef0cbc5' }, - { position: 16, - name: 'How is Your Day Going', - duration: 49.0354, - streamUrl: 'https://t4.bcbits.com/stream/18d8fc3015352bd76b5478f36dc9d23b/mp3-128/3760493673?p=0&ts=1634714784&t=dfcb5974aa0fb264e8859efd84f0af345e0897c2&token=1634714784_36edeaf1827c8e8e3ba548328cd448454c377a4d' }, - { position: 17, - name: 'Granular Loop', - duration: 66.7917, - streamUrl: 'https://t4.bcbits.com/stream/53db5fe0b0f85c17afb2d730c8ad993f/mp3-128/1780688429?p=0&ts=1634714784&t=8be88d05fb4c1a70cc7912bbf7590c7b89a68109&token=1634714784_0b6e8f748001d95d07e78d0df87a6d939e2a213b' }, - { position: 18, - name: 'Shadow Faces', - duration: 90.4234, - streamUrl: 'https://t4.bcbits.com/stream/4fcd1b502498b33194683f099565e754/mp3-128/4275310931?p=0&ts=1634714784&t=f22a7118000f64c933269167626531989a756e7e&token=1634714784_341d13cc0edbac1eeb7af34aac6b519493fb485b' }, - { position: 19, - name: 'The Greys', - duration: 67.9607, - streamUrl: 'https://t4.bcbits.com/stream/199e8a672421d7429b2fb1673917c0b1/mp3-128/3364736689?p=0&ts=1634714784&t=f768c2244d1f494f0639c7d61b83950c47e05ed3&token=1634714784_931a6e1a068db703b9da3c7da17af257c9b82da2' }, - { position: 20, - name: 'Fragmented Loop', - duration: 24.0135, - streamUrl: 'https://t4.bcbits.com/stream/d0345a94b9fc6ceb4f895fd5fc6fc1b6/mp3-128/3121980470?p=0&ts=1634714784&t=d0fd1961a302d8d60e93cba811ac3810d3becbd2&token=1634714784_1c5d7136eed222b1096ca31e96fb43677433ce66' }, - { position: 21, - name: 'Who Goes There', - duration: 33.0828, - streamUrl: 'https://t4.bcbits.com/stream/e65fbe773c8a05cce5fe657dc28b76c8/mp3-128/411868333?p=0&ts=1634714784&t=df6b49e1bd008119dd8768cc1321efbe56e0172d&token=1634714784_832b787ba7c6a1c1d459311a506f15746dbcb9e5' }, - { position: 22, - name: 'Two Lost Years', - duration: 568.698, - streamUrl: 'https://t4.bcbits.com/stream/7467ea7fd5fb544c20b50a561928f9da/mp3-128/2821042097?p=0&ts=1634714784&t=2923ec18e9497387b5ff8013100221a4b87498c2&token=1634714784_dbc08659067259fd03e750169ab8a803578ed81f' }, - { position: 23, - name: 'What is Song', - duration: 21.6526, - streamUrl: 'https://t4.bcbits.com/stream/4772122bb7d1c83daf16300738af3553/mp3-128/617262569?p=0&ts=1634714784&t=a401499c8af9b6532177838a553775938722f89d&token=1634714784_49a4a7b896795b5c9db83c466493f97ca62a2da5' } ], - mediaItemRef: 'a612750820' }, - { type: 'album', - name: 'Cold Ubiquity', - url: 'https://lahautdanslocean.bandcamp.com/album/cold-ubiquity', - imageUrl: 'https://f4.bcbits.com/img/a837598593_9.jpg', - featuredTrackPosition: 1, - artist: - { name: 'Là-haut Dans L\'Océan', - url: 'https://lahautdanslocean.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/12384834_21.jpg', - location: 'Grenoble, France' }, - tracks: - [ { position: 1, - name: 'Tentation d\'Ubiquité', - duration: 523.234, - streamUrl: 'https://t4.bcbits.com/stream/dd36a520ba2cb3a40b9e8e8abe85503a/mp3-128/130351574?p=0&ts=1634714784&t=88b61bdca04ac741475bf0c469f2041f286a4797&token=1634714784_6e34a9246c4a500427602ca398c19d24de8c16fa' }, - { position: 2, - name: 'Ambifidus', - duration: 243.438, - streamUrl: 'https://t4.bcbits.com/stream/86de9fd0403b65068f193ef61188472b/mp3-128/1707109576?p=0&ts=1634714784&t=b0a0090ebb81316272805654033cc54e609ef33a&token=1634714784_b42d6702cfab164522d5b0c1a10c03f5e39a79f1' }, - { position: 3, - name: 'Snow Bird', - duration: 248.527, - streamUrl: 'https://t4.bcbits.com/stream/e2af6f01452e374ddf48e7c46bb41489/mp3-128/136062705?p=0&ts=1634714784&t=f33770252c4970c7ec40cea114257cd3c4ad8e9e&token=1634714784_032381f6162eb4e7ff659d308bea253f1ce24cdf' }, - { position: 4, - name: 'Fake Seagulls and Fulfillment', - duration: 292.912, - streamUrl: 'https://t4.bcbits.com/stream/3ed2e54d7285d00da313befb72ed4c7d/mp3-128/573634003?p=0&ts=1634714784&t=019303364d0c09bfc5bd49d792318440390498bd&token=1634714784_efdfe8b267ea8c0b98a706a74e4e89a6c03dd5c6' }, - { position: 5, - name: 'Birds Coalition For My Apartment Cat', - duration: 340, - streamUrl: 'https://t4.bcbits.com/stream/6508dd8760542d30418021b7802ecb9c/mp3-128/1466403206?p=0&ts=1634714784&t=a9214a65d4f5b251d8510f09da900dfad6c42509&token=1634714784_2b71ab1b3f1a1c16aafb86dddecaeeb3c351dead' }, - { position: 6, - name: 'My Heart Play On The Lake', - duration: 429.978, - streamUrl: 'https://t4.bcbits.com/stream/6e3eb7b756bdf444930ffa576717fe5f/mp3-128/2764710200?p=0&ts=1634714784&t=490a92b6741a7cfb2f17af5e5bf039a0736571d2&token=1634714784_42564c635809cac3b8fd06f6a172beddbbe5f075' }, - { position: 7, - name: 'Riley Net Data', - duration: 232.441, - streamUrl: 'https://t4.bcbits.com/stream/ae9892a7be6390f426f787cf5ef0140b/mp3-128/3158881070?p=0&ts=1634714784&t=04306b3d2f1cdfdb3bd1733697fd15cb30a87536&token=1634714784_065607890611084e78458b703f40e9e0c63d84f2' }, - { position: 8, - name: 'Deepor Radiator Swamp', - duration: 327.733, - streamUrl: 'https://t4.bcbits.com/stream/c3a49f2d6b0c8d6bff69367956f0c5f6/mp3-128/3414841372?p=0&ts=1634714784&t=271e4f8a2d4999c63fcdf7e0c782a5965c3eab8e&token=1634714784_c0a176a5a1f17e04a009d6472aac72d1bf46beed' }, - { position: 9, - name: 'Far Cold Away', - duration: 98.5972, - streamUrl: 'https://t4.bcbits.com/stream/57d74642b683fcd05ba062fc908045b9/mp3-128/1563033969?p=0&ts=1634714784&t=868ac8416ea416c6895fe1181aa44f91ccc4cca8&token=1634714784_c5e3642f5f7e93c0650c48f4a55ab626b6e6cf38' }, - { position: 10, - name: 'Muages', - duration: 304.83, - streamUrl: 'https://t4.bcbits.com/stream/5108a71efc1eb4aeecbad4a18ea595e4/mp3-128/2851363235?p=0&ts=1634714784&t=32d8402366a601873da577a7247fb5e777407359&token=1634714784_42b9477c57b44560dc9158d04c2c3f0a16f45abb' }, - { position: 11, - name: 'Thunder The Ocean', - duration: 436.336, - streamUrl: 'https://t4.bcbits.com/stream/aa7c27bf7f052098de0535adf3604aba/mp3-128/1650176642?p=0&ts=1634714784&t=4e9acf52fab6aa87b25b77355a69996aff7f49bf&token=1634714784_905e7527dc3a588e26182a280676e1845fc42dd6' }, - { position: 12, - name: 'Mermaids', - duration: 352.662, - streamUrl: 'https://t4.bcbits.com/stream/c4b8764c805124986b4f97e49f96af13/mp3-128/104707931?p=0&ts=1634714784&t=ba238ab3d307a82ccef8ae96f951fe2ee9e5fd45&token=1634714784_f29d2cfeb9f10cb2b3694263a9e1cda7d5cbf46c' } ], - mediaItemRef: 'a42342034' } ], - sections: - [ { html: 'There are infinite atmospheric worlds available on Bandcamp. From the dreamy to the nightmarish, and the meditative to the deeply unsettling, these compositions often fall under the ambient umbrella. Albums without words that are deeply thoughtful and thought-provoking in their own way—these releases come from all corners of the globe. Every month, Aurora Mitchell will take you through the best ambient releases. In this edition, there’s music for snow days, reissued ‘80s Japanese ambient, and themes of loss and emptiness.', - text: 'There are infinite atmospheric worlds available on Bandcamp. From the dreamy to the nightmarish, and the meditative to the deeply unsettling, these compositions often fall under the ambient umbrella. Albums without words that are deeply thoughtful and thought-provoking in their own way—these releases come from all corners of the globe. Every month, Aurora Mitchell will take you through the best ambient releases. In this edition, there’s music for snow days, reissued ‘80s Japanese ambient, and themes of loss and emptiness.' }, - { heading: - { html: 'Somta
Aarde\n', - text: 'Somta\nAarde' }, - html: 'Last year, Leeds-based producer Somta released their first album, Surface to air, a dark and introspective offering. Exactly one year later, they return with their follow-up, Aarde. It starts off in blistering fashion, with bizarre voices echoing through a whipped-up wind: Every star is just a sail in the brain of the universe,they whisper. “Khiōn” is one of the album’s highlights, boasting intergalactic melodies and sliding pads. The gust of noise that blew through Surface to air is present here, but there are more moments of soft elegance, like the pleasant drift of “Ganos” and the shiny, high-pitched waves of “Zophos.” But the album really reaches an apex with “Phōs,” which makes excellent use of space (and cosmic bleeps).', - text: 'Last year, Leeds-based producer Somta released their first album, Surface to air, a dark and introspective offering. Exactly one year later, they return with their follow-up, Aarde. It starts off in blistering fashion, with bizarre voices echoing through a whipped-up wind: “Every star is just a sail in the brain of the universe,” they whisper. “Khiōn” is one of the album’s highlights, boasting intergalactic melodies and sliding pads. The gust of noise that blew through Surface to air is present here, but there are more moments of soft elegance, like the pleasant drift of “Ganos” and the shiny, high-pitched waves of “Zophos.” But the album really reaches an apex with “Phōs,” which makes excellent use of space (and cosmic bleeps).', - mediaItemRef: 't2272710569' }, - { heading: - { html: 'Dame Cook
music for snow days\n', - text: 'Dame Cook\nmusic for snow days' }, - html: 'We may be in early spring, but snow is still wreaking wintry havoc around the world, leaving people housebound while the ground is covered and transport is halted. Ontario’s Dame Cook’s four-track release is designed to soundtrack these days, titled, appropriately enough, music for snow days. While the track titles reference ice and freezing temperatures, the music is neither frosty nor cold; it has the sparkling, dewy atmosphere of a world slowly thawing. Flutes flutter dreamily across the bare bones of the first track, “glacial drift.” The record alternates between the twinkling instrumentation of that song and “under the glass” to the sluggish, meandering charm of “frosted panes” and “alpine train track.”', - text: 'We may be in early spring, but snow is still wreaking wintry havoc around the world, leaving people housebound while the ground is covered and transport is halted. Ontario’s Dame Cook’s four-track release is designed to soundtrack these days, titled, appropriately enough, music for snow days. While the track titles reference ice and freezing temperatures, the music is neither frosty nor cold; it has the sparkling, dewy atmosphere of a world slowly thawing. Flutes flutter dreamily across the bare bones of the first track, “glacial drift.” The record alternates between the twinkling instrumentation of that song and “under the glass” to the sluggish, meandering charm of “frosted panes” and “alpine train track.”', - mediaItemRef: 'a454040844' }, - { heading: - { html: 'Hiroshi Yoshimura
Music For Nine Post Cards\n', - text: 'Hiroshi Yoshimura\nMusic For Nine Post Cards' }, - html: 'A few years after Hiroshi Yoshimura’s death in 2003, his music started slowly finding its way onto popular mixes and playlists, and the demand for his music grew so much that many titles were repressed. His first album, Music For Nine Post Cards, was finally reissued last year, and it proves that few composers capture the elegance of space, the balance of hope and sadness, and the healing power of ambient music quite like Yoshimura does. It’s the perfect soundtrack to drift slowly to sleep to, the silence between each note more poignant in the darkness.', - text: 'A few years after Hiroshi Yoshimura’s death in 2003, his music started slowly finding its way onto popular mixes and playlists, and the demand for his music grew so much that many titles were repressed. His first album, Music For Nine Post Cards, was finally reissued last year, and it proves that few composers capture the elegance of space, the balance of hope and sadness, and the healing power of ambient music quite like Yoshimura does. It’s the perfect soundtrack to drift slowly to sleep to, the silence between each note more poignant in the darkness.', - mediaItemRef: 'a3834281211' }, - { heading: - { html: 'Tape Sounds
The Greys\n', - text: 'Tape Sounds\nThe Greys' }, - html: 'This album by Tape Sounds, on Tokyo-based label I Low You Records, has a despondent tone; The Greys is a collection of loops that center around feelings of loss, emptiness, and blankness. Each loop is two minutes or less, giving only brief snapshots of ideas or sequences before swiftly moving on. Despite the theme, there are a lot of magical, bright sounds on The Greys. It’s a largely guitar-based record, taking simple chords and notes and stretching them with the charm of delay.', - text: 'This album by Tape Sounds, on Tokyo-based label I Low You Records, has a despondent tone; The Greys is a collection of loops that center around feelings of loss, emptiness, and blankness. Each loop is two minutes or less, giving only brief snapshots of ideas or sequences before swiftly moving on. Despite the theme, there are a lot of magical, bright sounds on The Greys. It’s a largely guitar-based record, taking simple chords and notes and stretching them with the charm of delay.', - mediaItemRef: 'a612750820' }, - { heading: - { html: 'Denis Morin
Cold Ubiquity\n', - text: 'Denis Morin\nCold Ubiquity' }, - html: 'Denis Morin’s newest offering for French label Là-haut Dans L’Océan is a lush exploration of birds chirping in echoing circles, watery samples, and shimmering pads. Cold Ubiquity plays with the tones and pitches of a large variety of bird calls. The second half of the record is more synth-based, with pixelated melodies fluttering throughout. Some of the tracks were recorded in a 1400m altitude environment, and the whole record was made to be listened at that altitude “while snow melts in a little stream on the path.”', - text: 'Denis Morin’s newest offering for French label Là-haut Dans L’Océan is a lush exploration of birds chirping in echoing circles, watery samples, and shimmering pads. Cold Ubiquity plays with the tones and pitches of a large variety of bird calls. The second half of the record is more synth-based, with pixelated melodies fluttering throughout. Some of the tracks were recorded in a 1400m altitude environment, and the whole record was made to be listened at that altitude “while snow melts in a little stream on the path.”', - mediaItemRef: 'a42342034' } ] } diff --git a/examples/getArtistOrLabelInfo.js b/examples/getArtistOrLabelInfo.js deleted file mode 100644 index 1fc69e7..0000000 --- a/examples/getArtistOrLabelInfo.js +++ /dev/null @@ -1,21 +0,0 @@ -const bcfetch = require('../'); -const util = require('util'); - -const artistUrl = 'https://macmccaughan.bandcamp.com'; -const labelUrl = 'https://mergerecords.bandcamp.com'; - -const options = { - imageFormat: 'art_app_large', -} - -bcfetch.getArtistOrLabelInfo(artistUrl, Object.assign({ labelId: '1415932000' }, options)).then( results => { - console.log('Artist URL: ' + artistUrl); - console.log(util.inspect(results, false, null, false)); - console.log(); -}); - -bcfetch.getArtistOrLabelInfo(labelUrl, options).then( results => { - console.log('Label URL: ' + labelUrl); - console.log(util.inspect(results, false, null, false)); - console.log(); -}); \ No newline at end of file diff --git a/examples/getDiscography.js b/examples/getDiscography.js deleted file mode 100644 index 165de2e..0000000 --- a/examples/getDiscography.js +++ /dev/null @@ -1,21 +0,0 @@ -const bcfetch = require('../'); -const util = require('util'); - -const artistUrl = 'https://musique.coeurdepirate.com'; -const labelUrl = 'https://randsrecords.bandcamp.com'; - -const options = { - imageFormat: 'art_app_large', -} - -bcfetch.getDiscography(artistUrl, options).then( results => { - console.log('Artist URL: ' + artistUrl); - console.log(util.inspect(results, false, null, false)); - console.log(); -}); - -bcfetch.getDiscography(labelUrl, options).then( results => { - console.log('Label URL: ' + labelUrl); - console.log(util.inspect(results, false, null, false)); - console.log(); -}); \ No newline at end of file diff --git a/examples/getDiscography_output.txt b/examples/getDiscography_output.txt deleted file mode 100644 index 3c8f3cd..0000000 --- a/examples/getDiscography_output.txt +++ /dev/null @@ -1,595 +0,0 @@ -Artist URL: https://musique.coeurdepirate.com -[ { url: 'https://musique.coeurdepirate.com/album/impossible-aimer', - type: 'album', - name: 'Impossible à aimer', - imageUrl: 'https://f4.bcbits.com/img/a3147632514_16.jpg', - artist: 'Cœur de pirate' }, - { url: 'https://musique.coeurdepirate.com/track/on-saimera-toujours-2', - type: 'track', - name: 'On s\'aimera toujours', - imageUrl: 'https://f4.bcbits.com/img/a0599648879_16.jpg', - artist: 'Cœur de pirate' }, - { url: 'https://musique.coeurdepirate.com/track/plan-trois', - type: 'track', - name: 'Plan à trois', - imageUrl: 'https://f4.bcbits.com/img/a4236666126_16.jpg', - artist: 'Cœur de pirate' }, - { url: 'https://musique.coeurdepirate.com/album/pers-ides', - type: 'album', - name: 'Perséides', - imageUrl: 'https://f4.bcbits.com/img/a3751956000_16.jpg', - artist: 'Cœur de pirate' }, - { url: 'https://musique.coeurdepirate.com/track/tes-belle', - type: 'track', - name: 'T\'es belle', - imageUrl: 'https://f4.bcbits.com/img/a0774650359_16.jpg', - artist: 'Cœur de pirate' }, - { url: 'https://musique.coeurdepirate.com/track/ne-mappelle-pas', - type: 'track', - name: 'Ne m\'appelle pas', - imageUrl: 'https://f4.bcbits.com/img/a2603960871_16.jpg', - artist: 'Cœur de pirate' }, - { url: 'https://musique.coeurdepirate.com/album/en-cas-de-temp-te-ce-jardin-sera-ferm', - type: 'album', - name: 'en cas de tempête, ce jardin sera fermé.', - imageUrl: 'https://f4.bcbits.com/img/a0248413645_16.jpg', - artist: 'Cœur de pirate' }, - { url: 'https://musique.coeurdepirate.com/album/somnambule', - type: 'album', - name: 'Somnambule', - imageUrl: 'https://f4.bcbits.com/img/a1903816474_16.jpg', - artist: 'Cœur de pirate' }, - { url: 'https://musique.coeurdepirate.com/album/pr-monition-2', - type: 'album', - name: 'Prémonition', - imageUrl: 'https://f4.bcbits.com/img/a2454271957_16.jpg', - artist: 'Cœur de pirate' }, - { url: 'https://musique.coeurdepirate.com/album/chansons-tristes-pour-no-l', - type: 'album', - name: 'Chansons tristes pour Noël', - imageUrl: 'https://f4.bcbits.com/img/a2476670277_16.jpg', - artist: 'Cœur de pirate' }, - { url: 'https://musique.coeurdepirate.com/album/roses', - type: 'album', - name: 'Roses', - imageUrl: 'https://f4.bcbits.com/img/a3347055233_16.jpg', - artist: 'Cœur de pirate' }, - { url: 'https://musique.coeurdepirate.com/album/carry-on-2', - type: 'album', - name: 'Carry On', - imageUrl: 'https://f4.bcbits.com/img/a4277887982_16.jpg', - artist: 'Cœur de pirate' }, - { url: 'https://musique.coeurdepirate.com/album/oublie-moi-carry-on', - type: 'album', - name: 'Oublie-moi (Carry On)', - imageUrl: 'https://f4.bcbits.com/img/a0891943451_16.jpg', - artist: 'Cœur de pirate' }, - { url: 'https://musique.coeurdepirate.com/album/child-of-light', - type: 'album', - name: 'Child of Light', - imageUrl: 'https://f4.bcbits.com/img/a3984758353_16.jpg', - artist: 'Cœur de pirate' }, - { url: 'https://musique.coeurdepirate.com/album/trauma', - type: 'album', - name: 'Trauma', - imageUrl: 'https://f4.bcbits.com/img/a3799110102_16.jpg', - artist: 'Cœur de pirate' }, - { url: 'https://musique.coeurdepirate.com/album/blonde', - type: 'album', - name: 'Blonde', - imageUrl: 'https://f4.bcbits.com/img/a1328452291_16.jpg', - artist: 'Cœur de pirate' }, - { url: 'https://musique.coeurdepirate.com/album/comme-des-enfants-version-originale-et-remix-par-le-matos', - type: 'album', - name: 'Comme des enfants (Version originale et remix par Le Matos)', - imageUrl: 'https://f4.bcbits.com/img/a1250129776_16.jpg', - artist: 'Cœur de pirate' }, - { url: 'https://musique.coeurdepirate.com/album/coeur-de-pirate', - type: 'album', - name: 'Coeur de pirate', - imageUrl: 'https://f4.bcbits.com/img/a2201751482_16.jpg', - artist: 'Cœur de pirate' } ] - -Label URL: https://randsrecords.bandcamp.com -[ { url: 'https://loxyink.bandcamp.com/album/manifested-visions', - type: 'album', - name: 'Manifested Visions', - imageUrl: 'https://f4.bcbits.com/img/a0075554190_16.jpg', - artist: 'Loxy & Ink' }, - { url: 'https://bataviacollective.bandcamp.com/album/affirmation-feat-kamga', - type: 'album', - name: 'Affirmation feat. Kamga', - imageUrl: 'https://f4.bcbits.com/img/a0436292227_16.jpg', - artist: 'Batavia Collective' }, - { url: 'https://progedia.bandcamp.com/album/we-are-the-night', - type: 'album', - name: 'We Are The Night', - imageUrl: 'https://f4.bcbits.com/img/a3155966537_16.jpg', - artist: 'PROGedia' }, - { url: 'https://paulwhite.bandcamp.com/album/smile-see-the-light', - type: 'album', - name: 'Smile (See The Light)', - imageUrl: 'https://f4.bcbits.com/img/a0078770650_16.jpg', - artist: 'Paul White feat. Iyamah & Remi' }, - { url: 'https://saminterface.bandcamp.com/album/pink-dolphins-ep', - type: 'album', - name: 'Pink Dolphins EP', - imageUrl: 'https://f4.bcbits.com/img/a0822271284_16.jpg', - artist: 'Sam Interface' }, - { url: 'https://forestdrivewest.bandcamp.com/album/terminus-ep', - type: 'album', - name: 'Terminus EP', - imageUrl: 'https://f4.bcbits.com/img/a3636491375_16.jpg', - artist: 'Forest Drive West' }, - { url: 'https://lone.bandcamp.com/album/lone-x-kettama', - type: 'album', - name: 'Lone x KETTAMA', - imageUrl: 'https://f4.bcbits.com/img/a3844432643_16.jpg', - artist: 'Lone x KETTAMA' }, - { url: 'https://randsrecords.bandcamp.com/album/rv-trax-vol-5', - type: 'album', - name: 'RV Trax, Vol. 5', - imageUrl: 'https://f4.bcbits.com/img/a2129309811_16.jpg', - artist: 'R&S Records' }, - { url: 'https://randsrecords.bandcamp.com/album/in-order-to-care-deleted', - type: 'album', - name: 'In Order To Care (DELETED)', - imageUrl: 'https://f4.bcbits.com/img/a1662919219_16.jpg', - artist: 'R&S Records' }, - { url: 'https://architecturalrecs.bandcamp.com/album/planet-is-but-a-dream', - type: 'album', - name: 'Planet Is But A Dream', - imageUrl: 'https://f4.bcbits.com/img/a2801260386_16.jpg', - artist: 'Architectural' }, - { url: 'https://futurebeatalliance.bandcamp.com/album/never-forever', - type: 'album', - name: 'Never Forever', - imageUrl: 'https://f4.bcbits.com/img/a4088803645_16.jpg', - artist: 'Future Beat Alliance' }, - { url: 'https://yansima.bandcamp.com/album/tweede-cans', - type: 'album', - name: 'Tweede Cans', - imageUrl: 'https://f4.bcbits.com/img/a1336844977_16.jpg', - artist: 'Yansima' }, - { url: 'https://more-time.bandcamp.com/album/r-s-presents-more-time-records-vol-1', - type: 'album', - name: 'R&S presents: More Time Records Vol 1', - imageUrl: 'https://f4.bcbits.com/img/a0703436468_16.jpg', - artist: 'More Time' }, - { url: 'https://spacedimensioncontroller.bandcamp.com/album/love-beyond-the-intersect', - type: 'album', - name: 'Love Beyond The Intersect', - imageUrl: 'https://f4.bcbits.com/img/a4057499431_16.jpg', - artist: 'Space Dimension Controller' }, - { url: 'https://bartaub.bandcamp.com/album/marble-ya-betta-jam', - type: 'album', - name: 'Marble / Ya Betta Jam', - imageUrl: 'https://f4.bcbits.com/img/a1656383403_16.jpg', - artist: 'Bärtaub' }, - { url: 'https://randsrecords.bandcamp.com/album/rv-trax-vol-4', - type: 'album', - name: 'RV Trax Vol 4', - imageUrl: 'https://f4.bcbits.com/img/a2374816883_16.jpg', - artist: 'Various Artists' }, - { url: 'https://afriqua.bandcamp.com/album/colored', - type: 'album', - name: 'Colored', - imageUrl: 'https://f4.bcbits.com/img/a3931309346_16.jpg', - artist: 'Afriqua' }, - { url: 'https://sportinglife.bandcamp.com/album/black-diamond-ep', - type: 'album', - name: 'Black Diamond EP', - imageUrl: 'https://f4.bcbits.com/img/a1752543133_16.jpg', - artist: 'Sporting Life' }, - { url: 'https://djrum.bandcamp.com/album/hard-to-say-tournesol', - type: 'album', - name: 'Hard to Say / Tournesol', - imageUrl: 'https://f4.bcbits.com/img/a1414096599_16.jpg', - artist: 'Djrum' }, - { url: 'https://randsrecords.bandcamp.com/album/neverlasting-love', - type: 'album', - name: 'Neverlasting Love', - imageUrl: 'https://f4.bcbits.com/img/a3334302883_16.jpg', - artist: 'East Of Oceans' }, - { url: 'https://adakaleh.bandcamp.com/album/chemare-cosmic', - type: 'album', - name: 'Chemare cosmică', - imageUrl: 'https://f4.bcbits.com/img/a4225860793_16.jpg', - artist: 'Ada Kaleh' }, - { url: 'https://6siss.bandcamp.com/album/prisma', - type: 'album', - name: 'Prisma', - imageUrl: 'https://f4.bcbits.com/img/a3746802060_16.jpg', - artist: '6SISS' }, - { url: 'https://afriqua.bandcamp.com/album/jumpteenth', - type: 'album', - name: 'Jumpteenth', - imageUrl: 'https://f4.bcbits.com/img/a3540548131_16.jpg', - artist: 'Afriqua' }, - { url: 'https://lostsoulsofsaturn.bandcamp.com/album/lost-souls-of-saturn', - type: 'album', - name: 'Lost Souls Of Saturn', - imageUrl: 'https://f4.bcbits.com/img/a3766159075_16.jpg', - artist: 'Lost Souls Of Saturn' }, - { url: 'https://yaksound.bandcamp.com/album/termina-ep', - type: 'album', - name: 'Termina EP', - imageUrl: 'https://f4.bcbits.com/img/a2700459812_16.jpg', - artist: 'Yak' }, - { url: 'https://lostsoulsofsaturn.bandcamp.com/album/the-awakening-inc-james-holden-remix', - type: 'album', - name: 'The Awakening [inc James Holden remix]', - imageUrl: 'https://f4.bcbits.com/img/a0319633005_16.jpg', - artist: 'Lost Souls Of Saturn' }, - { url: 'https://spacedimensioncontroller.bandcamp.com/album/reseq-ep', - type: 'album', - name: 'ReSEQ EP', - imageUrl: 'https://f4.bcbits.com/img/a2561252353_16.jpg', - artist: 'Space Dimension Controller' }, - { url: 'https://benhayes.bandcamp.com/album/see-sun-ep', - type: 'album', - name: 'See Sun EP', - imageUrl: 'https://f4.bcbits.com/img/a2177273264_16.jpg', - artist: 'Ben Hayes' }, - { url: 'https://lakker.bandcamp.com/album/poca', - type: 'album', - name: 'Época', - imageUrl: 'https://f4.bcbits.com/img/a3481268406_16.jpg', - artist: 'Lakker' }, - { url: 'https://lostsoulsofsaturn.bandcamp.com/album/holes-in-the-holoverse-ep-inc-wolfgang-tillmans-remix', - type: 'album', - name: 'Holes In The Holoverse EP [inc Wolfgang Tillmans remix]', - imageUrl: 'https://f4.bcbits.com/img/a3422639050_16.jpg', - artist: 'Lost Souls Of Saturn' }, - { url: 'https://randsrecords.bandcamp.com/album/rv-trax-vol-3', - type: 'album', - name: 'RV Trax Vol 3', - imageUrl: 'https://f4.bcbits.com/img/a1395065717_16.jpg', - artist: 'R&S Records' }, - { url: 'https://benhayes.bandcamp.com/album/ready-yet-feat-nubya-garcia', - type: 'album', - name: 'Ready Yet (feat Nubya Garcia)', - imageUrl: 'https://f4.bcbits.com/img/a3751006084_16.jpg', - artist: 'Ben Hayes' }, - { url: 'https://shcaa.bandcamp.com/album/an-ungrateful-death', - type: 'album', - name: 'An Ungrateful Death', - imageUrl: 'https://f4.bcbits.com/img/a2877713528_16.jpg', - artist: 'Shcaa' }, - { url: 'https://nicolasjaar.bandcamp.com/album/nymphs-triple-lp', - type: 'album', - name: 'Nymphs - Triple LP', - imageUrl: 'https://f4.bcbits.com/img/a3174238166_16.jpg', - artist: 'Nicolas Jaar' }, - { url: 'https://gabriels.bandcamp.com/album/loyalty', - type: 'album', - name: 'Loyalty', - imageUrl: 'https://f4.bcbits.com/img/a0407526073_16.jpg', - artist: 'Gabriels' }, - { url: 'https://hermetics.bandcamp.com/album/techgnosis-ep', - type: 'album', - name: 'Techgnosis EP', - imageUrl: 'https://f4.bcbits.com/img/a3133044058_16.jpg', - artist: 'Hermetics' }, - { url: 'https://lone.bandcamp.com/album/ambivert-tools-one-four-japan-cd', - type: 'album', - name: 'Ambivert Tools One-Four Japan CD', - imageUrl: 'https://f4.bcbits.com/img/a2292587669_16.jpg', - artist: 'Lone' }, - { url: 'https://marielito.bandcamp.com/album/2000-2005', - type: 'album', - name: '2000-2005', - imageUrl: 'https://f4.bcbits.com/img/a3837740573_16.jpg', - artist: 'Mariel Ito' }, - { url: 'https://paulwhite.bandcamp.com/album/returning-rival-consoles-remix', - type: 'album', - name: 'Returning (Rival Consoles Remix)', - imageUrl: 'https://f4.bcbits.com/img/a3475848676_16.jpg', - artist: 'Paul White' }, - { url: 'https://lone.bandcamp.com/album/ambivert-tools-volume-four', - type: 'album', - name: 'Ambivert Tools Volume Four', - imageUrl: 'https://f4.bcbits.com/img/a2380250227_16.jpg', - artist: 'Lone' }, - { url: 'https://maghreban.bandcamp.com/album/monster-vip', - type: 'album', - name: 'Monster VIP', - imageUrl: 'https://f4.bcbits.com/img/a2159211960_16.jpg', - artist: 'The Maghreban' }, - { url: 'https://djrum.bandcamp.com/album/portrait-with-firewood', - type: 'album', - name: 'Portrait With Firewood', - imageUrl: 'https://f4.bcbits.com/img/a1924727225_16.jpg', - artist: 'Djrum' }, - { url: 'https://randsrecords.bandcamp.com/album/rv-trax-vol-2', - type: 'album', - name: 'RV Trax Vol 2', - imageUrl: 'https://f4.bcbits.com/img/a0843418465_16.jpg', - artist: 'Various Artists' }, - { url: 'https://paulwhite.bandcamp.com/album/ice-cream-man', - type: 'album', - name: 'Ice Cream Man', - imageUrl: 'https://f4.bcbits.com/img/a0488414899_16.jpg', - artist: 'Paul White Feat. Shungudzo' }, - { url: 'https://benjamindamage.bandcamp.com/album/malfunction', - type: 'album', - name: 'Malfunction', - imageUrl: 'https://f4.bcbits.com/img/a1569426334_16.jpg', - artist: 'Benjamin Damage' }, - { url: 'https://afriqua.bandcamp.com/album/vice-principle-ep', - type: 'album', - name: 'Vice/Principle EP', - imageUrl: 'https://f4.bcbits.com/img/a1112113317_16.jpg', - artist: 'Afriqua' }, - { url: 'https://talaboman.bandcamp.com/album/the-night-land-remixed', - type: 'album', - name: 'The Night Land Remixed', - imageUrl: 'https://f4.bcbits.com/img/a1841699442_16.jpg', - artist: 'Talaboman' }, - { url: 'https://paulwhite.bandcamp.com/album/rejuvenate', - type: 'album', - name: 'Rejuvenate', - imageUrl: 'https://f4.bcbits.com/img/a3202037917_16.jpg', - artist: 'Paul White' }, - { url: 'https://maghreban.bandcamp.com/album/revenge-dance-mix', - type: 'album', - name: 'Revenge - Dance Mix', - imageUrl: 'https://f4.bcbits.com/img/a2572455723_16.jpg', - artist: 'The Maghreban feat. Rutendo Machiridza' }, - { url: 'https://karimsahraoui.bandcamp.com/album/plenitude-ep', - type: 'album', - name: 'Plenitude EP', - imageUrl: 'https://f4.bcbits.com/img/a2450943221_16.jpg', - artist: 'Karim Sahraoui' }, - { url: 'https://maghreban.bandcamp.com/album/01deas', - type: 'album', - name: '01DEAS', - imageUrl: 'https://f4.bcbits.com/img/a4016273020_16.jpg', - artist: 'The Maghreban' }, - { url: 'https://blondes.bandcamp.com/album/quality-of-life-struction-remix', - type: 'album', - name: 'Quality Of Life (Struction Remix)', - imageUrl: 'https://f4.bcbits.com/img/a0167516807_16.jpg', - artist: 'Blondes' }, - { url: 'https://music.maartenvandervleuten.com/album/maarten-van-der-vleuten-presents-integrity-outrage', - type: 'album', - name: 'Maarten van der Vleuten Presents Integrity - Outrage', - imageUrl: 'https://f4.bcbits.com/img/a1909170598_16.jpg', - artist: 'Maarten van der Vleuten' }, - { url: 'https://lone.bandcamp.com/album/ambivert-tools-volume-three', - type: 'album', - name: 'Ambivert Tools Volume Three', - imageUrl: 'https://f4.bcbits.com/img/a3303507735_16.jpg', - artist: 'Lone' }, - { url: 'https://acidmondays.bandcamp.com/album/universal-rhythm', - type: 'album', - name: 'Universal Rhythm', - imageUrl: 'https://f4.bcbits.com/img/a2763790377_16.jpg', - artist: 'Acid Mondays' }, - { url: 'https://randsrecords.bandcamp.com/album/rv-trax', - type: 'album', - name: 'RV Trax', - imageUrl: 'https://f4.bcbits.com/img/a2057042953_16.jpg', - artist: 'Various Artists' }, - { url: 'https://afriqua.bandcamp.com/album/aleph-ep', - type: 'album', - name: 'Aleph EP', - imageUrl: 'https://f4.bcbits.com/img/a3629577728_16.jpg', - artist: 'Afriqua' }, - { url: 'https://djrum.bandcamp.com/album/broken-glass-arch', - type: 'album', - name: 'Broken Glass Arch', - imageUrl: 'https://f4.bcbits.com/img/a4267147376_16.jpg', - artist: 'Djrum' }, - { url: 'https://benjamindamage.bandcamp.com/album/montreal-ep', - type: 'album', - name: 'Montreal EP', - imageUrl: 'https://f4.bcbits.com/img/a2262177785_16.jpg', - artist: 'Benjamin Damage' }, - { url: 'https://blondes.bandcamp.com/track/kdm-barker-baumecker-remix', - type: 'track', - name: 'KDM (Barker & Baumecker remix)', - imageUrl: 'https://f4.bcbits.com/img/a4008618888_16.jpg', - artist: 'Blondes' }, - { url: 'https://lostsoulsofsaturn.bandcamp.com/album/bint-el-khandaq-vs-mashrou-leila', - type: 'album', - name: 'Bint El Khandaq (vs Mashrou\' Leila)', - imageUrl: 'https://f4.bcbits.com/img/a2811752237_16.jpg', - artist: 'Lost Souls Of Saturn' }, - { url: 'https://lone.bandcamp.com/album/ambivert-tools-volume-two', - type: 'album', - name: 'Ambivert Tools Volume Two', - imageUrl: 'https://f4.bcbits.com/img/a0296687793_16.jpg', - artist: 'Lone' }, - { url: 'https://blondes.bandcamp.com/album/warmth', - type: 'album', - name: 'Warmth', - imageUrl: 'https://f4.bcbits.com/img/a2313765301_16.jpg', - artist: 'Blondes' }, - { url: 'https://unknownarchetype.bandcamp.com/album/into-ether', - type: 'album', - name: 'Into Ether', - imageUrl: 'https://f4.bcbits.com/img/a2044920027_16.jpg', - artist: 'Unknown Archetype' }, - { url: 'https://adakaleh.bandcamp.com/album/palatul-de-cle-tar', - type: 'album', - name: 'Palatul de cleştar', - imageUrl: 'https://f4.bcbits.com/img/a3022230788_16.jpg', - artist: 'Ada Kaleh' }, - { url: 'https://michelemininni.bandcamp.com/album/rave-oscillations-vortex-stasi', - type: 'album', - name: 'Rave Oscillations / Vortex Stasi', - imageUrl: 'https://f4.bcbits.com/img/a2445110003_16.jpg', - artist: 'Michele Mininni' }, - { url: 'https://slackk.bandcamp.com/album/a-little-light', - type: 'album', - name: 'A Little Light', - imageUrl: 'https://f4.bcbits.com/img/a2384206037_16.jpg', - artist: 'Slackk' }, - { url: 'https://lone.bandcamp.com/album/ambivert-tools-volume-one', - type: 'album', - name: 'Ambivert Tools Volume One', - imageUrl: 'https://f4.bcbits.com/img/a0389879254_16.jpg', - artist: 'Lone' }, - { url: 'https://sportinglife.bandcamp.com/album/slam-dunk-remixes', - type: 'album', - name: 'Slam Dunk Remixes', - imageUrl: 'https://f4.bcbits.com/img/a2896007633_16.jpg', - artist: 'Sporting Life' }, - { url: 'https://spacedimensioncontroller.bandcamp.com/album/exostack', - type: 'album', - name: 'EXOSTACK', - imageUrl: 'https://f4.bcbits.com/img/a2211118179_16.jpg', - artist: 'Space Dimension Controller' }, - { url: 'https://talaboman.bandcamp.com/album/the-night-land', - type: 'album', - name: 'The Night Land', - imageUrl: 'https://f4.bcbits.com/img/a0709795804_16.jpg', - artist: 'Talaboman' }, - { url: 'https://paulwhite.bandcamp.com/album/accelerator', - type: 'album', - name: 'Accelerator', - imageUrl: 'https://f4.bcbits.com/img/a3525288854_16.jpg', - artist: 'Paul White ft Danny Brown' }, - { url: 'https://unknownarchetype.bandcamp.com/album/tripp-ep', - type: 'album', - name: 'TRIPP EP', - imageUrl: 'https://f4.bcbits.com/img/a0827482149_16.jpg', - artist: 'Unknown Archetype' }, - { url: 'https://sportinglife.bandcamp.com/album/slam-dunk-vinyl-lp', - type: 'album', - name: 'Slam Dunk - Vinyl LP', - imageUrl: 'https://f4.bcbits.com/img/a2750684373_16.jpg', - artist: 'Sporting Life' }, - { url: 'https://paulwhite.bandcamp.com/album/everything-youve-forgotten-free-beat-tape', - type: 'album', - name: 'Everything You\'ve Forgotten (Free Beat Tape)', - imageUrl: 'https://f4.bcbits.com/img/a0945396280_16.jpg', - artist: 'Paul White' }, - { url: 'https://randsrecords.bandcamp.com/album/meditation-will-manifest', - type: 'album', - name: 'Meditation Will Manifest', - imageUrl: 'https://f4.bcbits.com/img/a0983137742_16.jpg', - artist: 'Josh Wink' }, - { url: 'https://secondstoreyappleblim.bandcamp.com/album/gimme-6', - type: 'album', - name: 'Gimme 6', - imageUrl: 'https://f4.bcbits.com/img/a0962233606_16.jpg', - artist: 'Second Storey & Appleblim' }, - { url: 'https://sportinglife.bandcamp.com/album/slam-dunk-vol-iii', - type: 'album', - name: 'Slam Dunk Vol.III', - imageUrl: 'https://f4.bcbits.com/img/a3532028448_16.jpg', - artist: 'Sporting Life' }, - { url: 'https://struction.bandcamp.com/album/gef-ge', - type: 'album', - name: 'Gefüge', - imageUrl: 'https://f4.bcbits.com/img/a3602953469_16.jpg', - artist: 'Struction' }, - { url: 'https://sportinglife.bandcamp.com/album/slam-dunk-vol-ii', - type: 'album', - name: 'Slam Dunk Vol. II', - imageUrl: 'https://f4.bcbits.com/img/a4186689811_16.jpg', - artist: 'Sporting Life' }, - { url: 'https://sportinglife.bandcamp.com/album/slam-dunk-vol-i', - type: 'album', - name: 'Slam Dunk Vol. I', - imageUrl: 'https://f4.bcbits.com/img/a2825265641_16.jpg', - artist: 'Sporting Life' }, - { url: 'https://lakker.bandcamp.com/album/alex-smoke-remixes', - type: 'album', - name: 'Alex Smoke Remixes', - imageUrl: 'https://f4.bcbits.com/img/a0742598748_16.jpg', - artist: 'Lakker' }, - { url: 'https://lone.bandcamp.com/album/levitate', - type: 'album', - name: 'Levitate', - imageUrl: 'https://f4.bcbits.com/img/a3368931730_16.jpg', - artist: 'Lone' }, - { url: 'https://lakker.bandcamp.com/album/struggle-emerge', - type: 'album', - name: 'Struggle & Emerge', - imageUrl: 'https://f4.bcbits.com/img/a0074389183_16.jpg', - artist: 'Lakker' }, - { url: 'https://slackk.bandcamp.com/album/aviary-ep', - type: 'album', - name: 'Aviary EP', - imageUrl: 'https://f4.bcbits.com/img/a1674870329_16.jpg', - artist: 'Slackk' }, - { url: 'https://alexsmoke.bandcamp.com/album/love-over-will', - type: 'album', - name: 'Love Over Will', - imageUrl: 'https://f4.bcbits.com/img/a1635300453_16.jpg', - artist: 'Alex Smoke' }, - { url: 'https://bluedaisy.bandcamp.com/track/pure-anarchy', - type: 'track', - name: 'Pure Anarchy', - imageUrl: 'https://f4.bcbits.com/img/a3471736964_16.jpg', - artist: 'Blue Daisy' }, - { url: 'https://primitiveworld.bandcamp.com/album/purple-caps-ep', - type: 'album', - name: 'Purple Caps EP', - imageUrl: 'https://f4.bcbits.com/img/a1182531443_16.jpg', - artist: 'Primitive World' }, - { url: 'https://taleofus.bandcamp.com/album/north-star-silent-space', - type: 'album', - name: 'North Star / Silent Space', - imageUrl: 'https://f4.bcbits.com/img/a3260776828_16.jpg', - artist: 'Tale Of Us' }, - { url: 'https://moire.bandcamp.com/album/gel-ep', - type: 'album', - name: 'Gel EP', - imageUrl: 'https://f4.bcbits.com/img/a0481708077_16.jpg', - artist: 'Moiré' }, - { url: 'https://nicolasjaar.bandcamp.com/album/fight', - type: 'album', - name: 'Fight', - imageUrl: 'https://f4.bcbits.com/img/a1781921428_16.jpg', - artist: 'Nicolas Jaar' }, - { url: 'https://bluedaisy.bandcamp.com/album/darker-than-blue', - type: 'album', - name: 'Darker Than Blue', - imageUrl: 'https://f4.bcbits.com/img/a0003951232_16.jpg', - artist: 'Blue Daisy' }, - { url: 'https://sportinglife.bandcamp.com/album/55-5s', - type: 'album', - name: '55 5\'s', - imageUrl: 'https://f4.bcbits.com/img/a0558215035_16.jpg', - artist: 'Sporting Life' }, - { url: 'https://lakker.bandcamp.com/album/tundra-remixed', - type: 'album', - name: 'Tundra Remixed', - imageUrl: 'https://f4.bcbits.com/img/a4192171830_16.jpg', - artist: 'Lakker' }, - { url: 'https://lone.bandcamp.com/album/lemurian-2015-re-issue', - type: 'album', - name: 'Lemurian (2015 Re-Issue)', - imageUrl: 'https://f4.bcbits.com/img/a1897898710_16.jpg', - artist: 'Lone' }, - { url: 'https://slackk.bandcamp.com/album/backwards-light-ep', - type: 'album', - name: 'Backwards Light EP', - imageUrl: 'https://f4.bcbits.com/img/a2575436869_16.jpg', - artist: 'Slackk' }, - { url: 'https://lakker.bandcamp.com/album/tundra', - type: 'album', - name: 'Tundra', - imageUrl: 'https://f4.bcbits.com/img/a2862016036_16.jpg', - artist: 'Lakker' }, - { url: 'https://secondstoreyappleblim.bandcamp.com/album/also', - type: 'album', - name: 'ALSO', - imageUrl: 'https://f4.bcbits.com/img/a2896003816_16.jpg', - artist: 'Second Storey & Appleblim' }, - { url: 'https://tessela.bandcamp.com/album/bottom-out', - type: 'album', - name: 'Bottom Out', - imageUrl: 'https://f4.bcbits.com/img/a2168663857_16.jpg', - artist: 'Tessela' }, - { url: 'https://paulatemple.bandcamp.com/album/deathvox', - type: 'album', - name: 'Deathvox', - imageUrl: 'https://f4.bcbits.com/img/a2769035107_16.jpg', - artist: 'Paula Temple' }, - ... 67 more items ] - diff --git a/examples/getDiscoverOptions.js b/examples/getDiscoverOptions.js deleted file mode 100644 index df5e318..0000000 --- a/examples/getDiscoverOptions.js +++ /dev/null @@ -1,6 +0,0 @@ -const bcfetch = require('../'); -const util = require('util'); - -bcfetch.getDiscoverOptions().then( results => { - console.log(util.inspect(results, false, null, false)); -}); \ No newline at end of file diff --git a/examples/getDiscoverOptions_output.txt b/examples/getDiscoverOptions_output.txt deleted file mode 100644 index 2e4ee9d..0000000 --- a/examples/getDiscoverOptions_output.txt +++ /dev/null @@ -1,375 +0,0 @@ -{ genres: - [ { value: 'all', name: 'all' }, - { value: 'electronic', name: 'electronic' }, - { value: 'rock', name: 'rock' }, - { value: 'metal', name: 'metal' }, - { value: 'alternative', name: 'alternative' }, - { value: 'hip-hop-rap', name: 'hip-hop/rap' }, - { value: 'experimental', name: 'experimental' }, - { value: 'punk', name: 'punk' }, - { value: 'folk', name: 'folk' }, - { value: 'pop', name: 'pop' }, - { value: 'ambient', name: 'ambient' }, - { value: 'soundtrack', name: 'soundtrack' }, - { value: 'world', name: 'world' }, - { value: 'jazz', name: 'jazz' }, - { value: 'acoustic', name: 'acoustic' }, - { value: 'funk', name: 'funk' }, - { value: 'r-b-soul', name: 'r&b/soul' }, - { value: 'devotional', name: 'devotional' }, - { value: 'classical', name: 'classical' }, - { value: 'reggae', name: 'reggae' }, - { value: 'podcasts', name: 'podcasts' }, - { value: 'country', name: 'country' }, - { value: 'spoken-word', name: 'spoken word' }, - { value: 'comedy', name: 'comedy' }, - { value: 'blues', name: 'blues' }, - { value: 'kids', name: 'kids' }, - { value: 'audiobooks', name: 'audiobooks' }, - { value: 'latin', name: 'latin' } ], - subgenres: - { latin: - [ { value: 'all-latin', name: 'all latin' }, - { value: 'brazilian', name: 'brazilian' }, - { value: 'cumbia', name: 'cumbia' }, - { value: 'tango', name: 'tango' }, - { value: 'latin-rock', name: 'latin rock' }, - { value: 'flamenco', name: 'flamenco' }, - { value: 'salsa', name: 'salsa' }, - { value: 'reggaeton', name: 'reggaeton' }, - { value: 'merengue', name: 'merengue' }, - { value: 'bolero', name: 'bolero' }, - { value: 'méxico-d.f.', name: 'méxico d.f.' }, - { value: 'bachata', name: 'bachata' } ], - jazz: - [ { value: 'all-jazz', name: 'all jazz' }, - { value: 'fusion', name: 'fusion' }, - { value: 'big-band', name: 'big band' }, - { value: 'nu-jazz', name: 'nu jazz' }, - { value: 'modern-jazz', name: 'modern jazz' }, - { value: 'swing', name: 'swing' }, - { value: 'free-jazz', name: 'free jazz' }, - { value: 'soul-jazz', name: 'soul jazz' }, - { value: 'latin-jazz', name: 'latin jazz' }, - { value: 'vocal-jazz', name: 'vocal jazz' }, - { value: 'bebop', name: 'bebop' }, - { value: 'spiritual-jazz', name: 'spiritual jazz' } ], - blues: - [ { value: 'all-blues', name: 'all blues' }, - { value: 'rhythm-blues', name: 'rhythm & blues' }, - { value: 'blues-rock', name: 'blues rock' }, - { value: 'country-blues', name: 'country blues' }, - { value: 'boogie-woogie', name: 'boogie-woogie' }, - { value: 'delta-blues', name: 'delta blues' }, - { value: 'americana', name: 'americana' }, - { value: 'electric-blues', name: 'electric blues' }, - { value: 'gospel', name: 'gospel' }, - { value: 'bluegrass', name: 'bluegrass' } ], - 'spoken-word': - [ { value: 'all-spoken-word', name: 'all spoken word' }, - { value: 'poetry', name: 'poetry' }, - { value: 'inspirational', name: 'inspirational' }, - { value: 'storytelling', name: 'storytelling' }, - { value: 'self-help', name: 'self-help' } ], - metal: - [ { value: 'all-metal', name: 'all metal' }, - { value: 'hardcore', name: 'hardcore' }, - { value: 'black-metal', name: 'black metal' }, - { value: 'death-metal', name: 'death metal' }, - { value: 'thrash-metal', name: 'thrash metal' }, - { value: 'grindcore', name: 'grindcore' }, - { value: 'doom', name: 'doom' }, - { value: 'post-hardcore', name: 'post hardcore' }, - { value: 'progressive-metal', name: 'progressive metal' }, - { value: 'metalcore', name: 'metalcore' }, - { value: 'sludge-metal', name: 'sludge metal' }, - { value: 'heavy-metal', name: 'heavy metal' }, - { value: 'deathcore', name: 'deathcore' }, - { value: 'noise', name: 'noise' } ], - funk: - [ { value: 'all-funk', name: 'all funk' }, - { value: 'funk-jam', name: 'funk jam' }, - { value: 'deep-funk', name: 'deep funk' }, - { value: 'funk-rock', name: 'funk rock' }, - { value: 'jazz-funk', name: 'jazz funk' }, - { value: 'boogie', name: 'boogie' }, - { value: 'g-funk', name: 'g-funk' }, - { value: 'rare-groove', name: 'rare groove' }, - { value: 'electro', name: 'electro' }, - { value: 'go-go', name: 'go-go' } ], - rock: - [ { value: 'all-rock', name: 'all rock' }, - { value: 'indie', name: 'indie' }, - { value: 'prog-rock', name: 'prog rock' }, - { value: 'post-rock', name: 'post-rock' }, - { value: 'rock-roll', name: 'rock & roll' }, - { value: 'psychedelic-rock', name: 'psychedelic rock' }, - { value: 'hard-rock', name: 'hard rock' }, - { value: 'garage-rock', name: 'garage rock' }, - { value: 'surf-rock', name: 'surf rock' }, - { value: 'instrumental', name: 'instrumental' }, - { value: 'math-rock', name: 'math rock' }, - { value: 'rockabilly', name: 'rockabilly' } ], - punk: - [ { value: 'all-punk', name: 'all punk' }, - { value: 'hardcore-punk', name: 'hardcore punk' }, - { value: 'garage', name: 'garage' }, - { value: 'pop-punk', name: 'pop punk' }, - { value: 'punk-rock', name: 'punk rock' }, - { value: 'post-punk', name: 'post-punk' }, - { value: 'post-hardcore', name: 'post-hardcore' }, - { value: 'thrash', name: 'thrash' }, - { value: 'crust-punk', name: 'crust punk' }, - { value: 'folk-punk', name: 'folk punk' }, - { value: 'emo', name: 'emo' }, - { value: 'ska', name: 'ska' }, - { value: 'no-wave', name: 'no wave' } ], - country: - [ { value: 'all-country', name: 'all country' }, - { value: 'bluegrass', name: 'bluegrass' }, - { value: 'country-rock', name: 'country rock' }, - { value: 'americana', name: 'americana' }, - { value: 'country-folk', name: 'country folk' }, - { value: 'alt-country', name: 'alt-country' }, - { value: 'country-blues', name: 'country blues' }, - { value: 'western', name: 'western' }, - { value: 'singer-songwriter', name: 'singer-songwriter' }, - { value: 'outlaw', name: 'outlaw' }, - { value: 'honky-tonk', name: 'honky-tonk' }, - { value: 'roots', name: 'roots' }, - { value: 'hillbilly', name: 'hillbilly' } ], - comedy: - [ { value: 'all-comedy', name: 'all comedy' }, - { value: 'improv', name: 'improv' }, - { value: 'stand-up', name: 'stand-up' } ], - ambient: - [ { value: 'all-ambient', name: 'all ambient' }, - { value: 'chill-out', name: 'chill-out' }, - { value: 'drone', name: 'drone' }, - { value: 'dark-ambient', name: 'dark ambient' }, - { value: 'electronic', name: 'electronic' }, - { value: 'soundscapes', name: 'soundscapes' }, - { value: 'field-recordings', name: 'field recordings' }, - { value: 'atmospheric', name: 'atmospheric' }, - { value: 'meditation', name: 'meditation' }, - { value: 'noise', name: 'noise' }, - { value: 'new-age', name: 'new age' }, - { value: 'idm', name: 'idm' }, - { value: 'industrial', name: 'industrial' } ], - pop: - [ { value: 'all-pop', name: 'all pop' }, - { value: 'indie-pop', name: 'indie pop' }, - { value: 'synth-pop', name: 'synth pop' }, - { value: 'power-pop', name: 'power pop' }, - { value: 'new-wave', name: 'new wave' }, - { value: 'dream-pop', name: 'dream pop' }, - { value: 'noise-pop', name: 'noise pop' }, - { value: 'experimental-pop', name: 'experimental pop' }, - { value: 'electro-pop', name: 'electro pop' }, - { value: 'adult-contemporary', name: 'adult contemporary' }, - { value: 'jangle-pop', name: 'jangle pop' }, - { value: 'j-pop', name: 'j-pop' } ], - 'hip-hop-rap': - [ { value: 'all-hip-hop-rap', name: 'all hip-hop/rap' }, - { value: 'rap', name: 'rap' }, - { value: 'underground-hip-hop', name: 'underground hip-hop' }, - { value: 'instrumental-hip-hop', name: 'instrumental hip-hop' }, - { value: 'trap', name: 'trap' }, - { value: 'conscious-hip-hop', name: 'conscious hip-hop' }, - { value: 'boom-bap', name: 'boom-bap' }, - { value: 'beat-tape', name: 'beat-tape' }, - { value: 'hardcore', name: 'hardcore' }, - { value: 'grime', name: 'grime' } ], - electronic: - [ { value: 'all-electronic', name: 'all electronic' }, - { value: 'house', name: 'house' }, - { value: 'electronica', name: 'electronica' }, - { value: 'downtempo', name: 'downtempo' }, - { value: 'techno', name: 'techno' }, - { value: 'electro', name: 'electro' }, - { value: 'dubstep', name: 'dubstep' }, - { value: 'beats', name: 'beats' }, - { value: 'dance', name: 'dance' }, - { value: 'idm', name: 'idm' }, - { value: 'drum-bass', name: 'drum & bass' }, - { value: 'breaks', name: 'breaks' }, - { value: 'trance', name: 'trance' }, - { value: 'glitch', name: 'glitch' }, - { value: 'chiptune', name: 'chiptune' }, - { value: 'chillwave', name: 'chillwave' }, - { value: 'dub', name: 'dub' }, - { value: 'edm', name: 'edm' }, - { value: 'instrumental', name: 'instrumental' }, - { value: 'witch-house', name: 'witch house' }, - { value: 'garage', name: 'garage' }, - { value: 'juke', name: 'juke' }, - { value: 'footwork', name: 'footwork' }, - { value: 'vaporwave', name: 'vaporwave' }, - { value: 'synthwave', name: 'synthwave' } ], - alternative: - [ { value: 'all-alternative', name: 'all alternative' }, - { value: 'indie-rock', name: 'indie rock' }, - { value: 'industrial', name: 'industrial' }, - { value: 'shoegaze', name: 'shoegaze' }, - { value: 'grunge', name: 'grunge' }, - { value: 'goth', name: 'goth' }, - { value: 'dream-pop', name: 'dream pop' }, - { value: 'emo', name: 'emo' }, - { value: 'math-rock', name: 'math rock' }, - { value: 'britpop', name: 'britpop' }, - { value: 'jangle-pop', name: 'jangle pop' } ], - world: - [ { value: 'all-world', name: 'all world' }, - { value: 'latin', name: 'latin' }, - { value: 'roots', name: 'roots' }, - { value: 'african', name: 'african' }, - { value: 'tropical', name: 'tropical' }, - { value: 'tribal', name: 'tribal' }, - { value: 'brazilian', name: 'brazilian' }, - { value: 'celtic', name: 'celtic' }, - { value: 'world-fusion', name: 'world fusion' }, - { value: 'cumbia', name: 'cumbia' }, - { value: 'gypsy', name: 'gypsy' }, - { value: 'new-age', name: 'new age' }, - { value: 'balkan', name: 'balkan' }, - { value: 'reggaeton', name: 'reggaeton' } ], - soundtrack: - [ { value: 'all-soundtrack', name: 'all soundtrack' }, - { value: 'film-music', name: 'film music' }, - { value: 'video-game-music', name: 'video game music' } ], - folk: - [ { value: 'all-folk', name: 'all folk' }, - { value: 'singer-songwriter', name: 'singer-songwriter' }, - { value: 'folk-rock', name: 'folk rock' }, - { value: 'indie-folk', name: 'indie folk' }, - { value: 'pop-folk', name: 'pop folk' }, - { value: 'traditional', name: 'traditional' }, - { value: 'experimental-folk', name: 'experimental folk' }, - { value: 'roots', name: 'roots' } ], - classical: - [ { value: 'all-classical', name: 'all classical' }, - { value: 'orchestral', name: 'orchestral' }, - { value: 'neo-classical', name: 'neo-classical' }, - { value: 'chamber-music', name: 'chamber music' }, - { value: 'classical-piano', name: 'classical piano' }, - { value: 'contemporary-classical', - name: 'contemporary classical' }, - { value: 'baroque', name: 'baroque' }, - { value: 'opera', name: 'opera' }, - { value: 'choral', name: 'choral' }, - { value: 'modern-classical', name: 'modern classical' }, - { value: 'avant-garde', name: 'avant garde' } ], - reggae: - [ { value: 'all-reggae', name: 'all reggae' }, - { value: 'dub', name: 'dub' }, - { value: 'ska', name: 'ska' }, - { value: 'roots', name: 'roots' }, - { value: 'dancehall', name: 'dancehall' }, - { value: 'rocksteady', name: 'rocksteady' }, - { value: 'ragga', name: 'ragga' }, - { value: 'lovers-rock', name: 'lovers rock' } ], - kids: - [ { value: 'all-kids', name: 'all kids' }, - { value: 'family-music', name: 'family music' }, - { value: 'educational', name: 'educational' }, - { value: 'music-therapy', name: 'music therapy' }, - { value: 'lullaby', name: 'lullaby' }, - { value: 'baby', name: 'baby' } ], - acoustic: - [ { value: 'all-acoustic', name: 'all acoustic' }, - { value: 'folk', name: 'folk' }, - { value: 'singer-songwriter', name: 'singer-songwriter' }, - { value: 'rock', name: 'rock' }, - { value: 'pop', name: 'pop' }, - { value: 'guitar', name: 'guitar' }, - { value: 'americana', name: 'americana' }, - { value: 'electro-acoustic', name: 'electro-acoustic' }, - { value: 'instrumental', name: 'instrumental' }, - { value: 'piano', name: 'piano' }, - { value: 'bluegrass', name: 'bluegrass' }, - { value: 'roots', name: 'roots' } ], - 'r-b-soul': - [ { value: 'all-r-b-soul', name: 'all r&b/soul' }, - { value: 'soul', name: 'soul' }, - { value: 'r-b', name: 'r&b' }, - { value: 'neo-soul', name: 'neo-soul' }, - { value: 'gospel', name: 'gospel' }, - { value: 'contemporary-r-b', name: 'contemporary r&b' }, - { value: 'motown', name: 'motown' }, - { value: 'urban', name: 'urban' } ], - experimental: - [ { value: 'all-experimental', name: 'all experimental' }, - { value: 'noise', name: 'noise' }, - { value: 'drone', name: 'drone' }, - { value: 'avant-garde', name: 'avant garde' }, - { value: 'experimental-rock', name: 'experimental rock' }, - { value: 'improvisation', name: 'improvisation' }, - { value: 'sound-art', name: 'sound art' }, - { value: 'musique-concrete', name: 'musique concrete' } ], - devotional: - [ { value: 'all-devotional', name: 'all devotional' }, - { value: 'christian', name: 'christian' }, - { value: 'gospel', name: 'gospel' }, - { value: 'meditation', name: 'meditation' }, - { value: 'spiritual', name: 'spiritual' }, - { value: 'worship', name: 'worship' }, - { value: 'inspirational', name: 'inspirational' } ] }, - sortBys: - [ { value: 'top', name: 'best-selling' }, - { value: 'new', name: 'new arrivals' }, - { value: 'rec', name: 'artist-recommended' } ], - artistRecommendationTypes: - [ { value: 'most', name: 'most' }, - { value: 'latest', name: 'latest' } ], - locations: - [ { value: '0', name: 'artists from anywhere' }, - { value: '2759794', name: 'amsterdam' }, - { value: '4180439', name: 'atlanta' }, - { value: '4671654', name: 'austin' }, - { value: '4347778', name: 'baltimore' }, - { value: '2950159', name: 'berlin' }, - { value: '4930956', name: 'boston' }, - { value: '5110302', name: 'brooklyn' }, - { value: '3435907', name: 'buenos aires' }, - { value: '4887398', name: 'chicago' }, - { value: '5419384', name: 'denver' }, - { value: '4990729', name: 'detroit' }, - { value: '2964574', name: 'dublin' }, - { value: '3333231', name: 'glasgow' }, - { value: '2643743', name: 'london' }, - { value: '5368361', name: 'los angeles' }, - { value: '3117735', name: 'madrid' }, - { value: '2643123', name: 'manchester' }, - { value: '2158177', name: 'melbourne' }, - { value: '3530597', name: 'mexico city' }, - { value: '4164138', name: 'miami' }, - { value: '5037649', name: 'minneapolis' }, - { value: '6077243', name: 'montreal' }, - { value: '4644585', name: 'nashville' }, - { value: '4335045', name: 'new orleans' }, - { value: '5128581', name: 'new york city' }, - { value: '5378538', name: 'oakland' }, - { value: '2988507', name: 'paris' }, - { value: '4560349', name: 'philadelphia' }, - { value: '5746545', name: 'portland' }, - { value: '5391959', name: 'san francisco' }, - { value: '5809844', name: 'seattle' }, - { value: '2147714', name: 'sydney' }, - { value: '6167865', name: 'toronto' }, - { value: '6173331', name: 'vancouver' }, - { value: '4140963', name: 'washington, dc' } ], - formats: - [ { value: 'all', name: 'any format' }, - { value: 'digital', name: 'digital' }, - { value: 'vinyl', name: 'vinyl' }, - { value: 'cd', name: 'compact disc' }, - { value: 'cassette', name: 'cassette' } ], - times: - [ { value: -1, name: 'today', title: 'last 24 hours' }, - { value: 0, name: 'this week', title: 'week of Oct 17' }, - { value: 687, name: 'last week', title: 'week of Oct 10' }, - { value: 686, name: '2 weeks ago', title: 'week of Oct 03' }, - { value: 685, name: '3 weeks ago', title: 'week of Sep 26' }, - { value: 684, name: '4 weeks ago', title: 'week of Sep 19' }, - { value: 683, name: '5 weeks ago', title: 'week of Sep 12' }, - { value: 682, name: '6 weeks ago', title: 'week of Sep 05' } ] } diff --git a/examples/getFanCollection.js b/examples/getFanCollection.js deleted file mode 100644 index 93942db..0000000 --- a/examples/getFanCollection.js +++ /dev/null @@ -1,19 +0,0 @@ -const bcfetch = require('../lib'); -const util = require('util'); - -const username = 'dugout'; - -const options = { - imageFormat: 'art_app_large' -} - -bcfetch.getFanCollection(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.getFanCollection(results.continuationToken); - console.log(util.inspect(moreResults, false, null, false)); - } -}); diff --git a/examples/getFanFollowingArtistsAndLabels.js b/examples/getFanFollowingArtistsAndLabels.js deleted file mode 100644 index ba23e0a..0000000 --- a/examples/getFanFollowingArtistsAndLabels.js +++ /dev/null @@ -1,19 +0,0 @@ -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/getFanFollowingGenres.js b/examples/getFanFollowingGenres.js deleted file mode 100644 index a9c9a77..0000000 --- a/examples/getFanFollowingGenres.js +++ /dev/null @@ -1,19 +0,0 @@ -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 deleted file mode 100644 index dd00633..0000000 --- a/examples/getFanFollowingGenres_output.txt +++ /dev/null @@ -1,492 +0,0 @@ -{ - 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 deleted file mode 100644 index 5d59a91..0000000 --- a/examples/getFanInfo.js +++ /dev/null @@ -1,12 +0,0 @@ -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/getFanWishlist.js b/examples/getFanWishlist.js deleted file mode 100644 index 8beca44..0000000 --- a/examples/getFanWishlist.js +++ /dev/null @@ -1,19 +0,0 @@ -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/getImageFormats.js b/examples/getImageFormats.js deleted file mode 100644 index 57c8679..0000000 --- a/examples/getImageFormats.js +++ /dev/null @@ -1,13 +0,0 @@ -const bcfetch = require('../'); - -bcfetch.getImageFormats('album').then( results => { - console.log('Album image formats:'); - console.log(results); - console.log(); -}); - -bcfetch.getImageFormats('bio').then( results => { - console.log('Artist image formats:'); - console.log(results); - console.log(); -}); \ No newline at end of file diff --git a/examples/getLabelArtists.js b/examples/getLabelArtists.js deleted file mode 100644 index 57534de..0000000 --- a/examples/getLabelArtists.js +++ /dev/null @@ -1,12 +0,0 @@ -const bcfetch = require('../'); -const util = require('util'); - -const labelUrl = 'https://mergerecords.bandcamp.com'; - -const options = { - imageFormat: 'art_app_large', -} - -bcfetch.getLabelArtists(labelUrl, options).then( results => { - console.log(util.inspect(results, false, null, false)); -}); \ No newline at end of file diff --git a/examples/getLabelArtists_output.txt b/examples/getLabelArtists_output.txt deleted file mode 100644 index f4318a2..0000000 --- a/examples/getLabelArtists_output.txt +++ /dev/null @@ -1,401 +0,0 @@ -[ { name: 'Mac McCaughan', - url: 'https://macmccaughan.bandcamp.com', - location: 'Chapel Hill, North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0004961611_16.jpg' }, - { name: 'Wye Oak', - url: 'https://wyeoak.bandcamp.com', - location: 'Baltimore, Maryland', - imageUrl: 'https://f4.bcbits.com/img/0025004994_16.jpg' }, - { name: 'A Giant Dog', - url: 'https://agiantdog.bandcamp.com', - location: 'Austin, Texas', - imageUrl: 'https://f4.bcbits.com/img/0000651146_16.jpg' }, - { name: 'Caribou', - url: 'https://caribouband.bandcamp.com', - location: 'London, UK', - imageUrl: 'https://f4.bcbits.com/img/0018041279_16.jpg' }, - { name: 'DAWN', - url: 'https://dawnrichard.bandcamp.com', - location: 'Los Angeles, California', - imageUrl: 'https://f4.bcbits.com/img/0023711418_16.jpg' }, - { name: 'Torres', - url: 'https://torrestorrestorres.bandcamp.com', - location: 'New York, New York', - imageUrl: 'https://f4.bcbits.com/img/0024864649_16.jpg' }, - { name: 'Hiss Golden Messenger', - url: 'https://hissgoldenmessenger.bandcamp.com', - location: 'North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0024016536_16.jpg' }, - { name: 'The Mountain Goats', - url: 'https://themountaingoats.bandcamp.com', - location: 'Durham, North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0024563960_16.jpg' }, - { name: 'Superchunk', - url: 'https://superchunk.bandcamp.com', - location: 'Chapel Hill, North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0022205793_16.jpg' }, - { name: 'Lambchop', - url: 'https://lambchop.bandcamp.com', - location: 'Nashville, Tennessee', - imageUrl: 'https://f4.bcbits.com/img/0024120187_16.jpg' }, - { name: 'Reigning Sound', - url: 'https://reigningsound.bandcamp.com', - location: 'Asheville, North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0023915882_16.jpg' }, - { name: 'Cable Ties', - url: 'https://cableties.bandcamp.com', - location: 'Melbourne, Australia', - imageUrl: 'https://f4.bcbits.com/img/0008448159_16.jpg' }, - { name: 'Teenage Fanclub', - url: 'https://teenage-fanclub.bandcamp.com', - location: 'Glasgow, UK', - imageUrl: 'https://f4.bcbits.com/img/0024101151_16.jpg' }, - { name: 'Will Butler', - url: 'https://willbutler.bandcamp.com', - location: 'Brooklyn, New York', - imageUrl: 'https://f4.bcbits.com/img/0024918652_16.jpg' }, - { name: 'Waxahatchee', - url: 'https://waxahatchee.bandcamp.com', - location: 'Philadelphia, Pennsylvania', - imageUrl: 'https://f4.bcbits.com/img/0018456389_16.jpg' }, - { name: 'The Clean', - url: 'https://theclean.bandcamp.com', - location: 'New Zealand', - imageUrl: 'https://f4.bcbits.com/img/0023754516_16.jpg' }, - { name: 'Fruit Bats', - url: 'https://fruit-bats.bandcamp.com', - location: '', - imageUrl: 'https://f4.bcbits.com/img/0023214991_16.jpg' }, - { name: 'Mike Krol', - url: 'https://mikekrol.bandcamp.com', - location: 'Los Angeles, California', - imageUrl: 'https://f4.bcbits.com/img/0010450062_16.jpg' }, - { name: 'Bob Mould', - url: 'https://bobmould.bandcamp.com', - location: 'San Francisco, California', - imageUrl: 'https://f4.bcbits.com/img/0020299197_16.jpg' }, - { name: 'The Clientele', - url: 'https://theclientele.bandcamp.com', - location: 'London, UK', - imageUrl: 'https://f4.bcbits.com/img/0010580560_16.jpg' }, - { name: 'William Tyler', - url: 'https://williamtyler.bandcamp.com', - location: 'Los Angeles, California', - imageUrl: 'https://f4.bcbits.com/img/0018844022_16.jpg' }, - { name: 'H.C. McEntire', - url: 'https://hcmcentire.bandcamp.com', - location: 'Durham, North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0020946952_16.jpg' }, - { name: 'Sneaks', - url: 'https://sneaks.bandcamp.com', - location: 'Washington, D.C.', - imageUrl: 'https://f4.bcbits.com/img/0019714164_16.jpg' }, - { name: 'Lou Barlow', - url: 'https://loubarlow.bandcamp.com', - location: 'Massachusetts', - imageUrl: 'https://f4.bcbits.com/img/0023720984_16.jpg' }, - { name: 'Ibibio Sound Machine', - url: 'https://ibibiosoundmachine.bandcamp.com', - location: 'UK', - imageUrl: 'https://f4.bcbits.com/img/0015734678_16.jpg' }, - { name: 'Mikal Cronin', - url: 'https://mikalcronin.bandcamp.com', - location: 'Los Angeles, California', - imageUrl: 'https://f4.bcbits.com/img/0016983063_16.jpg' }, - { name: 'Redd Kross', - url: 'https://reddkross.bandcamp.com', - location: 'Hawthorne, California', - imageUrl: 'https://f4.bcbits.com/img/0016480045_16.jpg' }, - { name: 'Archers of Loaf', - url: 'https://archersofloaf.bandcamp.com', - location: 'North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0005368479_16.jpg' }, - { name: 'Jade Hairpins', - url: 'https://jadehairpins.bandcamp.com', - location: '', - imageUrl: 'https://f4.bcbits.com/img/0018584374_16.jpg' }, - { name: 'Sweet Spirit', - url: 'https://sweetspirittheband.bandcamp.com', - location: 'Austin, Texas', - imageUrl: 'https://f4.bcbits.com/img/0019349627_16.jpg' }, - { name: 'Mt. Wilson Repeater', - url: 'https://mtwilsonrptr.bandcamp.com', - location: '', - imageUrl: 'https://f4.bcbits.com/img/0019621062_16.jpg' }, - { name: 'The Music Tapes', - url: 'https://themusictapes.bandcamp.com', - location: 'Athens, Georgia', - imageUrl: 'https://f4.bcbits.com/img/0017776773_16.jpg' }, - { name: 'Martin Frawley', - url: 'https://martinfrawley.bandcamp.com', - location: 'Australia', - imageUrl: 'https://f4.bcbits.com/img/0018908062_16.jpg' }, - { name: 'Polvo', - url: 'https://polvonc.bandcamp.com', - location: 'Chapel Hill, North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0018801929_16.jpg' }, - { name: 'Destroyer', - url: 'https://destroyer.bandcamp.com', - location: 'Vancouver, British Columbia', - imageUrl: 'https://f4.bcbits.com/img/0017658270_16.jpg' }, - { name: 'Richard Buckner', - url: 'https://richardbuckner.bandcamp.com', - location: 'New York, New York', - imageUrl: 'https://f4.bcbits.com/img/0017863325_16.jpg' }, - { name: 'The Love Language', - url: 'https://thelovelanguage.bandcamp.com', - location: 'Chapel Hill, North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0013444279_16.jpg' }, - { name: 'Little Scream', - url: 'https://littlescream.bandcamp.com', - location: 'Montréal, Québec', - imageUrl: 'https://f4.bcbits.com/img/0005272073_16.jpg' }, - { name: 'Eric Bachmann', - url: 'https://ericbachmann.bandcamp.com', - location: 'North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0017551127_16.jpg' }, - { name: 'Gauche', - url: 'https://g-a-u-c-h-e.bandcamp.com', - location: 'Washington, D.C.', - imageUrl: 'https://f4.bcbits.com/img/0016153963_16.jpg' }, - { name: 'Joyero', - url: 'https://joyero.bandcamp.com', - location: 'Durham, North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0016703523_16.jpg' }, - { name: 'David Kilgour', - url: 'https://davidkilgour.bandcamp.com', - location: 'Dunedin', - imageUrl: 'https://f4.bcbits.com/img/0006370477_16.jpg' }, - { name: 'Apex Manor', - url: 'https://apexmanor.bandcamp.com', - location: 'Los Angeles, California', - imageUrl: 'https://f4.bcbits.com/img/0015921186_16.jpg' }, - { name: 'Ex Hex', - url: 'https://exhexband.bandcamp.com', - location: 'Washington, D.C.', - imageUrl: 'https://f4.bcbits.com/img/0015244159_16.jpg' }, - { name: 'Hollie Cook', - url: 'https://holliecook.bandcamp.com', - location: 'London, UK', - imageUrl: 'https://f4.bcbits.com/img/0017259533_16.jpg' }, - { name: 'Imperial Teen', - url: 'https://imperialteen.bandcamp.com', - location: 'San Francisco, California', - imageUrl: 'https://f4.bcbits.com/img/0016255829_16.jpg' }, - { name: 'SACRED//PAWS', - url: 'https://sacredpaws.bandcamp.com', - location: 'Glasgow, UK', - imageUrl: 'https://f4.bcbits.com/img/0000041645_16.jpg' }, - { name: 'Titus Andronicus', - url: 'https://titusandronicus.bandcamp.com', - location: '', - imageUrl: 'https://f4.bcbits.com/img/0015982887_16.jpg' }, - { name: 'The Broken West', - url: 'https://thebrokenwest.bandcamp.com', - location: 'Los Angeles, California', - imageUrl: 'https://f4.bcbits.com/img/0016185383_16.jpg' }, - { name: 'Telekinesis', - url: 'https://telekinesis.bandcamp.com', - location: 'Seattle, Washington', - imageUrl: 'https://f4.bcbits.com/img/0014904916_16.jpg' }, - { name: 'The Spinanes', - url: 'https://thespinanes.bandcamp.com', - location: 'Portland, Oregon', - imageUrl: 'https://f4.bcbits.com/img/0014533028_16.jpg' }, - { name: 'Swearin\'', - url: 'https://swearin.bandcamp.com', - location: 'Philadelphia, Pennsylvania', - imageUrl: 'https://f4.bcbits.com/img/0019671909_16.jpg' }, - { name: 'Fucked Up', - url: 'https://fuckedup.bandcamp.com', - location: 'Toronto, Ontario', - imageUrl: 'https://f4.bcbits.com/img/0007029567_16.jpg' }, - { name: 'Tracyanne & Danny', - url: 'https://tracyanneanddanny.bandcamp.com', - location: 'UK', - imageUrl: 'https://f4.bcbits.com/img/0012612375_16.jpg' }, - { name: 'Spider Bags', - url: 'https://spiderbags.bandcamp.com', - location: 'Chapel Hill, North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0006370483_16.jpg' }, - { name: 'Ought', - url: 'https://ought.bandcamp.com', - location: 'Montréal, Québec', - imageUrl: 'https://f4.bcbits.com/img/0011617874_16.jpg' }, - { name: 'She & Him', - url: 'https://sheandhim.bandcamp.com', - location: 'Los Angeles, California', - imageUrl: 'https://f4.bcbits.com/img/0026173854_16.jpg' }, - { name: 'The Essex Green', - url: 'https://theessexgreen.bandcamp.com', - location: 'Brooklyn, New York', - imageUrl: 'https://f4.bcbits.com/img/0013167432_16.jpg' }, - { name: 'The Rock*A*Teens', - url: 'https://therockateens.bandcamp.com', - location: 'Atlanta, Georgia', - imageUrl: 'https://f4.bcbits.com/img/0013223966_16.jpg' }, - { name: 'Crooked Fingers', - url: 'https://crookedfingers.bandcamp.com', - location: 'North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0013404323_16.jpg' }, - { name: 'Spoon', - url: 'https://spoontheband.bandcamp.com', - location: 'Austin, Texas', - imageUrl: 'https://f4.bcbits.com/img/0021448647_16.jpg' }, - { name: 'Neutral Milk Hotel', - url: 'https://neutralmilkhotel.bandcamp.com', - location: '', - imageUrl: 'https://f4.bcbits.com/img/0013466114_16.jpg' }, - { name: 'The Magnetic Fields', - url: 'https://themagneticfields.bandcamp.com', - location: 'New York, New York', - imageUrl: 'https://f4.bcbits.com/img/0014275749_16.jpg' }, - { name: 'Coco Hames', - url: 'https://cocohames.bandcamp.com', - location: 'Memphis, Tennessee', - imageUrl: 'https://f4.bcbits.com/img/0009197807_16.jpg' }, - { name: 'Allison Crutchfield', - url: 'https://allisoncrutchfield.bandcamp.com', - location: 'Philadelphia, Pennsylvania', - imageUrl: 'https://f4.bcbits.com/img/0008598378_16.jpg' }, - { name: 'M. Ward', - url: 'https://m-ward.bandcamp.com', - location: 'Portland, Oregon', - imageUrl: 'https://f4.bcbits.com/img/0018177684_16.jpg' }, - { name: 'Mount Moriah', - url: 'https://mountmoriah.bandcamp.com', - location: 'Durham, North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0006370463_16.jpg' }, - { name: 'Benji Hughes', - url: 'https://benjihughes.bandcamp.com', - location: 'Charlotte, North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0006362688_16.jpg' }, - { name: 'King Khan', - url: 'https://khannibalism.bandcamp.com', - location: 'Berlin, Germany', - imageUrl: 'https://f4.bcbits.com/img/0003997501_16.jpg' }, - { name: 'Drive Like Jehu', - url: 'https://drivelikejehu.bandcamp.com', - location: 'San Diego, California', - imageUrl: 'https://f4.bcbits.com/img/0007577688_16.jpg' }, - { name: 'HeCTA', - url: 'https://hecta.bandcamp.com', - location: 'Nashville, Tennessee', - imageUrl: 'https://f4.bcbits.com/img/0006073962_16.jpg' }, - { name: 'Seaweed', - url: 'https://seaweed.bandcamp.com', - location: 'Tacoma, Washington', - imageUrl: 'https://f4.bcbits.com/img/0006209491_16.jpg' }, - { name: 'Portastatic', - url: 'https://portastatic.bandcamp.com', - location: 'Chapel Hill, North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0006164342_16.jpg' }, - { name: 'East River Pipe', - url: 'https://eastriverpipe.bandcamp.com', - location: 'New York, New York', - imageUrl: 'https://f4.bcbits.com/img/0006133419_16.jpg' }, - { name: 'Volcano Suns', - url: 'https://volcanosuns.bandcamp.com', - location: 'Boston, Massachusetts', - imageUrl: 'https://f4.bcbits.com/img/0006581067_16.jpg' }, - { name: 'Flesh Wounds', - url: 'https://fleshwounds.bandcamp.com', - location: 'Carrboro, North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0006150428_16.jpg' }, - { name: 'Breadwinner', - url: 'https://breadwinner.bandcamp.com', - location: 'Richmond, Virginia', - imageUrl: 'https://f4.bcbits.com/img/0006382307_16.jpg' }, - { name: 'Vertical Scratchers', - url: 'https://verticalscratchers.bandcamp.com', - location: 'California', - imageUrl: 'https://f4.bcbits.com/img/0006149958_16.jpg' }, - { name: 'Hospitality', - url: 'https://hospitality.bandcamp.com', - location: 'Brooklyn, New York', - imageUrl: 'https://f4.bcbits.com/img/0006370459_16.jpg' }, - { name: 'SAINT RICH', - url: 'https://saintrich.bandcamp.com', - location: 'New Jersey', - imageUrl: 'https://f4.bcbits.com/img/0001635145_16.jpg' }, - { name: 'Future Bible Heroes', - url: 'https://futurebibleheroes.bandcamp.com', - location: 'New York, New York', - imageUrl: 'https://f4.bcbits.com/img/0006151240_16.jpg' }, - { name: 'Eleanor Friedberger', - url: 'https://eleanorfriedberger.bandcamp.com', - location: 'New York, New York', - imageUrl: 'https://f4.bcbits.com/img/0006151654_16.jpg' }, - { name: 'Barren Girls', - url: 'https://barrengirls.bandcamp.com', - location: 'Raleigh, North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0006156356_16.jpg' }, - { name: 'Amor de Días', - url: 'https://amordedias.bandcamp.com', - location: 'London, UK', - imageUrl: 'https://f4.bcbits.com/img/0006156859_16.jpg' }, - { name: 'Radar Brothers', - url: 'https://radarbrothers.bandcamp.com', - location: 'Los Angeles, California', - imageUrl: 'https://f4.bcbits.com/img/0006580634_16.jpg' }, - { name: 'Flock of Dimes', - url: 'https://flockofdimes.bandcamp.com', - location: 'Baltimore, Maryland', - imageUrl: 'https://f4.bcbits.com/img/0020591675_16.jpg' }, - { name: 'Wild Flag', - url: 'https://wildflag.bandcamp.com', - location: 'Portland, Oregon', - imageUrl: 'https://f4.bcbits.com/img/0006158205_16.jpg' }, - { name: 'The Extra Lens', - url: 'https://theextralens.bandcamp.com', - location: '', - imageUrl: 'https://f4.bcbits.com/img/0006158864_16.jpg' }, - { name: 'Julian Koster', - url: 'https://juliankoster.bandcamp.com', - location: 'Athens, Georgia', - imageUrl: 'https://f4.bcbits.com/img/0006164232_16.jpg' }, - { name: 'Conor Oberst', - url: 'https://conoroberst.bandcamp.com', - location: 'Omaha, Nebraska', - imageUrl: 'https://f4.bcbits.com/img/0014736743_16.jpg' }, - { name: 'Annie Hayden', - url: 'https://anniehayden.bandcamp.com', - location: 'New Jersey', - imageUrl: 'https://f4.bcbits.com/img/0006580804_16.jpg' }, - { name: 'Tenement Halls', - url: 'https://tenementhalls.bandcamp.com', - location: 'Atlanta, Georgia', - imageUrl: 'https://f4.bcbits.com/img/0006171792_16.jpg' }, - { name: 'Shark Quest', - url: 'https://sharkquest.bandcamp.com', - location: 'North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0006172106_16.jpg' }, - { name: 'Matt Suggs', - url: 'https://mattsuggs.bandcamp.com', - location: 'Visalia, California', - imageUrl: 'https://f4.bcbits.com/img/0006172356_16.jpg' }, - { name: 'The Karl Hendricks Trio', - url: 'https://thekarlhendrickstrio.bandcamp.com', - location: 'Pittsburgh, Pennsylvania', - imageUrl: 'https://f4.bcbits.com/img/0006172432_16.jpg' }, - { name: 'The Ladybug Transistor', - url: 'https://theladybugtransistor.bandcamp.com', - location: 'Brooklyn, New York', - imageUrl: 'https://f4.bcbits.com/img/0006177588_16.jpg' }, - { name: 'White Whale', - url: 'https://whitewhale.bandcamp.com', - location: 'Lawrence, Kansas', - imageUrl: 'https://f4.bcbits.com/img/0006177676_16.jpg' }, - { name: 'Ashley Stove', - url: 'https://ashleystove.bandcamp.com', - location: 'Raleigh, North Carolina', - imageUrl: 'https://f4.bcbits.com/img/0006178380_16.jpg' }, - { name: 'Versus', - url: 'https://versusny.bandcamp.com', - location: 'New York, New York', - imageUrl: 'https://f4.bcbits.com/img/0015735441_16.jpg' }, - { name: 'Big Dipper', - url: 'https://bigdipper.bandcamp.com', - location: 'Boston, Massachusetts', - imageUrl: 'https://f4.bcbits.com/img/0006178810_16.jpg' }, - ... 10 more items ] diff --git a/examples/getReleasesByTag.js b/examples/getReleasesByTag.js deleted file mode 100644 index 34cd0ee..0000000 --- a/examples/getReleasesByTag.js +++ /dev/null @@ -1,23 +0,0 @@ -const bcfetch = require('../'); -const util = require('util'); - -const tagUrl = 'https://bandcamp.com/tag/dark-ambient'; - -const params = { - filters: { - tags: [ 'electronica' ], - sort: 'random' - }, - page: 2 -}; - -const options = { - imageFormat: 2, - useHardcodedDefaultFilters: true -} - -bcfetch.getReleasesByTag(tagUrl, params, options).then( results => { - console.log(util.inspect(results, false, null, false)); -}); - - diff --git a/examples/getReleasesByTagFilterOptions.js b/examples/getReleasesByTagFilterOptions.js deleted file mode 100644 index 7dc8bec..0000000 --- a/examples/getReleasesByTagFilterOptions.js +++ /dev/null @@ -1,8 +0,0 @@ -const bcfetch = require('../'); -const util = require('util'); - -const tagUrl = 'https://bandcamp.com/tag/dark-ambient'; - -bcfetch.getReleasesByTagFilterOptions(tagUrl).then( results => { - console.log(util.inspect(results, false, null, false)); -}); diff --git a/examples/getReleasesByTagFilterOptions_output.txt b/examples/getReleasesByTagFilterOptions_output.txt deleted file mode 100644 index 2c08ec4..0000000 --- a/examples/getReleasesByTagFilterOptions_output.txt +++ /dev/null @@ -1,58 +0,0 @@ -[ { name: 'format', - options: - [ { value: 'all', - name: 'any format', - selected: true, - default: true }, - { value: 'vinyl', name: 'vinyl' }, - { value: 'cd', name: 'compact disc' }, - { value: 'cassette', name: 'cassette' } ] }, - { name: 'sort', - options: - [ { value: 'pop', - name: 'best-selling', - selected: true, - default: true }, - { value: 'date', name: 'new arrivals' }, - { value: 'random', name: 'surprise me!' } ] }, - { name: 'location', - options: - [ { value: 0, name: 'any location', selected: true, default: true }, - { value: 2759794, name: 'amsterdam' }, - { value: 4180439, name: 'atlanta' }, - { value: 4671654, name: 'austin' }, - { value: 4347778, name: 'baltimore' }, - { value: 2950159, name: 'berlin' }, - { value: 4930956, name: 'boston' }, - { value: 5110302, name: 'brooklyn' }, - { value: 3435907, name: 'buenos aires' }, - { value: 4887398, name: 'chicago' }, - { value: 5419384, name: 'denver' }, - { value: 4990729, name: 'detroit' }, - { value: 2964574, name: 'dublin' }, - { value: 3333231, name: 'glasgow' }, - { value: 2643743, name: 'london' }, - { value: 5368361, name: 'los angeles' }, - { value: 3117735, name: 'madrid' }, - { value: 2643123, name: 'manchester' }, - { value: 2158177, name: 'melbourne' }, - { value: 3530597, name: 'mexico city' }, - { value: 4164138, name: 'miami' }, - { value: 5037649, name: 'minneapolis' }, - { value: 6077243, name: 'montreal' }, - { value: 4644585, name: 'nashville' }, - { value: 4335045, name: 'new orleans' }, - { value: 5128581, name: 'new york city' }, - { value: 5378538, name: 'oakland' }, - { value: 2988507, name: 'paris' }, - { value: 4560349, name: 'philadelphia' }, - { value: 5746545, name: 'portland' }, - { value: 5391959, name: 'san francisco' }, - { value: 5809844, name: 'seattle' }, - { value: 2147714, name: 'sydney' }, - { value: 6167865, name: 'toronto' }, - { value: 6173331, name: 'vancouver' }, - { value: 4140963, name: 'washington, dc' } ] }, - { name: 'tags', - options: - [ { value: 'dark-ambient', name: 'dark ambient', selected: true } ] } ] diff --git a/examples/getReleasesByTag_output.txt b/examples/getReleasesByTag_output.txt deleted file mode 100644 index 2a196f4..0000000 --- a/examples/getReleasesByTag_output.txt +++ /dev/null @@ -1,217 +0,0 @@ -{ items: - [ { type: 'album', - name: 'postscriptum', - url: 'https://stavorius.bandcamp.com/album/postscriptum', - imageUrl: 'https://f4.bcbits.com/img/a2761638200_2.jpg', - genre: 'electronic', - artist: { name: 'Stavorius', url: 'https://stavorius.bandcamp.com' }, - featuredTrack: - { name: 'salt', - position: 2, - streamUrl: 'https://t4.bcbits.com/stream/60a918bc2559fa65c0f74d282f6258db/mp3-128/1530345467?p=0&ts=1634715197&t=6720f923785c93481e8f90dbd61a8144904d3e49&token=1634715197_b9aeff021b14988fdfd50e53a27953fe9e380387' } }, - { type: 'album', - name: 'Perspectives', - url: 'https://welos2.bandcamp.com/album/perspectives', - imageUrl: 'https://f4.bcbits.com/img/a3358639281_2.jpg', - genre: 'electronic', - artist: { name: 'welos2', url: 'https://welos2.bandcamp.com' }, - featuredTrack: - { name: 'Mars Swamp Dub', - position: 11, - streamUrl: 'https://t4.bcbits.com/stream/236aaf2cf09e6a99dc6d706605ff67d8/mp3-128/2036452373?p=0&ts=1634715197&t=daec76c1ed53d3fc16c713555e1722b2d2f54fbc&token=1634715197_254f89bb2a9bd95045926d27e0a851163abe1d0d' } }, - { type: 'album', - name: 'Eloisa to Abelard', - url: 'https://thelightandday.bandcamp.com/album/eloisa-to-abelard', - imageUrl: 'https://f4.bcbits.com/img/a1164114313_2.jpg', - genre: 'ambient', - artist: - { name: 'The Light & Day', - url: 'https://thelightandday.bandcamp.com' }, - featuredTrack: - { name: 'Vanishing Essence', - position: 1, - streamUrl: 'https://t4.bcbits.com/stream/9ad59cdac5a9aea8b975edac6fdfa561/mp3-128/203753509?p=0&ts=1634715197&t=c022bbe2aa8a2c0841cb5279e49500524f4550f2&token=1634715197_c6b3e7eda6412aa5defc1c16fd8b8621d1b03d3f' } }, - { type: 'album', - name: 'Northern Lights', - url: 'https://ichxde.bandcamp.com/album/northern-lights', - imageUrl: 'https://f4.bcbits.com/img/a3749436388_2.jpg', - genre: 'experimental', - artist: { name: 'Ichxde', url: 'https://ichxde.bandcamp.com' }, - featuredTrack: - { name: 'Northern Lights', - position: 1, - streamUrl: 'https://t4.bcbits.com/stream/53c264895ea8378900eec20591aeb0cb/mp3-128/3432187287?p=0&ts=1634715197&t=e8285e1de716198a3e15c8411f4b4575d336ae95&token=1634715197_9a6e24c2905771fca375d4fe8672f85d60334ad2' } }, - { type: 'album', - name: 'Geometría de los sueños y el santuario interior.', - url: 'https://mavlart.bandcamp.com/album/geometr-a-de-los-sue-os-y-el-santuario-interior', - imageUrl: 'https://f4.bcbits.com/img/a1608996299_2.jpg', - genre: 'electronic', - artist: - { name: 'Miguel Ángel Verdugo', - url: 'https://mavlart.bandcamp.com' }, - featuredTrack: - { name: 'Santuario Interior', - position: 2, - streamUrl: 'https://t4.bcbits.com/stream/44ec2ac3224b3846fa58393a6856d190/mp3-128/2358345289?p=0&ts=1634715197&t=29e5354d3be212f387040ab4f0156461b45b2477&token=1634715197_5926716f9a9bfcd90f9ef73c63ed1c4692491437' } }, - { type: 'album', - name: 'Mobile Polarity', - url: 'https://fmglowe.bandcamp.com/album/mobile-polarity', - imageUrl: 'https://f4.bcbits.com/img/a336777130_2.jpg', - genre: 'ambient', - artist: { name: 'FM Glowe', url: 'https://fmglowe.bandcamp.com' }, - featuredTrack: - { name: 'Celestial', - position: 1, - streamUrl: 'https://t4.bcbits.com/stream/a28b99ec4d84948e303bb46653b16070/mp3-128/467194986?p=0&ts=1634715197&t=95449fb10804c1c095a1f0b52bd8a2e134845b00&token=1634715197_a5a1a51779d41a42c2475ce671c08cc043e9e419' } }, - { type: 'album', - name: 'All Demons Are Horned', - url: 'https://winter-light.bandcamp.com/album/all-demons-are-horned-2', - imageUrl: 'https://f4.bcbits.com/img/a1508224094_2.jpg', - genre: 'electronic', - artist: { name: 'ABBILDUNG', url: 'https://winter-light.bandcamp.com' }, - featuredTrack: - { name: 'Usdeno', - position: 3, - streamUrl: 'https://t4.bcbits.com/stream/267cc865fbe60e5a02742b2585f74a65/mp3-128/3854891695?p=0&ts=1634715197&t=2599d4315ead80a893ded29fce054ad5792eb85a&token=1634715197_dcfb831f07e252ae8c73621eb2c11618eed7e533' } }, - { type: 'album', - name: 'Lost Myths : Sons of Ragnar', - url: 'https://auja.bandcamp.com/album/lost-myths-sons-of-ragnar', - imageUrl: 'https://f4.bcbits.com/img/a2328868366_2.jpg', - genre: 'electronic', - artist: { name: 'Auja', url: 'https://auja.bandcamp.com' }, - featuredTrack: - { name: 'Limbos', - position: 1, - streamUrl: 'https://t4.bcbits.com/stream/07b855f912b63d01cc51ca78947159bb/mp3-128/2684301534?p=0&ts=1634715197&t=ba729bb285eeea2044569a993ac971c88f743714&token=1634715197_5e1a585be1a3e892151a43eefb01b673235e746d' } }, - { type: 'album', - name: 'Thin Places', - url: 'https://bathyphysa.bandcamp.com/album/thin-places', - imageUrl: 'https://f4.bcbits.com/img/a3172461480_2.jpg', - genre: 'ambient', - artist: { name: 'BATHYPHYSA', url: 'https://bathyphysa.bandcamp.com' }, - featuredTrack: - { name: 'The beach is a pane of glass', - position: 8, - streamUrl: 'https://t4.bcbits.com/stream/f6c942af8dde297febdbfe1ba53109e1/mp3-128/3502376764?p=0&ts=1634715197&t=e363b718c0fbb2154cacb0a5ca06983a055f7281&token=1634715197_b243a57bf46b665f66f1ebec3fc806797a614f58' } }, - { type: 'album', - name: 'Notebook of Strange Dreams & Nightmares', - url: 'https://triplicaterecords.bandcamp.com/album/notebook-of-strange-dreams-nightmares', - imageUrl: 'https://f4.bcbits.com/img/a1349738161_2.jpg', - genre: 'electronic', - artist: - { name: 'Building a Building', - url: 'https://triplicaterecords.bandcamp.com' }, - featuredTrack: - { name: 'At Swim Flickering Memories of Vietnam', - position: 1, - streamUrl: 'https://t4.bcbits.com/stream/acd86a2e50de2113958dde14c879fadb/mp3-128/1689803961?p=0&ts=1634715197&t=9da21901c4cc61d916d6e7543a78b9ebb06b80ff&token=1634715197_bce94eb69bc205aa6bc115b85f2a0aed88c0444c' } }, - { type: 'album', - name: 'Thomas Bel - Aux ombres, mon corps, en manifeste', - url: 'https://distantvoices.bandcamp.com/album/thomas-bel-aux-ombres-mon-corps-en-manifeste', - imageUrl: 'https://f4.bcbits.com/img/a1928082173_2.jpg', - genre: 'metal', - artist: - { name: 'DISTANT VOICES', - url: 'https://distantvoices.bandcamp.com' }, - featuredTrack: - { name: 'Et en miroir, le secret des larmes versées', - position: 1, - streamUrl: 'https://t4.bcbits.com/stream/a73e05158f5afe191737991041c955ea/mp3-128/3096519738?p=0&ts=1634715197&t=2f46272f3b229c458a6df8648d5af77967c30e90&token=1634715197_811963c14c5a583236bd3b0409bb021bff8135ab' } }, - { type: 'album', - name: 'Cosmic Meditation', - url: 'https://hoducoma.bandcamp.com/album/cosmic-meditation', - imageUrl: 'https://f4.bcbits.com/img/a193891435_2.jpg', - genre: 'ambient', - artist: { name: 'Hoducoma', url: 'https://hoducoma.bandcamp.com' }, - featuredTrack: - { name: 'Cosmic Meditation', - position: 2, - streamUrl: 'https://t4.bcbits.com/stream/d36eb9bf5fe6442b2d72d037fd0bfc63/mp3-128/2545362027?p=0&ts=1634715197&t=4979e3cd1eadd52267e5fae8266b8e38e23cb129&token=1634715197_0ece61ade4096fbf16f6f8527b85990ee9832ecb' } }, - { type: 'album', - name: 'Citie Of Lost Machines: The Esoteric Engines', - url: 'https://arkaeomecha.bandcamp.com/album/citie-of-lost-machines-the-esoteric-engines', - imageUrl: 'https://f4.bcbits.com/img/a4226048469_2.jpg', - genre: 'electronic', - artist: { name: 'Arkaeomecha', url: 'https://arkaeomecha.bandcamp.com' }, - featuredTrack: - { name: 'Scythe Ritual 2. The Cathedral of Wires', - position: 4, - streamUrl: 'https://t4.bcbits.com/stream/1c400cf81bce8d9d7f28788c6f3b7b28/mp3-128/335154179?p=0&ts=1634715197&t=fc2c43df1548aba8aa160f9ff7abcdff07d1b696&token=1634715197_5dc397752a41a4fe430605b96bbd7420e85ff21d' } }, - { type: 'album', - name: 'Drumscapes Vol 1', - url: 'https://markheaney.bandcamp.com/album/drumscapes-vol-1', - imageUrl: 'https://f4.bcbits.com/img/a3006997561_2.jpg', - genre: 'experimental', - artist: { name: 'Mark Heaney', url: 'https://markheaney.bandcamp.com' }, - featuredTrack: - { name: 'Sacrifice', - position: 1, - streamUrl: 'https://t4.bcbits.com/stream/fb0ae19f16d56c656392edaa484ac603/mp3-128/4130672029?p=0&ts=1634715198&t=c822c26923a1429b0844af40fcd5410b28c134d5&token=1634715198_5bc265eb858b707609e83c4677b0e34d78c5cf3b' } }, - { type: 'album', - name: 'Classic Collection - Studio Quality', - url: 'https://d4vdeka3r.bandcamp.com/album/classic-collection-studio-quality', - imageUrl: 'https://f4.bcbits.com/img/a4097297637_2.jpg', - genre: 'electronic', - artist: { name: 'DeKa3r', url: 'https://d4vdeka3r.bandcamp.com' }, - featuredTrack: - { name: 'DeKa3r. Dark Sign (Original Mix) 2006 Studio Quality', - position: 3, - streamUrl: 'https://t4.bcbits.com/stream/7b9e442235cbcf13816a630356c44b80/mp3-128/1850739639?p=0&ts=1634715198&t=7869a4df80fe6f29c7a20fe1965906896dd3090f&token=1634715198_50df721a3a7ef0f9cef489ebd0519db46d4b71f7' } }, - { type: 'album', - name: '原形質 - Protoplasma Originale', - url: 'https://westgard.bandcamp.com/album/protoplasma-originale', - imageUrl: 'https://f4.bcbits.com/img/a3183029554_2.jpg', - genre: 'electronic', - artist: { name: 'Westgård', url: 'https://westgard.bandcamp.com' }, - featuredTrack: - { name: 'Phantasm N°1', - position: 1, - streamUrl: 'https://t4.bcbits.com/stream/e920930b4ddd195c91ae6688147d4f72/mp3-128/2484591907?p=0&ts=1634715198&t=bfa802c0414d7dd197d85bc0be434c8accb5ce92&token=1634715198_7222738708bdd9abf7f98265c90a109cfe1e840a' } }, - { type: 'album', - name: 'Musings 4', - url: 'https://bosonspin.bandcamp.com/album/musings-4', - imageUrl: 'https://f4.bcbits.com/img/a2300749537_2.jpg', - genre: 'ambient', - artist: { name: 'Boson Spin', url: 'https://bosonspin.bandcamp.com' }, - featuredTrack: - { name: 'One', - position: 1, - streamUrl: 'https://t4.bcbits.com/stream/a65cfa993e4b0f800f281d11d758dcd7/mp3-128/1272146872?p=0&ts=1634715198&t=ff09c178e7aea242eeac922798f9200ead50448d&token=1634715198_b27917fa9de712eca5438d9ae77384505d1c1faa' } }, - { type: 'album', - name: 'INDIGO', - url: 'https://symbols.bandcamp.com/album/indigo', - imageUrl: 'https://f4.bcbits.com/img/a3158081941_2.jpg', - genre: 'electronic', - artist: { name: 'my.head', url: 'https://symbols.bandcamp.com' }, - featuredTrack: - { name: 'Matriarch', - position: 1, - streamUrl: 'https://t4.bcbits.com/stream/0976647c979932453a3ae589098b4c4a/mp3-128/4243112759?p=0&ts=1634715198&t=4075c317df22270bcbaa93c1f2d690316a29f7ac&token=1634715198_8802e21ef5ffb545cd6ab927cb03eae3a7fca3d8' } }, - { type: 'album', - name: 'Distances', - url: 'https://timbenjamin.bandcamp.com/album/distances', - imageUrl: 'https://f4.bcbits.com/img/a1062460752_2.jpg', - genre: 'electronic', - artist: - { name: 'Tim Benjamin', - url: 'https://timbenjamin.bandcamp.com' }, - featuredTrack: - { name: 'Planck', - position: 1, - streamUrl: 'https://t4.bcbits.com/stream/042b092f37e77044036ff8763c64e9da/mp3-128/1193126134?p=0&ts=1634715198&t=f17094f6c9a0497b29fc138c1789a46a2480b1dc&token=1634715198_b534755c43a99824d3c1f3f8c5a9bbc60ced06e9' } }, - { type: 'album', - name: 'Salvage', - url: 'https://nebulas.bandcamp.com/album/salvage', - imageUrl: 'https://f4.bcbits.com/img/a940156336_2.jpg', - genre: 'electronic', - artist: { name: 'Nebulaś', url: 'https://nebulas.bandcamp.com' }, - featuredTrack: - { name: 'Child', - position: 1, - streamUrl: 'https://t4.bcbits.com/stream/1851fd5ec311dd719402f35a5830b4f7/mp3-128/1567952320?p=0&ts=1634715198&t=7ec3f67ae549c434665c7196197587fb98aaa4b2&token=1634715198_d636545d05ce791fb2401b2dadd0235b3b2631e2' } } ], - hasMore: true, - filters: - { format: 'all', - sort: 'random', - tags: [ 'dark-ambient', 'electronica' ], - location: 0 } } diff --git a/examples/getShow.js b/examples/getShow.js deleted file mode 100644 index d2d5271..0000000 --- a/examples/getShow.js +++ /dev/null @@ -1,8 +0,0 @@ -const bcfetch = require('../'); -const util = require('util'); - -const showUrl = 'https://bandcamp.com/?show=421'; - -bcfetch.getShow(showUrl).then( results => { - console.log(util.inspect(results, false, null, false)); -}); \ No newline at end of file diff --git a/examples/getShow_output.txt b/examples/getShow_output.txt deleted file mode 100644 index 7adf3d7..0000000 --- a/examples/getShow_output.txt +++ /dev/null @@ -1,343 +0,0 @@ -{ type: 'show', - name: 'The Hip-Hop Show', - url: 'https://bandcamp.com/?show=421', - publishedDate: '29 Jan 2021 00:00:00 GMT', - description: 'Ovrkast joins the show to discuss his latest album, Try Again. Plus sounds from Potatohead People, Isaiah Mclane, and Boom Baptist.', - shortDescription: 'Ovrkast discusses his latest album, Try Again, plus appearances from Boom Baptist and Isaiah Mclane', - imageCaption: 'Hosted by Stoney Creation. Illustration of Ovrkast by McKay Felt', - subtitle: '55 Degree Weather', - duration: 5133.84, - imageUrl: 'https://f4.bcbits.com/img/23443998_25.jpg', - screenImageUrl: 'https://f4.bcbits.com/img/23443998_0', - streamUrl: - { 'mp3-128': 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1825658732&ts=1634629052&t=8c539a55731f2ea4b38736908912b7e02d8d793d', - 'opus-lo': 'https://popplers5.bandcamp.com/download/track?enc=opus-lo&fsig=99eb94ef2d6630ffc9c99c346211db7c&id=1825658732&stream=1&ts=1634629053.0' }, - tracks: - [ { name: 'time to go', - url: 'https://eighty9s.bandcamp.com/track/time-to-go', - imageUrl: 'https://f4.bcbits.com/img/a1082190285_9.jpg', - seekPosition: 0, - artist: - { name: 'eighty9s', - url: 'https://eighty9s.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/19154098_21.jpg', - location: 'Oakland, California' }, - album: - { name: 'Past Time', - url: 'https://eighty9s.bandcamp.com/album/past-time' } }, - { name: 'What It Feels Like (feat. T3, Illa J & Kapok)', - url: 'https://potatoheadpeople.bandcamp.com/track/what-it-feels-like-feat-t3-illa-j-kapok-2', - imageUrl: 'https://f4.bcbits.com/img/a56254833_9.jpg', - seekPosition: 106, - artist: - { name: 'Potatohead People', - url: 'https://potatoheadpeople.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/21755415_21.jpg', - location: 'Vancouver, British Columbia' }, - album: - { name: 'Mellow Fantasy', - url: 'https://potatoheadpeople.bandcamp.com/album/mellow-fantasy' } }, - { name: 'Translate It ft. B-Money', - url: 'https://juggaknots.bandcamp.com/track/translate-it-ft-b-money', - imageUrl: 'https://f4.bcbits.com/img/a2530516806_9.jpg', - seekPosition: 313, - artist: - { name: 'Breeze Brewin, Juggaknots', - url: 'https://juggaknots.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/14673280_21.jpg', - location: 'New York, New York' }, - album: - { name: 'Breeze Brewin, Juggaknots - Hindsight', - url: 'https://juggaknots.bandcamp.com/album/breeze-brewin-juggaknots-hindsight' } }, - { name: 'FistfulofGreens', - url: 'https://yungmorpheus.bandcamp.com/track/fistfulofgreens', - imageUrl: 'https://f4.bcbits.com/img/a122830275_9.jpg', - seekPosition: 460, - artist: - { name: 'YUNGMORPHEUS & ewonee', - url: 'https://yungmorpheus.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/23396531_21.jpg', - location: 'Los Angeles, California' }, - album: - { name: 'Thumbing Thru Foliage', - url: 'https://yungmorpheus.bandcamp.com/album/thumbing-thru-foliage' } }, - { name: 'When I\'m Gone', - url: 'https://aaronxfairchild.bandcamp.com/track/when-im-gone', - imageUrl: 'https://f4.bcbits.com/img/a1569522923_9.jpg', - seekPosition: 575, - artist: - { name: 'Aaron Fairchild', - url: 'https://aaronxfairchild.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/25703843_21.jpg', - location: 'Portland, Oregon' }, - album: - { name: 'The Sun Comes Through', - url: 'https://aaronxfairchild.bandcamp.com/album/the-sun-comes-through' } }, - { name: 'U callin\' Me (ft. pink siifu & akeema-zane) [prod. swarvy]', - url: 'https://lojii.bandcamp.com/track/u-callin-me-ft-pink-siifu-akeema-zane-prod-swarvy', - imageUrl: 'https://f4.bcbits.com/img/a23158227_9.jpg', - seekPosition: 780, - artist: - { name: 'lojii', - url: 'https://lojii.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/22800934_21.jpg', - location: 'Philadelphia, Pennsylvania' }, - album: - { name: 'lo&behold', - url: 'https://lojii.bandcamp.com/album/lo-behold-2' } }, - { name: 'Wings', - url: 'https://isaiahmostafa.bandcamp.com/track/wings', - imageUrl: 'https://f4.bcbits.com/img/a454474599_9.jpg', - seekPosition: 955, - artist: - { name: 'Isaiah Mostafa', - url: 'https://isaiahmostafa.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/24824562_21.jpg', - location: 'Oakland, California' }, - album: - { name: 'The 37 Week Challenge', - url: 'https://isaiahmostafa.bandcamp.com/album/the-37-week-challenge' } }, - { name: 'Press Play', - url: 'https://ashiakarana.bandcamp.com/track/press-play', - imageUrl: 'https://f4.bcbits.com/img/a2001339741_9.jpg', - seekPosition: 1123, - artist: - { name: 'Ashia Karana', - url: 'https://ashiakarana.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/21083350_21.jpg', - location: 'Atlanta, Georgia' }, - album: null }, - { name: '8thstrike_', - url: 'https://knxwledge.bandcamp.com/track/8thstrike', - imageUrl: 'https://f4.bcbits.com/img/a1776099330_9.jpg', - seekPosition: 1342, - artist: - { name: 'Knxwledge.', - url: 'https://knxwledge.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/20991183_21.jpg', - location: 'Los Angeles, California' }, - album: - { name: 'VGM.8', - url: 'https://knxwledge.bandcamp.com/album/vgm-8' } }, - { name: 'Henny Bottles', - url: 'https://shawcalhoune.bandcamp.com/track/henny-bottles', - imageUrl: 'https://f4.bcbits.com/img/a3592642307_9.jpg', - seekPosition: 1538, - artist: - { name: 'Shaw Calhoune', - url: 'https://shawcalhoune.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/22975182_21.jpg', - location: 'Maryland' }, - album: - { name: 'The Rudy Tape', - url: 'https://shawcalhoune.bandcamp.com/album/the-rudy-tape' } }, - { name: '3 WAY PHONE CALL (feat. 3WAYSLIM, BIG KAHUNA OG, MONDAY NIGHT)', - url: 'https://schemeteamallstars.bandcamp.com/track/3-way-phone-call-feat-3wayslim-big-kahuna-og-monday-night', - imageUrl: 'https://f4.bcbits.com/img/a309077249_9.jpg', - seekPosition: 1628, - artist: - { name: 'Big Kahuna OG & Monday Night', - url: 'https://schemeteamallstars.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/13872878_21.jpg', - location: 'Richmond, Virginia' }, - album: - { name: 'SHARK REPORT', - url: 'https://schemeteamallstars.bandcamp.com/album/shark-report' } }, - { name: 'Loonng Time Coming', - url: 'https://culturepower45.bandcamp.com/track/loonng-time-coming', - imageUrl: 'https://f4.bcbits.com/img/a3904534507_9.jpg', - seekPosition: 1833, - artist: - { name: 'Primeridian x Rashid Hadee', - url: 'https://culturepower45.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/16005517_21.jpg', - location: 'Chicago, Illinois' }, - album: - { name: 'Prime Diesel', - url: 'https://culturepower45.bandcamp.com/album/prime-diesel' } }, - { name: 'Toucan Wing', - url: 'https://boombaptist.bandcamp.com/track/toucan-wing', - imageUrl: 'https://f4.bcbits.com/img/a3906030308_9.jpg', - seekPosition: 2047, - artist: - { name: 'BoomBaptist', - url: 'https://boombaptist.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/23197928_21.jpg', - location: 'Austin' }, - album: - { name: 'Komfort Food', - url: 'https://boombaptist.bandcamp.com/album/komfort-food' } }, - { name: 'A Distant Night', - url: 'https://atelier71.bandcamp.com/track/a-distant-night-2', - imageUrl: 'https://f4.bcbits.com/img/a2599977834_9.jpg', - seekPosition: 2262, - artist: - { name: 'Arya', - url: 'https://atelier71.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/19571878_21.jpg', - location: 'Milan, Italy' }, - album: - { name: 'Peace Of Mind', - url: 'https://atelier71.bandcamp.com/album/peace-of-mind' } }, - { name: 'Awards (Intro) feat. Noah Archangel', - url: 'https://bohup.bandcamp.com/track/awards-intro-feat-noah-archangel', - imageUrl: 'https://f4.bcbits.com/img/a3760799760_9.jpg', - seekPosition: 2440, - artist: - { name: 'The Band of the Hawk & Yeaux Majesty', - url: 'https://bohup.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/20980289_21.jpg', - location: 'Houston, Texas' }, - album: - { name: 'Memorabilia', - url: 'https://bohup.bandcamp.com/album/memorabilia' } }, - { name: 'AllPraise', - url: 'https://itsovrkast.bandcamp.com/track/allpraise', - imageUrl: 'https://f4.bcbits.com/img/a4154433266_9.jpg', - seekPosition: 2601, - artist: - { name: 'ovrkast.', - url: 'https://itsovrkast.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/19079138_21.jpg', - location: 'Oakland, California' }, - album: - { name: 'Try Again', - url: 'https://itsovrkast.bandcamp.com/album/try-again' } }, - { name: 'Face Ft. Navy Blue', - url: 'https://itsovrkast.bandcamp.com/track/face-ft-navy-blue', - imageUrl: 'https://f4.bcbits.com/img/a4154433266_9.jpg', - seekPosition: 2856, - artist: - { name: 'ovrkast.', - url: 'https://itsovrkast.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/19079138_21.jpg', - location: 'Oakland, California' }, - album: - { name: 'Try Again', - url: 'https://itsovrkast.bandcamp.com/album/try-again' } }, - { name: 'Church Ft. Demahjiae', - url: 'https://itsovrkast.bandcamp.com/track/church-ft-demahjiae', - imageUrl: 'https://f4.bcbits.com/img/a4154433266_9.jpg', - seekPosition: 3210, - artist: - { name: 'ovrkast.', - url: 'https://itsovrkast.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/19079138_21.jpg', - location: 'Oakland, California' }, - album: - { name: 'Try Again', - url: 'https://itsovrkast.bandcamp.com/album/try-again' } }, - { name: 'Try Again', - url: 'https://itsovrkast.bandcamp.com/track/try-again', - imageUrl: 'https://f4.bcbits.com/img/a4154433266_9.jpg', - seekPosition: 3498, - artist: - { name: 'ovrkast.', - url: 'https://itsovrkast.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/19079138_21.jpg', - location: 'Oakland, California' }, - album: - { name: 'Try Again', - url: 'https://itsovrkast.bandcamp.com/album/try-again' } }, - { name: 'Outro (its time)', - url: 'https://itsovrkast.bandcamp.com/track/outro-its-time', - imageUrl: 'https://f4.bcbits.com/img/a4154433266_9.jpg', - seekPosition: 3788, - artist: - { name: 'ovrkast.', - url: 'https://itsovrkast.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/19079138_21.jpg', - location: 'Oakland, California' }, - album: - { name: 'Try Again', - url: 'https://itsovrkast.bandcamp.com/album/try-again' } }, - { name: 'The Intro(vert)', - url: 'https://jyps.bandcamp.com/track/the-intro-vert', - imageUrl: 'https://f4.bcbits.com/img/a1841901895_9.jpg', - seekPosition: 4060, - artist: - { name: 'Jypsy', - url: 'https://jyps.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/23396305_21.jpg', - location: 'Inkster, Michigan' }, - album: - { name: 'Born Wit It', - url: 'https://jyps.bandcamp.com/album/born-wit-it' } }, - { name: 'swordfish', - url: 'https://jdreid.bandcamp.com/track/swordfish', - imageUrl: 'https://f4.bcbits.com/img/a3603942056_9.jpg', - seekPosition: 4190, - artist: - { name: 'JD. Reid', - url: 'https://jdreid.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/22245447_21.jpg', - location: 'London, UK' }, - album: - { name: 'North West\'s Finest', - url: 'https://jdreid.bandcamp.com/album/north-wests-finest' } }, - { name: 'high rise in newark', - url: 'https://afrolab9000.bandcamp.com/track/high-rise-in-newark', - imageUrl: 'https://f4.bcbits.com/img/a738960096_9.jpg', - seekPosition: 4286, - artist: - { name: 'R.A.P. Ferreira', - url: 'https://afrolab9000.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/4451843_21.jpg', - location: 'Nashville, Tennessee' }, - album: - { name: 'bob\'s son: R.A.P. Ferreira in the garden level cafe of the scallops hotel', - url: 'https://afrolab9000.bandcamp.com/album/bobs-son-r-a-p-ferreira-in-the-garden-level-cafe-of-the-scallops-hotel' } }, - { name: 'Volvo', - url: 'https://zekeultra.bandcamp.com/track/volvo-2', - imageUrl: 'https://f4.bcbits.com/img/a3185032099_9.jpg', - seekPosition: 4407, - artist: - { name: 'ZekeUltra', - url: 'https://zekeultra.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/15204938_21.jpg', - location: 'Delaware' }, - album: - { name: 'From Time', - url: 'https://zekeultra.bandcamp.com/album/from-time' } }, - { name: 'if you slide', - url: 'https://baghead1.bandcamp.com/track/if-you-slide', - imageUrl: 'https://f4.bcbits.com/img/a1167110350_9.jpg', - seekPosition: 4532, - artist: - { name: 'Baghead', - url: 'https://baghead1.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/13162500_21.jpg', - location: 'California' }, - album: null }, - { name: 'Out The Window', - url: 'https://lovillage.bandcamp.com/track/out-the-window', - imageUrl: 'https://f4.bcbits.com/img/a2948500240_9.jpg', - seekPosition: 4613, - artist: - { name: 'Lo Village', - url: 'https://lovillage.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/21829714_21.jpg', - location: 'Maryland' }, - album: null }, - { name: 'PROTECT feat Jack Stephenson Oliver', - url: 'https://morriarchi.bandcamp.com/track/protect-feat-jack-stephenson-oliver', - imageUrl: 'https://f4.bcbits.com/img/a2544455082_9.jpg', - seekPosition: 4841, - artist: - { name: 'Morriarchi', - url: 'https://morriarchi.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/6940676_21.jpg', - location: 'London, UK' }, - album: - { name: 'Geography of Peace', - url: 'https://morriarchi.bandcamp.com/album/geography-of-peace' } }, - { name: 'Constellations (feat. King Isis)', - url: 'https://stari.bandcamp.com/track/constellations-feat-king-isis', - imageUrl: 'https://f4.bcbits.com/img/a3631955375_9.jpg', - seekPosition: 4977, - artist: - { name: 'Stari', - url: 'https://stari.bandcamp.com', - imageUrl: 'https://f4.bcbits.com/img/22116052_21.jpg', - location: 'Oakland, California' }, - album: null } ] } diff --git a/examples/getTagInfo.js b/examples/getTagInfo.js deleted file mode 100644 index ae4be85..0000000 --- a/examples/getTagInfo.js +++ /dev/null @@ -1,10 +0,0 @@ -const bcfetch = require('../'); -const util = require('util'); - -const tagUrl = 'https://bandcamp.com/tag/dark-ambient'; - -bcfetch.getTagInfo(tagUrl).then( results => { - console.log(util.inspect(results, false, null, false)); -}); - - diff --git a/examples/getTagInfo_output.txt b/examples/getTagInfo_output.txt deleted file mode 100644 index 0086dd7..0000000 --- a/examples/getTagInfo_output.txt +++ /dev/null @@ -1,80 +0,0 @@ -{ type: 'tag', - name: 'dark ambient', - url: 'https://bandcamp.com/tag/dark-ambient', - value: 'dark-ambient', - relatedTags: - [ { type: 'tag', - name: 'dungeon synth', - url: 'https://bandcamp.com/tag/dungeon-synth', - value: 'dungeon-synth', - isLocation: false }, - { type: 'tag', - name: 'drone ambient', - url: 'https://bandcamp.com/tag/drone-ambient', - value: 'drone-ambient', - isLocation: false }, - { type: 'tag', - name: 'drone', - url: 'https://bandcamp.com/tag/drone', - value: 'drone', - isLocation: false }, - { type: 'tag', - name: 'atmospheric', - url: 'https://bandcamp.com/tag/atmospheric', - value: 'atmospheric', - isLocation: false }, - { type: 'tag', - name: 'field recordings', - url: 'https://bandcamp.com/tag/field-recordings', - value: 'field-recordings', - isLocation: false }, - { type: 'tag', - name: 'soundscape', - url: 'https://bandcamp.com/tag/soundscape', - value: 'soundscape', - isLocation: false }, - { type: 'tag', - name: 'ambient', - url: 'https://bandcamp.com/tag/ambient', - value: 'ambient', - isLocation: false }, - { type: 'tag', - name: 'industrial', - url: 'https://bandcamp.com/tag/industrial', - value: 'industrial', - isLocation: false }, - { type: 'tag', - name: 'harsh noise', - url: 'https://bandcamp.com/tag/harsh-noise', - value: 'harsh-noise', - isLocation: false }, - { type: 'tag', - name: 'ambient electronic', - url: 'https://bandcamp.com/tag/ambient-electronic', - value: 'ambient-electronic', - isLocation: false }, - { type: 'tag', - name: 'noise', - url: 'https://bandcamp.com/tag/noise', - value: 'noise', - isLocation: false }, - { type: 'tag', - name: 'experimental electronic', - url: 'https://bandcamp.com/tag/experimental-electronic', - value: 'experimental-electronic', - isLocation: false }, - { type: 'tag', - name: 'Russia', - url: 'https://bandcamp.com/tag/russia', - value: 'russia', - isLocation: true }, - { type: 'tag', - name: 'soundtrack', - url: 'https://bandcamp.com/tag/soundtrack', - value: 'soundtrack', - isLocation: false }, - { type: 'tag', - name: 'black metal', - url: 'https://bandcamp.com/tag/black-metal', - value: 'black-metal', - isLocation: false } ] } diff --git a/examples/getTags.js b/examples/getTags.js deleted file mode 100644 index 21bd7ed..0000000 --- a/examples/getTags.js +++ /dev/null @@ -1,6 +0,0 @@ -const bcfetch = require('../'); -const util = require('util'); - -bcfetch.getTags().then( results => { - console.log(util.inspect(results, false, null, false)); -}); \ No newline at end of file diff --git a/examples/getTags_output.txt b/examples/getTags_output.txt deleted file mode 100644 index 0dc501f..0000000 --- a/examples/getTags_output.txt +++ /dev/null @@ -1,260 +0,0 @@ -{ tags: - [ { name: 'electronic', - url: 'https://bandcamp.com/tag/electronic' }, - { name: 'experimental', - url: 'https://bandcamp.com/tag/experimental' }, - { name: 'rock', url: 'https://bandcamp.com/tag/rock' }, - { name: 'alternative', - url: 'https://bandcamp.com/tag/alternative' }, - { name: 'hip-hop/rap', - url: 'https://bandcamp.com/tag/hip-hop-rap' }, - { name: 'ambient', url: 'https://bandcamp.com/tag/ambient' }, - { name: 'metal', url: 'https://bandcamp.com/tag/metal' }, - { name: 'punk', url: 'https://bandcamp.com/tag/punk' }, - { name: 'indie', url: 'https://bandcamp.com/tag/indie' }, - { name: 'noise', url: 'https://bandcamp.com/tag/noise' }, - { name: 'techno', url: 'https://bandcamp.com/tag/techno' }, - { name: 'indie rock', - url: 'https://bandcamp.com/tag/indie-rock' }, - { name: 'pop', url: 'https://bandcamp.com/tag/pop' }, - { name: 'instrumental', - url: 'https://bandcamp.com/tag/instrumental' }, - { name: 'hip hop', url: 'https://bandcamp.com/tag/hip-hop' }, - { name: 'drone', url: 'https://bandcamp.com/tag/drone' }, - { name: 'rap', url: 'https://bandcamp.com/tag/rap' }, - { name: 'acoustic', url: 'https://bandcamp.com/tag/acoustic' }, - { name: 'folk', url: 'https://bandcamp.com/tag/folk' }, - { name: 'electronica', - url: 'https://bandcamp.com/tag/electronica' }, - { name: 'house', url: 'https://bandcamp.com/tag/house' }, - { name: 'psychedelic', - url: 'https://bandcamp.com/tag/psychedelic' }, - { name: 'singer-songwriter', - url: 'https://bandcamp.com/tag/singer-songwriter' }, - { name: 'hardcore', url: 'https://bandcamp.com/tag/hardcore' }, - { name: 'lo-fi', url: 'https://bandcamp.com/tag/lo-fi' }, - { name: 'dark ambient', - url: 'https://bandcamp.com/tag/dark-ambient' }, - { name: 'industrial', - url: 'https://bandcamp.com/tag/industrial' }, - { name: 'jazz', url: 'https://bandcamp.com/tag/jazz' }, - { name: 'alternative rock', - url: 'https://bandcamp.com/tag/alternative-rock' }, - { name: 'hip-hop', url: 'https://bandcamp.com/tag/hip-hop' }, - { name: 'indie pop', url: 'https://bandcamp.com/tag/indie-pop' }, - { name: 'experimental electronic', - url: 'https://bandcamp.com/tag/experimental-electronic' }, - { name: 'world', url: 'https://bandcamp.com/tag/world' }, - { name: 'beats', url: 'https://bandcamp.com/tag/beats' }, - { name: 'electro', url: 'https://bandcamp.com/tag/electro' }, - { name: 'lofi', url: 'https://bandcamp.com/tag/lofi' }, - { name: 'black metal', - url: 'https://bandcamp.com/tag/black-metal' }, - { name: 'soundtrack', - url: 'https://bandcamp.com/tag/soundtrack' }, - { name: 'post-rock', url: 'https://bandcamp.com/tag/post-rock' }, - { name: 'soul', url: 'https://bandcamp.com/tag/soul' }, - { name: 'punk rock', url: 'https://bandcamp.com/tag/punk-rock' }, - { name: 'post-punk', url: 'https://bandcamp.com/tag/post-punk' }, - { name: 'deep house', - url: 'https://bandcamp.com/tag/deep-house' }, - { name: 'shoegaze', url: 'https://bandcamp.com/tag/shoegaze' }, - { name: 'downtempo', url: 'https://bandcamp.com/tag/downtempo' }, - { name: 'death metal', - url: 'https://bandcamp.com/tag/death-metal' }, - { name: 'funk', url: 'https://bandcamp.com/tag/funk' }, - { name: 'dance', url: 'https://bandcamp.com/tag/dance' }, - { name: 'avant-garde', - url: 'https://bandcamp.com/tag/avant-garde' }, - { name: 'minimal', url: 'https://bandcamp.com/tag/minimal' }, - { name: 'blues', url: 'https://bandcamp.com/tag/blues' }, - { name: 'idm', url: 'https://bandcamp.com/tag/idm' }, - { name: 'progressive', - url: 'https://bandcamp.com/tag/progressive' }, - { name: 'improvisation', - url: 'https://bandcamp.com/tag/improvisation' }, - { name: 'vaporwave', url: 'https://bandcamp.com/tag/vaporwave' }, - { name: 'synthwave', url: 'https://bandcamp.com/tag/synthwave' }, - { name: 'emo', url: 'https://bandcamp.com/tag/emo' }, - { name: 'dub', url: 'https://bandcamp.com/tag/dub' }, - { name: 'synthpop', url: 'https://bandcamp.com/tag/synthpop' }, - { name: 'trap', url: 'https://bandcamp.com/tag/trap' }, - { name: 'indie folk', - url: 'https://bandcamp.com/tag/indie-folk' }, - { name: 'drum & bass', - url: 'https://bandcamp.com/tag/drum-bass' }, - { name: 'underground', - url: 'https://bandcamp.com/tag/underground' }, - { name: 'synth', url: 'https://bandcamp.com/tag/synth' }, - { name: 'underground hip hop', - url: 'https://bandcamp.com/tag/underground-hip-hop' }, - { name: 'r&b', url: 'https://bandcamp.com/tag/r-b' }, - { name: 'folk rock', url: 'https://bandcamp.com/tag/folk-rock' }, - { name: 'garage', url: 'https://bandcamp.com/tag/garage' }, - { name: 'piano', url: 'https://bandcamp.com/tag/piano' }, - { name: 'dubstep', url: 'https://bandcamp.com/tag/dubstep' }, - { name: 'instrumental hip-hop', - url: 'https://bandcamp.com/tag/instrumental-hip-hop' }, - { name: 'ambient electronic', - url: 'https://bandcamp.com/tag/ambient-electronic' }, - { name: 'chillout', url: 'https://bandcamp.com/tag/chillout' }, - { name: 'pop rock', url: 'https://bandcamp.com/tag/pop-rock' }, - { name: 'americana', url: 'https://bandcamp.com/tag/americana' }, - { name: 'guitar', url: 'https://bandcamp.com/tag/guitar' }, - { name: 'electronic music', - url: 'https://bandcamp.com/tag/electronic-music' }, - { name: 'chill', url: 'https://bandcamp.com/tag/chill' }, - { name: 'tech house', - url: 'https://bandcamp.com/tag/tech-house' }, - { name: 'progressive rock', - url: 'https://bandcamp.com/tag/progressive-rock' }, - { name: 'hardcore punk', - url: 'https://bandcamp.com/tag/hardcore-punk' }, - { name: 'harsh noise', - url: 'https://bandcamp.com/tag/harsh-noise' }, - { name: 'pop punk', url: 'https://bandcamp.com/tag/pop-punk' }, - { name: 'psychedelic rock', - url: 'https://bandcamp.com/tag/psychedelic-rock' }, - { name: 'soundscape', - url: 'https://bandcamp.com/tag/soundscape' }, - { name: 'classical', url: 'https://bandcamp.com/tag/classical' }, - { name: 'atmospheric', - url: 'https://bandcamp.com/tag/atmospheric' }, - { name: 'grunge', url: 'https://bandcamp.com/tag/grunge' }, - { name: 'garage rock', - url: 'https://bandcamp.com/tag/garage-rock' }, - { name: 'psytrance', url: 'https://bandcamp.com/tag/psytrance' }, - { name: 'dream pop', url: 'https://bandcamp.com/tag/dream-pop' }, - { name: 'edm', url: 'https://bandcamp.com/tag/edm' }, - { name: 'hard rock', url: 'https://bandcamp.com/tag/hard-rock' }, - { name: 'trance', url: 'https://bandcamp.com/tag/trance' }, - { name: 'reggae', url: 'https://bandcamp.com/tag/reggae' }, - { name: 'country', url: 'https://bandcamp.com/tag/country' }, - { name: 'bass', url: 'https://bandcamp.com/tag/bass' }, - { name: 'diy', url: 'https://bandcamp.com/tag/diy' }, - { name: 'hiphop', url: 'https://bandcamp.com/tag/hiphop' }, - { name: 'grindcore', url: 'https://bandcamp.com/tag/grindcore' }, - ... 226 more items ], - locations: - [ { name: 'United Kingdom', - url: 'https://bandcamp.com/tag/united-kingdom' }, - { name: 'California', - url: 'https://bandcamp.com/tag/california' }, - { name: 'England', url: 'https://bandcamp.com/tag/england' }, - { name: 'Canada', url: 'https://bandcamp.com/tag/canada' }, - { name: 'Germany', url: 'https://bandcamp.com/tag/germany' }, - { name: 'France', url: 'https://bandcamp.com/tag/france' }, - { name: 'New York', url: 'https://bandcamp.com/tag/new-york' }, - { name: 'Australia', url: 'https://bandcamp.com/tag/australia' }, - { name: 'USA', url: 'https://bandcamp.com/tag/usa' }, - { name: 'Spain', url: 'https://bandcamp.com/tag/spain' }, - { name: 'Russia', url: 'https://bandcamp.com/tag/russia' }, - { name: 'Los Angeles', - url: 'https://bandcamp.com/tag/los-angeles' }, - { name: 'Italy', url: 'https://bandcamp.com/tag/italy' }, - { name: 'Texas', url: 'https://bandcamp.com/tag/texas' }, - { name: 'Illinois', url: 'https://bandcamp.com/tag/illinois' }, - { name: 'Ontario', url: 'https://bandcamp.com/tag/ontario' }, - { name: 'London', url: 'https://bandcamp.com/tag/london' }, - { name: 'Pennsylvania', - url: 'https://bandcamp.com/tag/pennsylvania' }, - { name: 'Japan', url: 'https://bandcamp.com/tag/japan' }, - { name: 'Berlin', url: 'https://bandcamp.com/tag/berlin' }, - { name: 'Argentina', url: 'https://bandcamp.com/tag/argentina' }, - { name: 'Chicago', url: 'https://bandcamp.com/tag/chicago' }, - { name: 'Washington', - url: 'https://bandcamp.com/tag/washington' }, - { name: 'Florida', url: 'https://bandcamp.com/tag/florida' }, - { name: 'Brazil', url: 'https://bandcamp.com/tag/brazil' }, - { name: 'Massachusetts', - url: 'https://bandcamp.com/tag/massachusetts' }, - { name: 'Netherlands', - url: 'https://bandcamp.com/tag/netherlands' }, - { name: 'Michigan', url: 'https://bandcamp.com/tag/michigan' }, - { name: 'Oregon', url: 'https://bandcamp.com/tag/oregon' }, - { name: 'Ohio', url: 'https://bandcamp.com/tag/ohio' }, - { name: 'Québec', url: 'https://bandcamp.com/tag/qu%C3%A9bec' }, - { name: 'Sweden', url: 'https://bandcamp.com/tag/sweden' }, - { name: 'Mexico', url: 'https://bandcamp.com/tag/mexico' }, - { name: 'VIC', url: 'https://bandcamp.com/tag/vic' }, - { name: 'Seattle', url: 'https://bandcamp.com/tag/seattle' }, - { name: 'Toronto', url: 'https://bandcamp.com/tag/toronto' }, - { name: 'Melbourne', url: 'https://bandcamp.com/tag/melbourne' }, - { name: 'Portland', url: 'https://bandcamp.com/tag/portland' }, - { name: 'British Columbia', - url: 'https://bandcamp.com/tag/british-columbia' }, - { name: 'North Carolina', - url: 'https://bandcamp.com/tag/north-carolina' }, - { name: 'Georgia', url: 'https://bandcamp.com/tag/georgia' }, - { name: 'Belgium', url: 'https://bandcamp.com/tag/belgium' }, - { name: 'Colorado', url: 'https://bandcamp.com/tag/colorado' }, - { name: 'New Jersey', - url: 'https://bandcamp.com/tag/new-jersey' }, - { name: 'Poland', url: 'https://bandcamp.com/tag/poland' }, - { name: 'Philadelphia', - url: 'https://bandcamp.com/tag/philadelphia' }, - { name: 'Tennessee', url: 'https://bandcamp.com/tag/tennessee' }, - { name: 'UK', url: 'https://bandcamp.com/tag/uk' }, - { name: 'Virginia', url: 'https://bandcamp.com/tag/virginia' }, - { name: 'San Francisco', - url: 'https://bandcamp.com/tag/san-francisco' }, - { name: 'Minnesota', url: 'https://bandcamp.com/tag/minnesota' }, - { name: 'Portugal', url: 'https://bandcamp.com/tag/portugal' }, - { name: 'Montreal', url: 'https://bandcamp.com/tag/montreal' }, - { name: 'Boston', url: 'https://bandcamp.com/tag/boston' }, - { name: 'Finland', url: 'https://bandcamp.com/tag/finland' }, - { name: 'Austin', url: 'https://bandcamp.com/tag/austin' }, - { name: 'Île-de-France', - url: 'https://bandcamp.com/tag/%C3%8Ele-de-france' }, - { name: 'NSW', url: 'https://bandcamp.com/tag/nsw' }, - { name: 'Switzerland', - url: 'https://bandcamp.com/tag/switzerland' }, - { name: 'Scotland', url: 'https://bandcamp.com/tag/scotland' }, - { name: 'Arizona', url: 'https://bandcamp.com/tag/arizona' }, - { name: 'Paris', url: 'https://bandcamp.com/tag/paris' }, - { name: 'Maryland', url: 'https://bandcamp.com/tag/maryland' }, - { name: 'New Zealand', - url: 'https://bandcamp.com/tag/new-zealand' }, - { name: 'Greece', url: 'https://bandcamp.com/tag/greece' }, - { name: 'Missouri', url: 'https://bandcamp.com/tag/missouri' }, - { name: 'Atlanta', url: 'https://bandcamp.com/tag/atlanta' }, - { name: 'Chile', url: 'https://bandcamp.com/tag/chile' }, - { name: 'Vancouver', url: 'https://bandcamp.com/tag/vancouver' }, - { name: 'CT', url: 'https://bandcamp.com/tag/ct' }, - { name: 'Indiana', url: 'https://bandcamp.com/tag/indiana' }, - { name: 'Wisconsin', url: 'https://bandcamp.com/tag/wisconsin' }, - { name: 'Brooklyn', url: 'https://bandcamp.com/tag/brooklyn' }, - { name: 'NRW', url: 'https://bandcamp.com/tag/nrw' }, - { name: 'Denmark', url: 'https://bandcamp.com/tag/denmark' }, - { name: 'Denver', url: 'https://bandcamp.com/tag/denver' }, - { name: 'Austria', url: 'https://bandcamp.com/tag/austria' }, - { name: 'IDF', url: 'https://bandcamp.com/tag/idf' }, - { name: 'Ireland', url: 'https://bandcamp.com/tag/ireland' }, - { name: 'Ukraine', url: 'https://bandcamp.com/tag/ukraine' }, - { name: 'Minneapolis', - url: 'https://bandcamp.com/tag/minneapolis' }, - { name: 'Norway', url: 'https://bandcamp.com/tag/norway' }, - { name: 'Sydney', url: 'https://bandcamp.com/tag/sydney' }, - { name: 'Detroit', url: 'https://bandcamp.com/tag/detroit' }, - { name: 'Barcelona', url: 'https://bandcamp.com/tag/barcelona' }, - { name: 'Nashville', url: 'https://bandcamp.com/tag/nashville' }, - { name: 'CABA', url: 'https://bandcamp.com/tag/caba' }, - { name: 'Buenos Aires', - url: 'https://bandcamp.com/tag/buenos-aires' }, - { name: 'Tokyo', url: 'https://bandcamp.com/tag/tokyo' }, - { name: 'Israel', url: 'https://bandcamp.com/tag/israel' }, - { name: 'Connecticut', - url: 'https://bandcamp.com/tag/connecticut' }, - { name: 'Hungary', url: 'https://bandcamp.com/tag/hungary' }, - { name: 'Alberta', url: 'https://bandcamp.com/tag/alberta' }, - { name: 'District of Columbia', - url: 'https://bandcamp.com/tag/district-of-columbia' }, - { name: 'San Diego', url: 'https://bandcamp.com/tag/san-diego' }, - { name: 'Moscow', url: 'https://bandcamp.com/tag/moscow' }, - { name: 'Community of Madrid', - url: 'https://bandcamp.com/tag/community-of-madrid' }, - { name: 'Pittsburgh', - url: 'https://bandcamp.com/tag/pittsburgh' }, - { name: 'Madrid', url: 'https://bandcamp.com/tag/madrid' }, - { name: 'SP', url: 'https://bandcamp.com/tag/sp' }, - ... 241 more items ] } diff --git a/examples/getTrackInfo.js b/examples/getTrackInfo.js deleted file mode 100644 index 44705a8..0000000 --- a/examples/getTrackInfo.js +++ /dev/null @@ -1,14 +0,0 @@ -const bcfetch = require('../'); -const util = require('util'); - -const trackUrl = 'https://musique.coeurdepirate.com/track/tes-belle'; - -const options = { - albumImageFormat: 'art_app_large', - artistImageFormat: 'bio_featured', - includeRawData: false -} - -bcfetch.getTrackInfo(trackUrl, options).then( results => { - console.log(util.inspect(results, false, null, false)); -}); \ No newline at end of file diff --git a/examples/getTrackInfo_output.txt b/examples/getTrackInfo_output.txt deleted file mode 100644 index aa52c15..0000000 --- a/examples/getTrackInfo_output.txt +++ /dev/null @@ -1,21 +0,0 @@ -{ type: 'track', - name: 'T\'es belle', - url: 'https://musique.coeurdepirate.com/track/tes-belle', - imageUrl: 'https://f4.bcbits.com/img/a774650359_16.jpg', - releaseDate: '01 Oct 2020 00:00:00 GMT', - duration: 176.373, - streamUrl: 'https://t4.bcbits.com/stream/63315678db5ea41d2f0417ac7d4f5ca3/mp3-128/3387079907?p=0&ts=1634748723&t=4cbd86e4f7d1e19e198f19fd909de81896b004bf&token=1634748723_5e0056a064e48e7dfa433c877cd46e30d4cafd37', - artist: - { name: 'Cœur de pirate', - url: 'https://musique.coeurdepirate.com', - description: 'Plus d’une décennie s’est écoulée depuis que Béatrice Martin s’est incrustée dans le paysage sous le pseudonyme désormais coutumier de Cœur de pirate. Armée d’un talent digne de l’orfèvrerie, d’une poésie tantôt raffinée, tantôt subversive, et d’une aura insaisissable, elle séduit comme elle surprend, jaillissant là où on ne l’attend pas.', - imageUrl: 'https://f4.bcbits.com/img/0026415167_28.jpg' }, - publisher: - { name: 'Cœur de pirate', - url: 'https://musique.coeurdepirate.com', - description: 'Plus d’une décennie s’est écoulée depuis que Béatrice Martin s’est incrustée dans le paysage sous le pseudonyme désormais coutumier de Cœur de pirate. Armée d’un talent digne de l’orfèvrerie, d’une poésie tantôt raffinée, tantôt subversive, et d’une aura insaisissable, elle séduit comme elle surprend, jaillissant là où on ne l’attend pas.', - imageUrl: 'https://f4.bcbits.com/img/0026415167_28.jpg' }, - label: - { name: 'Bravo musique', - url: 'https://bravomusique.bandcamp.com' }, - album: null } diff --git a/examples/image/getFormats.ts b/examples/image/getFormats.ts new file mode 100644 index 0000000..11e0f85 --- /dev/null +++ b/examples/image/getFormats.ts @@ -0,0 +1,13 @@ +import bcfetch, { ImageFormatFilter } from '../../'; + +bcfetch.image.getFormats(ImageFormatFilter.Album).then((results) => { + console.log('Album image formats:'); + console.log(results); + console.log(); +}); + +bcfetch.image.getFormats(ImageFormatFilter.Bio).then((results) => { + console.log('Artist image formats:'); + console.log(results); + console.log(); +}); diff --git a/examples/getImageFormats_output.txt b/examples/image/getFormats_output.txt similarity index 60% rename from examples/getImageFormats_output.txt rename to examples/image/getFormats_output.txt index b525560..50e1c9b 100644 --- a/examples/getImageFormats_output.txt +++ b/examples/image/getFormats_output.txt @@ -3,83 +3,72 @@ Artist image formats: { id: 20, name: 'bio_screen', - resize_algo: 'fit', width: 1024, height: 1024, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 21, name: 'bio_thumb', - resize_algo: 'fit', width: 120, height: 180, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 22, name: 'bio_navbar', - resize_algo: 'thumb', width: 25, height: 25, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 23, name: 'bio_phone', - resize_algo: 'fit', width: 300, height: 300, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 24, name: 'bio_licensing', - resize_algo: 'thumb', width: 300, height: 300, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 25, name: 'bio_app', - resize_algo: 'fit', width: 700, height: 700, - file_format: 'JPEG', - quality: 70 + fileFormat: 'JPEG' }, { id: 26, name: 'bio_subscribe', - resize_algo: 'thumb', width: 800, height: 600, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 27, name: 'bio_subscribe2', - resize_algo: 'thumb', width: 715, height: 402, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 28, name: 'bio_featured', - resize_algo: 'thumb', width: 768, height: 432, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 29, name: 'bio_autocomplete', - resize_algo: 'thumb', width: 100, height: 75, - file_format: 'JPEG' + fileFormat: 'JPEG' } ] @@ -88,143 +77,121 @@ Album image formats: { id: 2, name: 'art_thumb', - resize_algo: 'thumb', width: 350, height: 350, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 3, name: 'art_thumbthumb', - resize_algo: 'thumb', width: 100, height: 100, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 4, name: 'art_embedded_metadata', - resize_algo: 'thumb', width: 300, height: 300, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 5, name: 'art_embedded_metadata_large', - resize_algo: 'thumb', width: 700, height: 700, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 6, name: 'art_embedded_player', - resize_algo: 'thumb', width: 100, height: 100, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 7, name: 'art_embedded_player_large', - resize_algo: 'thumb', width: 150, height: 150, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 8, name: 'art_tags', - resize_algo: 'thumb', width: 124, height: 124, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 9, name: 'art_tags_large', - resize_algo: 'thumb', width: 210, height: 210, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 11, name: 'art_tag_search', - resize_algo: 'thumb', width: 172, height: 172, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 12, name: 'art_artist_index', - resize_algo: 'thumb', width: 138, height: 138, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 13, name: 'art_solo_feature', - resize_algo: 'thumb', width: 380, height: 380, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 14, name: 'art_feature', - resize_algo: 'thumb', width: 368, height: 368, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 15, name: 'art_feed_new_release', - resize_algo: 'thumb', width: 135, height: 135, - file_format: 'JPEG' + fileFormat: 'JPEG' }, { id: 16, name: 'art_app_large', - resize_algo: 'thumb', width: 700, height: 700, - file_format: 'JPEG', - quality: 70, - minsize: { size: 30000, format: 5 } + fileFormat: 'JPEG' }, { id: 67, name: 'art_thumb_anim_ok', - resize_algo: 'thumb', width: 350, height: 350, - file_format: 'JPEG', - anim_ok: true + fileFormat: 'JPEG' }, { id: 68, name: 'art_tags_large_anim_ok', - resize_algo: 'thumb', width: 210, height: 210, - file_format: 'JPEG', - anim_ok: true + fileFormat: 'JPEG' }, { id: 69, name: 'art_embedded_metadata_large_anim_ok', - resize_algo: 'thumb', width: 700, height: 700, - file_format: 'JPEG', - anim_ok: true + fileFormat: 'JPEG' } ] diff --git a/examples/limiter.js b/examples/limiter.js deleted file mode 100644 index d3ba99f..0000000 --- a/examples/limiter.js +++ /dev/null @@ -1,90 +0,0 @@ -const bcfetch = require('../'); - -bcfetch.limiter.updateSettings({ - maxConcurrent: 10, - minTime: 100 -}); - -const albumUrls = [ - 'https://phoebebridgers.bandcamp.com/album/punisher', - 'https://mrsgreenbird.bandcamp.com/album/10-years-live', - 'https://ryleywalker.bandcamp.com/album/course-in-fable', - 'https://phoebebridgers.bandcamp.com/album/stranger-in-the-alps', - 'https://wearetyphoon.bandcamp.com/album/sympathetic-magic', - 'https://mariabc.bandcamp.com/album/devils-rain-2', - 'https://jimpullman.bandcamp.com/album/go-on-boldly', - 'https://debutants.bandcamp.com/album/indiana-newgrass-ep', - 'https://cassandrajenkins.bandcamp.com/album/an-overview-on-phenomenal-nature', - 'https://stefonaclearday.bandcamp.com/album/songs-of-love-and-unlove', - 'https://haleyheynderickx.bandcamp.com/album/i-need-to-start-a-garden', - 'https://phoebebridgers.bandcamp.com/album/copycat-killer', - 'https://grantleephillips.bandcamp.com/album/rag-town-pink-rebel', - 'https://garlandofhours.bandcamp.com/album/lucidia', - 'https://landehekt.bandcamp.com/album/going-to-hell', - 'https://emmaswift.bandcamp.com/album/blonde-on-the-tracks', - 'https://joannanewsom.bandcamp.com/album/ys', - 'https://joannanewsom.bandcamp.com/album/have-one-on-me', - 'https://saintseneca.bandcamp.com/album/all-youve-got-is-everyone', - 'https://phoebebridgers.bandcamp.com/album/if-we-make-it-through-december', - 'https://music.theohhellos.com/album/boreas', - 'https://rikkiwill.bandcamp.com/album/songs-for-rivers', - 'https://alexsalcido.bandcamp.com/album/im-a-bird', - 'https://helenadeland.bandcamp.com/album/someone-new', - 'https://ryleywalker.bandcamp.com/album/for-michael-ripps', - 'https://thebargain.bandcamp.com/album/yes-b-w-stay-awhile', - 'https://curtismcmurtry.bandcamp.com/album/toothless-messiah', - 'https://ceciliablairwright.bandcamp.com/album/another-human', - 'https://darrenhayman.bandcamp.com/album/music-to-watch-news-by', - 'https://virginiaastley.bandcamp.com/album/from-gardens-where-we-feel-secure', - 'https://radicalface.bandcamp.com/album/hidden-hollow-vol-one-singles', - 'https://miloandlovina.bandcamp.com/album/paper-hearts', - 'https://fatherjohnmisty.bandcamp.com/album/anthem-3-2', - 'https://thefauxpaws.bandcamp.com/album/the-hurricane-ep', - 'https://mrsgreenbird.bandcamp.com/album/dark-waters', - 'https://indigosparke.bandcamp.com/album/echo', - 'https://joannanewsom.bandcamp.com/album/divers', - 'https://kellymcfarling.bandcamp.com/album/deep-the-habit', - 'https://dogwood.bandcamp.com/album/the-imperfect-ep', - 'https://bedouine.bandcamp.com/album/bedouine', - 'https://craigmckerron.bandcamp.com/album/cabin-fever', - 'https://imskullcrusher.bandcamp.com/album/skullcrusher', - 'https://fleetfoxes.bandcamp.com/album/crack-up', - 'https://anothermichael.bandcamp.com/album/new-music-and-big-pop', - 'https://autourdelucie.bandcamp.com/album/bunker', - 'https://laurastevenson.bandcamp.com/album/sit-resist-remastered-deluxe-edition' -]; - -const options = { - albumImageFormat: 'art_app_large', - artistImageFormat: 'bio_featured', - includeRawData: false -} - -let fetchPromises = []; -albumUrls.forEach( url => { - fetchPromises.push(bcfetch.limiter.getAlbumInfo(url, options).then( result => { - console.log(`Resolved: ${url}`) - return result; - })); -}); - -console.log('Resolving album URLs with limiter...'); -Promise.all(fetchPromises).then( results => { - console.log(`Total ${results.length} URLs resolved!`); - console.log(''); - console.log('Now let\'s see what happens when we don\'t use limiter...'); - - fetchPromises = []; - albumUrls.forEach( url => { - fetchPromises.push(bcfetch.getAlbumInfo(url, options).then( result => { - console.log(`Resolved: ${url}`) - return result; - })); - }); - bcfetch.cache.clear(); - Promise.all(fetchPromises).then( results => { - console.log(`Total ${results.length} URLs resolved!`); - }).catch( error => { - console.log(`An error occurred: ${error.message}`); - }); -}); \ No newline at end of file diff --git a/examples/limiter/limiter.ts b/examples/limiter/limiter.ts new file mode 100644 index 0000000..fc9183d --- /dev/null +++ b/examples/limiter/limiter.ts @@ -0,0 +1,96 @@ +import bcfetch from '../../'; + +bcfetch.limiter.updateSettings({ + maxConcurrent: 10, + minTime: 100 +}); + +const albumUrls = [ + 'https://phoebebridgers.bandcamp.com/album/punisher', + 'https://mrsgreenbird.bandcamp.com/album/10-years-live', + 'https://ryleywalker.bandcamp.com/album/course-in-fable', + 'https://phoebebridgers.bandcamp.com/album/stranger-in-the-alps', + 'https://wearetyphoon.bandcamp.com/album/sympathetic-magic', + 'https://mariabc.bandcamp.com/album/devils-rain-2', + 'https://jimpullman.bandcamp.com/album/go-on-boldly', + 'https://debutants.bandcamp.com/album/indiana-newgrass-ep', + 'https://cassandrajenkins.bandcamp.com/album/an-overview-on-phenomenal-nature', + 'https://stefonaclearday.bandcamp.com/album/songs-of-love-and-unlove', + 'https://haleyheynderickx.bandcamp.com/album/i-need-to-start-a-garden', + 'https://phoebebridgers.bandcamp.com/album/copycat-killer', + 'https://grantleephillips.bandcamp.com/album/rag-town-pink-rebel', + 'https://garlandofhours.bandcamp.com/album/lucidia', + 'https://landehekt.bandcamp.com/album/going-to-hell', + 'https://emmaswift.bandcamp.com/album/blonde-on-the-tracks', + 'https://joannanewsom.bandcamp.com/album/ys', + 'https://joannanewsom.bandcamp.com/album/have-one-on-me', + 'https://saintseneca.bandcamp.com/album/all-youve-got-is-everyone', + 'https://phoebebridgers.bandcamp.com/album/if-we-make-it-through-december', + 'https://music.theohhellos.com/album/boreas', + 'https://rikkiwill.bandcamp.com/album/songs-for-rivers', + 'https://alexsalcido.bandcamp.com/album/im-a-bird', + 'https://helenadeland.bandcamp.com/album/someone-new', + 'https://ryleywalker.bandcamp.com/album/for-michael-ripps', + 'https://thebargain.bandcamp.com/album/yes-b-w-stay-awhile', + 'https://curtismcmurtry.bandcamp.com/album/toothless-messiah', + 'https://ceciliablairwright.bandcamp.com/album/another-human', + 'https://darrenhayman.bandcamp.com/album/music-to-watch-news-by', + 'https://virginiaastley.bandcamp.com/album/from-gardens-where-we-feel-secure', + 'https://radicalface.bandcamp.com/album/hidden-hollow-vol-one-singles', + 'https://miloandlovina.bandcamp.com/album/paper-hearts', + 'https://fatherjohnmisty.bandcamp.com/album/anthem-3-2', + 'https://thefauxpaws.bandcamp.com/album/the-hurricane-ep', + 'https://mrsgreenbird.bandcamp.com/album/dark-waters', + 'https://indigosparke.bandcamp.com/album/echo', + 'https://joannanewsom.bandcamp.com/album/divers', + 'https://kellymcfarling.bandcamp.com/album/deep-the-habit', + 'https://dogwood.bandcamp.com/album/the-imperfect-ep', + 'https://bedouine.bandcamp.com/album/bedouine', + 'https://craigmckerron.bandcamp.com/album/cabin-fever', + 'https://imskullcrusher.bandcamp.com/album/skullcrusher', + 'https://fleetfoxes.bandcamp.com/album/crack-up', + 'https://anothermichael.bandcamp.com/album/new-music-and-big-pop', + 'https://autourdelucie.bandcamp.com/album/bunker', + 'https://laurastevenson.bandcamp.com/album/sit-resist-remastered-deluxe-edition' +]; + +const baseParams = { + albumImageFormat: 'art_app_large', + artistImageFormat: 'bio_featured', + includeRawData: false +}; + +console.time('Execution time with limiter'); +let fetchPromises = albumUrls.map(async (url) => { + const result = await bcfetch.limiter.album.getInfo({ ...baseParams, albumUrl: url }); + console.log(`Resolved: ${url}`); + return result; +}); + +console.log('Resolving album URLs with limiter...'); + +Promise.all(fetchPromises).then((results) => { + + console.log(`Total ${results.length} URLs resolved!`); + console.timeEnd('Execution time with limiter'); + console.log(); + + console.log('Now let\'s see what happens when we don\'t use limiter...'); + + bcfetch.cache.clear(); + + console.time('Execution time without limiter'); + fetchPromises = albumUrls.map(async (url) => { + const result = await bcfetch.album.getInfo({ ...baseParams, albumUrl: url }); + console.log(`Resolved: ${url}`); + return result; + }); + + Promise.all(fetchPromises).then((results) => { + console.log(`Total ${results.length} URLs resolved!`); + console.timeEnd('Execution time without limiter'); + }).catch((error) => { + console.log(`An error occurred: ${error.message}`); + }); + +}); diff --git a/examples/limiter_output.txt b/examples/limiter/limiter_output.txt similarity index 53% rename from examples/limiter_output.txt rename to examples/limiter/limiter_output.txt index b194f1a..5010546 100644 --- a/examples/limiter_output.txt +++ b/examples/limiter/limiter_output.txt @@ -1,55 +1,99 @@ Resolving album URLs with limiter... -Resolved: https://wearetyphoon.bandcamp.com/album/sympathetic-magic -Resolved: https://stefonaclearday.bandcamp.com/album/songs-of-love-and-unlove -Resolved: https://phoebebridgers.bandcamp.com/album/stranger-in-the-alps Resolved: https://phoebebridgers.bandcamp.com/album/punisher -Resolved: https://debutants.bandcamp.com/album/indiana-newgrass-ep Resolved: https://mrsgreenbird.bandcamp.com/album/10-years-live Resolved: https://jimpullman.bandcamp.com/album/go-on-boldly +Resolved: https://phoebebridgers.bandcamp.com/album/stranger-in-the-alps Resolved: https://mariabc.bandcamp.com/album/devils-rain-2 -Resolved: https://ryleywalker.bandcamp.com/album/course-in-fable Resolved: https://cassandrajenkins.bandcamp.com/album/an-overview-on-phenomenal-nature -Resolved: https://haleyheynderickx.bandcamp.com/album/i-need-to-start-a-garden +Resolved: https://stefonaclearday.bandcamp.com/album/songs-of-love-and-unlove +Resolved: https://wearetyphoon.bandcamp.com/album/sympathetic-magic +Resolved: https://ryleywalker.bandcamp.com/album/course-in-fable +Resolved: https://debutants.bandcamp.com/album/indiana-newgrass-ep +Resolved: https://phoebebridgers.bandcamp.com/album/copycat-killer Resolved: https://grantleephillips.bandcamp.com/album/rag-town-pink-rebel Resolved: https://garlandofhours.bandcamp.com/album/lucidia -Resolved: https://joannanewsom.bandcamp.com/album/ys -Resolved: https://phoebebridgers.bandcamp.com/album/copycat-killer -Resolved: https://emmaswift.bandcamp.com/album/blonde-on-the-tracks +Resolved: https://haleyheynderickx.bandcamp.com/album/i-need-to-start-a-garden Resolved: https://landehekt.bandcamp.com/album/going-to-hell -Resolved: https://joannanewsom.bandcamp.com/album/have-one-on-me Resolved: https://music.theohhellos.com/album/boreas -Resolved: https://saintseneca.bandcamp.com/album/all-youve-got-is-everyone -Resolved: https://phoebebridgers.bandcamp.com/album/if-we-make-it-through-december +Resolved: https://emmaswift.bandcamp.com/album/blonde-on-the-tracks Resolved: https://alexsalcido.bandcamp.com/album/im-a-bird -Resolved: https://helenadeland.bandcamp.com/album/someone-new -Resolved: https://ceciliablairwright.bandcamp.com/album/another-human -Resolved: https://darrenhayman.bandcamp.com/album/music-to-watch-news-by -Resolved: https://radicalface.bandcamp.com/album/hidden-hollow-vol-one-singles Resolved: https://rikkiwill.bandcamp.com/album/songs-for-rivers -Resolved: https://virginiaastley.bandcamp.com/album/from-gardens-where-we-feel-secure -Resolved: https://miloandlovina.bandcamp.com/album/paper-hearts +Resolved: https://phoebebridgers.bandcamp.com/album/if-we-make-it-through-december +Resolved: https://joannanewsom.bandcamp.com/album/have-one-on-me +Resolved: https://joannanewsom.bandcamp.com/album/ys +Resolved: https://saintseneca.bandcamp.com/album/all-youve-got-is-everyone Resolved: https://thebargain.bandcamp.com/album/yes-b-w-stay-awhile -Resolved: https://mrsgreenbird.bandcamp.com/album/dark-waters -Resolved: https://curtismcmurtry.bandcamp.com/album/toothless-messiah -Resolved: https://thefauxpaws.bandcamp.com/album/the-hurricane-ep -Resolved: https://kellymcfarling.bandcamp.com/album/deep-the-habit -Resolved: https://dogwood.bandcamp.com/album/the-imperfect-ep -Resolved: https://indigosparke.bandcamp.com/album/echo Resolved: https://ryleywalker.bandcamp.com/album/for-michael-ripps -Resolved: https://craigmckerron.bandcamp.com/album/cabin-fever -Resolved: https://imskullcrusher.bandcamp.com/album/skullcrusher +Resolved: https://helenadeland.bandcamp.com/album/someone-new +Resolved: https://curtismcmurtry.bandcamp.com/album/toothless-messiah +Resolved: https://radicalface.bandcamp.com/album/hidden-hollow-vol-one-singles +Resolved: https://miloandlovina.bandcamp.com/album/paper-hearts +Resolved: https://ceciliablairwright.bandcamp.com/album/another-human +Resolved: https://virginiaastley.bandcamp.com/album/from-gardens-where-we-feel-secure +Resolved: https://darrenhayman.bandcamp.com/album/music-to-watch-news-by +Resolved: https://thefauxpaws.bandcamp.com/album/the-hurricane-ep +Resolved: https://mrsgreenbird.bandcamp.com/album/dark-waters +Resolved: https://fatherjohnmisty.bandcamp.com/album/anthem-3-2 +Resolved: https://dogwood.bandcamp.com/album/the-imperfect-ep Resolved: https://joannanewsom.bandcamp.com/album/divers +Resolved: https://kellymcfarling.bandcamp.com/album/deep-the-habit +Resolved: https://indigosparke.bandcamp.com/album/echo +Resolved: https://craigmckerron.bandcamp.com/album/cabin-fever Resolved: https://bedouine.bandcamp.com/album/bedouine +Resolved: https://fleetfoxes.bandcamp.com/album/crack-up +Resolved: https://imskullcrusher.bandcamp.com/album/skullcrusher Resolved: https://autourdelucie.bandcamp.com/album/bunker Resolved: https://anothermichael.bandcamp.com/album/new-music-and-big-pop Resolved: https://laurastevenson.bandcamp.com/album/sit-resist-remastered-deluxe-edition -Resolved: https://fatherjohnmisty.bandcamp.com/album/anthem-3-2 -Resolved: https://fleetfoxes.bandcamp.com/album/crack-up Total 46 URLs resolved! +Execution time with limiter: 10.300s Now let's see what happens when we don't use limiter... -An error occurred: 429 Too Many Requests +Resolved: https://autourdelucie.bandcamp.com/album/bunker Resolved: https://garlandofhours.bandcamp.com/album/lucidia +Resolved: https://ryleywalker.bandcamp.com/album/course-in-fable +Resolved: https://joannanewsom.bandcamp.com/album/have-one-on-me +Resolved: https://bedouine.bandcamp.com/album/bedouine +Resolved: https://mariabc.bandcamp.com/album/devils-rain-2 +Resolved: https://fatherjohnmisty.bandcamp.com/album/anthem-3-2 Resolved: https://craigmckerron.bandcamp.com/album/cabin-fever +Resolved: https://thebargain.bandcamp.com/album/yes-b-w-stay-awhile +Resolved: https://stefonaclearday.bandcamp.com/album/songs-of-love-and-unlove +Resolved: https://debutants.bandcamp.com/album/indiana-newgrass-ep +Resolved: https://rikkiwill.bandcamp.com/album/songs-for-rivers +Resolved: https://landehekt.bandcamp.com/album/going-to-hell +Resolved: https://phoebebridgers.bandcamp.com/album/if-we-make-it-through-december +Resolved: https://jimpullman.bandcamp.com/album/go-on-boldly +Resolved: https://phoebebridgers.bandcamp.com/album/copycat-killer +Resolved: https://cassandrajenkins.bandcamp.com/album/an-overview-on-phenomenal-nature +Resolved: https://grantleephillips.bandcamp.com/album/rag-town-pink-rebel +Resolved: https://dogwood.bandcamp.com/album/the-imperfect-ep +Resolved: https://ceciliablairwright.bandcamp.com/album/another-human +Resolved: https://fleetfoxes.bandcamp.com/album/crack-up +Resolved: https://wearetyphoon.bandcamp.com/album/sympathetic-magic +Resolved: https://emmaswift.bandcamp.com/album/blonde-on-the-tracks +Resolved: https://darrenhayman.bandcamp.com/album/music-to-watch-news-by +Resolved: https://mrsgreenbird.bandcamp.com/album/dark-waters +Resolved: https://thefauxpaws.bandcamp.com/album/the-hurricane-ep +Resolved: https://curtismcmurtry.bandcamp.com/album/toothless-messiah +Resolved: https://haleyheynderickx.bandcamp.com/album/i-need-to-start-a-garden Resolved: https://music.theohhellos.com/album/boreas +Resolved: https://indigosparke.bandcamp.com/album/echo +Resolved: https://phoebebridgers.bandcamp.com/album/punisher +Resolved: https://radicalface.bandcamp.com/album/hidden-hollow-vol-one-singles +Resolved: https://helenadeland.bandcamp.com/album/someone-new +Resolved: https://miloandlovina.bandcamp.com/album/paper-hearts Resolved: https://mrsgreenbird.bandcamp.com/album/10-years-live +Resolved: https://saintseneca.bandcamp.com/album/all-youve-got-is-everyone +Resolved: https://virginiaastley.bandcamp.com/album/from-gardens-where-we-feel-secure +Resolved: https://joannanewsom.bandcamp.com/album/ys +Resolved: https://joannanewsom.bandcamp.com/album/divers +Resolved: https://anothermichael.bandcamp.com/album/new-music-and-big-pop +Resolved: https://laurastevenson.bandcamp.com/album/sit-resist-remastered-deluxe-edition +Resolved: https://imskullcrusher.bandcamp.com/album/skullcrusher +Resolved: https://phoebebridgers.bandcamp.com/album/stranger-in-the-alps +Resolved: https://ryleywalker.bandcamp.com/album/for-michael-ripps +Resolved: https://alexsalcido.bandcamp.com/album/im-a-bird +Resolved: https://kellymcfarling.bandcamp.com/album/deep-the-habit +Total 46 URLs resolved! +Execution time without limiter: 6.265s diff --git a/examples/sanitizeDiscoverParams.js b/examples/sanitizeDiscoverParams.js deleted file mode 100644 index b83aded..0000000 --- a/examples/sanitizeDiscoverParams.js +++ /dev/null @@ -1,9 +0,0 @@ -const bcfetch = require('../'); - -const params = { - genre: 'ambient' -} - -bcfetch.sanitizeDiscoverParams(params).then( results => { - console.log(results); -}); \ No newline at end of file diff --git a/examples/search.js b/examples/search.js deleted file mode 100644 index ab11396..0000000 --- a/examples/search.js +++ /dev/null @@ -1,28 +0,0 @@ -const bcfetch = require('../'); -const util = require('util'); - -const options = { - albumImageFormat: 'art_app_large', - artistImageFormat: 'bio_featured', -} - -function doSearch(itemType) { - const params = { - query: 'Coeur de pirate', - page: 1, - itemType - } - - bcfetch.search(params, options).then( results => { - let title = `Search query: ${ params.query }. Item type: ${ itemType || 'all' }. ${ results.items.length } results.`; - console.log(title); - console.log('-'.repeat(title.length)); - console.log(util.inspect(results, false, null, false)); - console.log(''); - }); -} - -doSearch(); -doSearch('artistsAndLabels'); -doSearch('albums'); -doSearch('tracks'); diff --git a/examples/search/search.ts b/examples/search/search.ts new file mode 100644 index 0000000..e47568e --- /dev/null +++ b/examples/search/search.ts @@ -0,0 +1,39 @@ +import bcfetch from '../../'; +import util from 'util'; + +function printResults(results: any, query: string, itemType: string ) { + const title = `Search query: ${query}. Item type: ${itemType}. ${results.items.length} results.`; + console.log(title); + console.log('-'.repeat(title.length)); + console.log(util.inspect(results, false, null, false)); + console.log(''); +} + +const baseParams = { + page: 1, + albumImageFormat: 'art_app_large', + artistImageFormat: 'bio_featured' +}; + +const searchAllParams = { ...baseParams, query: 'jazz' }; +const searchArtistsAndLabelsParams = { ...baseParams, query: 'merge' }; +const searchAlbumsParams = { ...baseParams, query: 'Coeur de pirate' }; +const searchTracksParams = { ...baseParams, query: 'Coeur de pirate' }; +const searchFansParams = { ...baseParams, query: 'merge' }; + +const searches = [ + bcfetch.search.all(searchAllParams), + bcfetch.search.artistsAndLabels(searchArtistsAndLabelsParams), + bcfetch.search.albums(searchAlbumsParams), + bcfetch.search.tracks(searchTracksParams), + bcfetch.search.fans(searchFansParams) +]; + +Promise.all(searches).then((results) => { + const [ all, artistsAndLabels, albums, tracks, fans ] = results; + printResults(all, searchAllParams.query, 'All'); + printResults(artistsAndLabels, searchArtistsAndLabelsParams.query, 'Artists & labels'); + printResults(albums, searchAlbumsParams.query, 'Albums'); + printResults(tracks, searchTracksParams.query, 'Tracks'); + printResults(fans, searchFansParams.query, 'Fans'); +}); diff --git a/examples/search/search_output.txt b/examples/search/search_output.txt new file mode 100644 index 0000000..a893b39 --- /dev/null +++ b/examples/search/search_output.txt @@ -0,0 +1,769 @@ +Search query: jazz. Item type: All. 18 results. +----------------------------------------------- +{ + items: [ + { + type: 'label', + name: 'Bastard Jazz Recordings', + url: 'https://bastardjazz.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0001263098_28.jpg', + location: 'Brooklyn, New York' + }, + { + type: 'label', + name: 'Jazz Is Dead', + url: 'https://jazzisdead.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0019613576_28.jpg', + location: 'Los Angeles, California' + }, + { + type: 'label', + name: 'We Jazz Records', + url: 'https://wejazzrecords.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0020709568_28.jpg', + location: 'Helsinki, Finland' + }, + { + type: 'label', + name: 'Concord Jazz', + url: 'https://concordjazz.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0020800855_28.jpg', + location: 'Los Angeles, California' + }, + { + type: 'artist', + name: 'Jazz Spastiks', + url: 'https://jazzspastiks.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0023354039_28.jpg', + location: 'UK', + genre: 'Hip-Hop/Rap', + tags: 'instrumental, underground, Hip-Hop/Rap, hip hop, alternative, rap' + }, + { + type: 'label', + name: 'Acid Jazz Records', + url: 'https://acidjazz.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0005692295_28.jpg', + location: 'UK' + }, + { + type: 'artist', + name: 'Jazz In Britain', + url: 'https://jazzinbritain.co.uk', + imageUrl: 'https://f4.bcbits.com/img/0031210649_28.jpg', + location: 'England, UK', + genre: 'Jazz', + tags: 'Jazz, Jazz Fusion, Jazz Rock, British Jazz, Modern Jazz' + }, + { + type: 'label', + name: 'Millennium Jazz Music', + url: 'https://millenniumjazz.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0003984404_28.jpg', + location: 'London, UK' + }, + { + type: 'label', + name: 'Jazz Room Records', + url: 'https://jazzroomrecords.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0017307260_28.jpg', + location: 'London, UK' + }, + { + type: 'artist', + name: 'The Jazz Hop Café', + url: 'https://jazzhopcafe.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0032164967_28.jpg', + location: 'England, UK', + genre: 'Hip-Hop/Rap', + tags: 'the jazz hop cafe, chill beats, jazz hop, chillhop, Hip-Hop/Rap, lofi' + }, + { + type: 'artist', + name: 'Jazz Sabbath', + url: 'https://jazzsabbath.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0018637470_28.jpg', + location: 'UK', + genre: 'Jazz', + tags: 'jazzy, jazz rock, jazz metal, black sabbath, Jazz' + }, + { + type: 'artist', + name: 'Modern Urban Jazz', + url: 'https://modernurbanjazz.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0025435106_28.jpg', + location: 'England, UK', + genre: 'Electronic', + tags: 'Breaks, Electronic, Bass Music, Drum & Bass' + }, + { + type: 'artist', + name: 'Spiritual Jazz', + url: 'https://spiritualjazz.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0011829726_28.jpg', + location: 'London, UK', + genre: 'Jazz', + tags: 'Modal Jazz, Jazz, Spiritual Jazz' + }, + { + type: 'label', + name: 'Super-Sonic Jazz Records', + url: 'https://super-sonic-jazz-records.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0018388156_28.jpg', + location: 'Amsterdam, Netherlands' + }, + { + type: 'artist', + name: 'Adult Jazz', + url: 'https://adultjazz.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0007847789_28.jpg', + location: 'UK', + genre: 'Alternative', + tags: 'Alternative' + }, + { + type: 'artist', + name: 'British Progressive Jazz', + url: 'https://britishprogressivejazz.com', + imageUrl: 'https://f4.bcbits.com/img/0029537480_28.jpg', + location: 'London, UK', + genre: 'Jazz', + tags: 'Prog Rock, free jazz, hard bop, Jazz, Jazz Fusion, Jazz Rock Fusion' + }, + { + type: 'artist', + name: 'Jazz to Jazz', + url: 'https://jazztojazz.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0002152925_28.jpg', + location: 'Barcelona, Spain', + genre: 'Jazz', + tags: 'Jazz, Barcelona' + }, + { + type: 'label', + name: 'JAZZ & MILK', + url: 'https://jazzandmilk.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0022362718_28.jpg', + location: 'Cologne, Germany' + } + ], + totalPages: 6 +} + +Search query: merge. Item type: Artists & labels. 18 results. +------------------------------------------------------------- +{ + items: [ + { + type: 'label', + name: 'Merge Records', + url: 'https://mergerecords.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0018287817_28.jpg', + location: 'Durham, North Carolina' + }, + { + type: 'artist', + name: 'Merge', + url: 'https://mergeband.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0029512726_28.jpg', + location: 'Germany', + genre: 'Alternative', + tags: 'electronic, germany, synthpop, gothic, Alternative' + }, + { + type: 'artist', + name: 'Merge', + url: 'https://merge4.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/a2626147609_28.jpg', + location: 'Chicago, Illinois', + genre: 'Jazz', + tags: 'Jazz, bass, saxophone, modern jazz, quintet' + }, + { + type: 'artist', + name: 'Merge', + url: 'https://thisismerge.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0011165847_28.jpg', + location: 'England, UK', + genre: 'Rock', + tags: 'Rock, pop, pop rock, alternative rock' + }, + { + type: 'artist', + name: 'Merge', + url: 'https://merge.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/a2204167753_28.jpg', + location: 'Vancouver, British Columbia', + genre: 'Rock', + tags: 'Rock' + }, + { + type: 'artist', + name: 'Merge Quintet', + url: 'https://mergequintet.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0003813575_28.jpg', + location: 'Chicago, Illinois', + genre: 'Jazz', + tags: 'quintet, Jazz, modern' + }, + { + type: 'artist', + name: 'MERGE', + url: 'https://mergespartanjet-plexberkolover.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0027360854_28.jpg', + location: 'Richmond, Virginia', + genre: 'Alternative', + tags: 'bedroom pop, alternative, trip hop, dance, pop, Alternative' + }, + { + type: 'artist', + name: 'Merge Conflict', + url: 'https://mergeconflict.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0014637328_28.jpg', + location: 'Warsaw, Poland', + genre: 'Rock', + tags: 'Hard Rock, Rock, vintage rock, doom, polish, metal' + }, + { + type: 'artist', + name: 'Merge2One', + url: 'https://merge2one.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0018259940_28.jpg', + location: 'Boston, Massachusetts', + genre: 'Funk', + tags: 'Electronic, jazz, funk rock, Funk, guitar solo' + }, + { + type: 'artist', + name: 'Merge Beats', + url: 'https://mergebeats.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0006078072_28.jpg', + location: 'Bakersfield, California', + genre: 'Hip-Hop/Rap', + tags: 'instrumentals, underground, Hip-Hop/Rap, Hip Hop' + }, + { + type: 'artist', + name: 'Merge', + url: 'https://mergednb.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0002032674_28.jpg', + location: 'Leeds, UK', + genre: 'Electronic', + tags: 'Electronic, Drum & Bass, Jungle' + }, + { + type: 'artist', + name: 'Perge', + url: 'https://perge.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0006086709_28.jpg', + location: 'UK', + genre: 'Electronic', + tags: 'Electronic, synthwave, ambient electronic, progressive, Berlin School, synth' + }, + { + type: 'artist', + name: 'Merge', + url: 'https://merge2.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0000200110_28.jpg', + location: 'Chicago, Illinois', + genre: 'Alternative', + tags: 'Alternative' + }, + { + type: 'artist', + name: 'MERGE', + url: 'https://mergeonmerge.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0013677342_28.jpg', + location: 'Los Angeles, California', + genre: 'Hip-Hop/Rap', + tags: 'Rap, Hip Hop, jazz, instrumentals, Hip-Hop/Rap, Hip-Hop' + }, + { + type: 'artist', + name: 'Tiberius Merge', + url: 'https://tiberiusmerge.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0018900882_28.jpg', + location: 'Victoria, British Columbia', + genre: 'Metal', + tags: 'Metal, death, progressive metal, thrash metal, deathcore, melodic' + }, + { + type: 'artist', + name: 'attenuation circuit', + url: 'https://emerge.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0003392229_28.jpg', + location: 'Augsburg, Germany', + genre: 'Experimental', + tags: 'drone, ambient, Experimental, noise' + }, + { + type: 'artist', + name: 'URGE TO MERGE', + url: 'https://urgetomerge.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0012586132_28.jpg', + genre: 'Rock', + tags: 'Rock, KISS' + }, + { + type: 'artist', + name: 'theMerge', + url: 'https://themergeofficial.bandcamp.com', + imageUrl: 'https://f4.bcbits.com/img/0023463011_28.jpg', + location: 'Calgary, Alberta', + genre: 'Pop', + tags: 'Acoustic, Pop, World, Rock, folk pop, Country' + } + ], + totalPages: 6 +} + +Search query: Coeur de pirate. Item type: Albums. 14 results. +------------------------------------------------------------- +{ + items: [ + { + type: 'album', + name: 'Coeur de pirate', + url: 'https://musique.coeurdepirate.com/album/coeur-de-pirate', + imageUrl: 'https://f4.bcbits.com/img/a2201751482_16.jpg', + artist: 'Cœur de pirate', + numTracks: 12, + duration: 1860, + releaseDate: 'September 16, 2008', + tags: 'french, french pop, Montréal, Canada, Pop, Québec, piano pop' + }, + { + type: 'album', + name: 'Blonde', + url: 'https://musique.coeurdepirate.com/album/blonde', + imageUrl: 'https://f4.bcbits.com/img/a1328452291_16.jpg', + artist: 'Cœur de pirate', + numTracks: 12, + duration: 2160, + releaseDate: 'November 7, 2011', + tags: 'grosse boîte, Pop, coeur de pirate, montreal, amour, piano pop, Canada, Montréal, french, Québec, french pop' + }, + { + type: 'album', + name: 'Trauma', + url: 'https://musique.coeurdepirate.com/album/trauma', + imageUrl: 'https://f4.bcbits.com/img/a3799110102_16.jpg', + artist: 'Cœur de pirate', + numTracks: 12, + duration: 2760, + releaseDate: 'January 14, 2014', + tags: 'Pop, amy winehouse, Montréal, piano pop, Canada, Québec, Dare To Care Records, french, french pop, cover songs, trauma' + }, + { + type: 'album', + name: 'Child of Light', + url: 'https://musique.coeurdepirate.com/album/child-of-light', + imageUrl: 'https://f4.bcbits.com/img/a3984758353_16.jpg', + artist: 'Cœur de pirate', + numTracks: 18, + duration: 3000, + releaseDate: 'April 29, 2014', + tags: 'Pop, piano pop, Canada, french, video game music, Québec, french pop, instrumental, Montréal' + }, + { + type: 'album', + name: 'Roses', + url: 'https://musique.coeurdepirate.com/album/roses', + imageUrl: 'https://f4.bcbits.com/img/a3347055233_16.jpg', + artist: 'Cœur de pirate', + numTracks: 11, + duration: 2400, + releaseDate: 'August 28, 2015', + tags: 'piano pop, french pop, french, Montréal, Québec, Pop, Canada' + }, + { + type: 'album', + name: 'Montréal Symphonique', + url: 'https://montrealsymphonique.bandcamp.com/album/montr-al-symphonique', + imageUrl: 'https://f4.bcbits.com/img/a3972424743_16.jpg', + artist: 'Cœur de pirate, Rufus Wainwright, Daniel Bélanger, Wyclef Jean, Diane Dufresne, Patrick Watson', + numTracks: 31, + duration: 6600, + releaseDate: 'November 3, 2017', + tags: 'musique classique, Diane Dufresne, Québec, Classical, Canada, orchestre, Montreal, Daniel Bélanger, Montréal, Symphonique, cassical' + }, + { + type: 'album', + name: 'Perséides', + url: 'https://musique.coeurdepirate.com/album/pers-ides', + imageUrl: 'https://f4.bcbits.com/img/a3751956000_16.jpg', + artist: 'Cœur de pirate', + numTracks: 10, + duration: 1500, + releaseDate: 'April 30, 2021', + tags: 'french pop, french, Pop, Montréal, piano pop, Québec, Canada' + }, + { + type: 'album', + name: 'Impossible à aimer', + url: 'https://musique.coeurdepirate.com/album/impossible-aimer', + imageUrl: 'https://f4.bcbits.com/img/a3147632514_16.jpg', + artist: 'Cœur de pirate', + numTracks: 10, + duration: 2040, + releaseDate: 'October 15, 2021', + tags: 'Pop, french, Montréal, Québec, Canada, piano pop, french pop' + }, + { + type: 'album', + name: 'Prémonition', + url: 'https://musique.coeurdepirate.com/album/pr-monition-2', + imageUrl: 'https://f4.bcbits.com/img/a2454271957_16.jpg', + artist: 'Cœur de pirate', + duration: 180, + releaseDate: 'January 19, 2018', + tags: 'Québec, Canada, Pop, Montréal, french pop, french, piano pop' + }, + { + type: 'album', + name: 'Chansons tristes pour Noël', + url: 'https://musique.coeurdepirate.com/album/chansons-tristes-pour-no-l', + imageUrl: 'https://f4.bcbits.com/img/a2476670277_16.jpg', + artist: 'Cœur de pirate', + numTracks: 3, + duration: 600, + releaseDate: 'December 16, 2016', + tags: 'Montréal, french pop, Canada, Québec, piano pop, Christmas, Pop, coeur de pirate, french' + }, + { + type: 'album', + name: 'Somnambule', + url: 'https://musique.coeurdepirate.com/album/somnambule', + imageUrl: 'https://f4.bcbits.com/img/a1903816474_16.jpg', + artist: 'Cœur de pirate', + duration: 180, + releaseDate: 'April 13, 2018', + tags: 'french pop, french, piano pop, Québec, Canada, Pop, Montréal' + }, + { + type: 'album', + name: 'Clou', + url: 'https://musique.coeurdepirate.com/album/clou', + imageUrl: 'https://f4.bcbits.com/img/a0422319817_16.jpg', + artist: 'Cœur de pirate', + numTracks: 2, + duration: 600, + releaseDate: 'August 25, 2022', + tags: 'Canada, Pop, french, piano pop, Montréal, Québec, french pop' + }, + { + type: 'album', + name: 'en cas de tempête, ce jardin sera fermé.', + url: 'https://musique.coeurdepirate.com/album/en-cas-de-temp-te-ce-jardin-sera-ferm', + imageUrl: 'https://f4.bcbits.com/img/a0248413645_16.jpg', + artist: 'Cœur de pirate', + numTracks: 10, + duration: 1920, + releaseDate: 'June 1, 2018', + tags: 'french pop, french, Pop, Montréal, piano pop, Québec, Canada' + }, + { + type: 'album', + name: 'Comme des enfants (Version originale et remix par Le Matos)', + url: 'https://musique.coeurdepirate.com/album/comme-des-enfants-version-originale-et-remix-par-le-matos', + imageUrl: 'https://f4.bcbits.com/img/a1250129776_16.jpg', + artist: 'Cœur de pirate', + numTracks: 2, + duration: 480, + releaseDate: 'September 7, 2010', + tags: 'piano pop, Québec, Canada, french pop, french, Montréal, Pop' + } + ], + totalPages: 1 +} + +Search query: Coeur de pirate. Item type: Tracks. 18 results. +------------------------------------------------------------- +{ + items: [ + { + type: 'track', + name: 'Comme des enfants', + url: 'https://musique.coeurdepirate.com/track/comme-des-enfants-2', + imageUrl: 'https://f4.bcbits.com/img/a2201751482_16.jpg', + artist: 'Cœur de pirate', + album: 'Coeur de pirate', + releaseDate: 'September 16, 2008' + }, + { + type: 'track', + name: 'Ensemble', + url: 'https://musique.coeurdepirate.com/track/ensemble', + imageUrl: 'https://f4.bcbits.com/img/a2201751482_16.jpg', + artist: 'Cœur de pirate', + album: 'Coeur de pirate', + releaseDate: 'September 16, 2008' + }, + { + type: 'track', + name: 'Francis', + url: 'https://musique.coeurdepirate.com/track/francis', + imageUrl: 'https://f4.bcbits.com/img/a2201751482_16.jpg', + artist: 'Cœur de pirate', + album: 'Coeur de pirate', + releaseDate: 'September 16, 2008' + }, + { + type: 'track', + name: 'Intermission', + url: 'https://musique.coeurdepirate.com/track/intermission', + imageUrl: 'https://f4.bcbits.com/img/a2201751482_16.jpg', + artist: 'Cœur de pirate', + album: 'Coeur de pirate', + releaseDate: 'September 16, 2008' + }, + { + type: 'track', + name: 'Corbeau', + url: 'https://musique.coeurdepirate.com/track/corbeau', + imageUrl: 'https://f4.bcbits.com/img/a2201751482_16.jpg', + artist: 'Cœur de pirate', + album: 'Coeur de pirate', + releaseDate: 'September 16, 2008' + }, + { + type: 'track', + name: "T'es belle - Coeur de pirate", + url: 'https://pianovoix.bandcamp.com/track/tes-belle-coeur-de-pirate', + imageUrl: 'https://f4.bcbits.com/img/a3645401914_16.jpg', + artist: 'pianovoix', + releaseDate: 'October 15, 2020', + tags: 'chanson française, piano, IDF, France, Pop, Paris' + }, + { + type: 'track', + name: "C'était Salement Romantique (Coeur de Pirate)", + url: 'https://alicianight.bandcamp.com/track/c-tait-salement-romantique-coeur-de-pirate', + imageUrl: 'https://f4.bcbits.com/img/a1215277442_16.jpg', + artist: 'Alicia Night', + releaseDate: 'October 2, 2018', + tags: 'Rock, Cholet, France, Pays de la Loire' + }, + { + type: 'track', + name: 'Francis (Coeur de Pirate cover)', + url: 'https://jeneiffel.bandcamp.com/track/francis-coeur-de-pirate-cover', + imageUrl: 'https://f4.bcbits.com/img/a0532659386_16.jpg', + artist: 'Jen Eiffel', + album: 'Covers', + releaseDate: 'March 26, 2014' + }, + { + type: 'track', + name: 'Jeter un sort (feat. Coeur de pirate)', + url: 'https://alexnevsky.bandcamp.com/track/jeter-un-sort-feat-coeur-de-pirate', + imageUrl: 'https://f4.bcbits.com/img/a3550838341_16.jpg', + artist: 'Alex Nevsky', + album: 'Nos Eldorados', + releaseDate: 'November 11, 2016' + }, + { + type: 'track', + name: 'Adieu (Lincoln Remix)', + url: 'https://lincolnplease.bandcamp.com/track/adieu-lincoln-remix', + imageUrl: 'https://f4.bcbits.com/img/a3583685498_16.jpg', + artist: 'Cœur de pirate', + releaseDate: 'December 2, 2014', + tags: 'Funk, piano, United States, bass, dark, House, California, Hip Hop, San Diego, Electronic, groove, Dance, future' + }, + { + type: 'track', + name: 'Coeur de pirate', + url: 'https://sergemonette.bandcamp.com/track/coeur-de-pirate', + imageUrl: 'https://f4.bcbits.com/img/a3469921391_16.jpg', + artist: 'Monette', + album: "Sonnez l'éveil", + releaseDate: 'March 28, 2020', + tags: 'folk rock, ONfr, AFO, ontario, franco, francophone' + }, + { + type: 'track', + name: 'Coeur de pirate - Place de la republique (Rauschhaus Kuschelhouse edit) (djnodj edit)', + url: 'https://djnodj.bandcamp.com/track/coeur-de-pirate-place-de-la-republique-rauschhaus-kuschelhouse-edit-djnodj-edit', + imageUrl: 'https://f4.bcbits.com/img/a0795995370_16.jpg', + artist: 'dj no dj', + releaseDate: 'March 1, 2013', + tags: 'remix, nibblicious, Seattle, United States, Pop, Washington, chill, house, experimental dub pop, tech, djnodj, 122, minimal' + }, + { + type: 'track', + name: 'Coeur de Pirate- Place de la République (Stingray remix)', + url: 'https://drstingray.bandcamp.com/track/coeur-de-pirate-place-de-la-r-publique-stingray-remix', + imageUrl: 'https://f4.bcbits.com/img/a0866391113_16.jpg', + artist: 'Dr Stingray', + releaseDate: 'May 2, 2014', + tags: 'Electronic, French House, Remix, Canada, Toronto, Electronica, Synthpop, Ontario, Trap, Trip-Hop' + }, + { + type: 'track', + name: 'Crier tout bas', + url: 'https://montrealsymphonique.bandcamp.com/track/crier-tout-bas', + imageUrl: 'https://f4.bcbits.com/img/a3972424743_16.jpg', + artist: 'Cœur de pirate, Orchestre symphonique de Montréal', + album: 'Montréal Symphonique', + releaseDate: 'November 3, 2017' + }, + { + type: 'track', + name: 'Dans tes rêves (ft. Coeur de pirate)', + url: 'https://omnikromofficiel.bandcamp.com/track/dans-tes-r-ves-ft-coeur-de-pirate', + imageUrl: 'https://f4.bcbits.com/img/a1848345881_16.jpg', + artist: 'Omnikrom', + album: 'Comme à la télévision', + releaseDate: 'May 19, 2009' + }, + { + type: 'track', + name: 'Danse et danse', + url: 'https://promodaretocare.bandcamp.com/track/danse-et-danse', + imageUrl: 'https://f4.bcbits.com/img/a2578420227_16.jpg', + artist: 'Coeur de pirate', + album: 'Compilation de Noël 2012', + releaseDate: 'December 1, 2012', + tags: 'Montréal, Grosse Boîte, piano, Pop, french' + }, + { + type: 'track', + name: 'Peace Sign - Lights + Coeur de Pirate Cover', + url: 'https://thesteadylungs.bandcamp.com/track/peace-sign-lights-coeur-de-pirate-cover', + imageUrl: 'https://f4.bcbits.com/img/a1448091967_16.jpg', + artist: 'The Steady Lungs', + album: 'Demos and Other Nice Stuff', + releaseDate: 'January 23, 2013' + }, + { + type: 'track', + name: 'Coeur De Pirate- Wicked Games "The Weeknd Cover" (OneTwoKno Remix)', + url: 'https://onetwokno.bandcamp.com/track/coeur-de-pirate-wicked-games-the-weeknd-cover-onetwokno-remix', + imageUrl: 'https://f4.bcbits.com/img/a1967760778_16.jpg', + artist: 'Onetwokno', + album: 'Red Light Special', + releaseDate: 'January 2, 2012' + } + ], + totalPages: 8 +} + +Search query: merge. Item type: Fans. 17 results. +------------------------------------------------- +{ + items: [ + { + type: 'fan', + name: 'Merge', + url: 'https://bandcamp.com/Merge', + imageUrl: 'https://f4.bcbits.com/img/0004033550_28.jpg', + genre: 'Alternative' + }, + { + type: 'fan', + name: 'merge_e', + url: 'https://bandcamp.com/merge_e', + genre: 'Metal' + }, + { + type: 'fan', + name: 'merge_musique', + url: 'https://bandcamp.com/merge_musique' + }, + { + type: 'fan', + name: 'merge4989', + url: 'https://bandcamp.com/merge4989', + imageUrl: 'https://f4.bcbits.com/img/0011180189_28.jpg', + genre: 'Alternative' + }, + { + type: 'fan', + name: 'merge225', + url: 'https://bandcamp.com/merge225', + imageUrl: 'https://f4.bcbits.com/img/0032446043_28.jpg', + genre: 'Electronic' + }, + { + type: 'fan', + name: 'merge909', + url: 'https://bandcamp.com/merge909', + genre: 'Punk' + }, + { + type: 'fan', + name: 'merge216', + url: 'https://bandcamp.com/merge216', + genre: 'Comedy' + }, + { + type: 'fan', + name: 'merge_conflicts', + url: 'https://bandcamp.com/merge_conflicts', + genre: 'Electronic' + }, + { + type: 'fan', + name: 'merge810', + url: 'https://bandcamp.com/merge810', + genre: 'Electronic' + }, + { + type: 'fan', + name: 'merge2one', + url: 'https://bandcamp.com/merge2one', + imageUrl: 'https://f4.bcbits.com/img/0024536409_28.jpg', + genre: 'Funk' + }, + { + type: 'fan', + name: 'y-n-merge', + url: 'https://bandcamp.com/y-n-merge', + genre: 'Metal' + }, + { + type: 'fan', + name: 'wintereaglee', + url: 'https://bandcamp.com/heavy_merge', + imageUrl: 'https://f4.bcbits.com/img/0022254557_28.jpg', + genre: 'Electronic' + }, + { + type: 'fan', + name: 'TheMerge', + url: 'https://bandcamp.com/norlaine', + imageUrl: 'https://f4.bcbits.com/img/0023783739_28.jpg', + genre: 'Pop' + }, + { + type: 'fan', + name: 'Outshift', + url: 'https://bandcamp.com/alister_merge', + imageUrl: 'https://f4.bcbits.com/img/0026869092_28.jpg' + }, + { + type: 'fan', + name: 'Matt Melvin', + url: 'https://bandcamp.com/merge5', + imageUrl: 'https://f4.bcbits.com/img/0006048796_28.jpg', + genre: 'Rock' + }, + { + type: 'fan', + name: 'Matt Mergener', + url: 'https://bandcamp.com/mergeCinema', + imageUrl: 'https://f4.bcbits.com/img/0004443173_28.jpg', + genre: 'Electronic' + }, + { + type: 'fan', + name: 'MaleMerge', + url: 'https://bandcamp.com/bitter-nerd', + imageUrl: 'https://f4.bcbits.com/img/0021440800_28.jpg', + genre: 'Electronic' + } + ], + totalPages: 6 +} + diff --git a/examples/searchLocation.js b/examples/searchLocation.js deleted file mode 100644 index 9f452e7..0000000 --- a/examples/searchLocation.js +++ /dev/null @@ -1,11 +0,0 @@ -const bcfetch = require('../'); -const util = require('util'); - -const params = { - q: 'Fin', - limit: 10 -} - -bcfetch.searchLocation(params).then( results => { - console.log(util.inspect(results, false, null, false)); -}); diff --git a/examples/searchLocation_output.txt b/examples/searchLocation_output.txt deleted file mode 100644 index 59307cb..0000000 --- a/examples/searchLocation_output.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ { value: '2186224', - name: 'New Zealand', - fullName: 'New Zealand' }, - { value: '660013', name: 'Finland', fullName: 'Finland' }, - { value: '5308655', - name: 'Phoenix', - fullName: 'Phoenix, Arizona' }, - { value: '830685', - name: 'Keski-Suomi', - fullName: 'Keski-Suomi, Republic of Finland' }, - { value: '2139685', - name: 'New Caledonia', - fullName: 'New Caledonia' }, - { value: '5153924', name: 'Findlay', fullName: 'Findlay, Ohio' }, - { value: '830708', - name: 'Finland Proper', - fullName: 'Finland Proper, Republic of Finland' }, - { value: '780166', - name: 'Finnmark Fylke', - fullName: 'Finnmark Fylke, Kingdom of Norway' }, - { value: '2714903', - name: 'Finspång', - fullName: 'Finspång, Östergötland' }, - { value: '3176983', - name: 'Finale Emilia', - fullName: 'Finale Emilia, Emilia-Romagna' } ] diff --git a/examples/searchTag.js b/examples/searchTag.js deleted file mode 100644 index 9f3d099..0000000 --- a/examples/searchTag.js +++ /dev/null @@ -1,11 +0,0 @@ -const bcfetch = require('../'); -const util = require('util'); - -const params = { - q: 'ambient', - limit: 10 -} - -bcfetch.searchTag(params).then( results => { - console.log(util.inspect(results, false, null, false)); -}); diff --git a/examples/searchTag_output.txt b/examples/searchTag_output.txt deleted file mode 100644 index 4ca2721..0000000 --- a/examples/searchTag_output.txt +++ /dev/null @@ -1,8 +0,0 @@ -[ { count: 682181, value: 'ambient', name: 'ambient' }, - { count: 144561, value: 'dark-ambient', name: 'dark ambient' }, - { count: 69057, - value: 'ambient-electronic', - name: 'ambient electronic' }, - { count: 23253, value: 'drone-ambient', name: 'drone ambient' }, - { count: 13690, value: 'ambient-rock', name: 'ambient rock' }, - { count: 7413, value: 'noise-ambient', name: 'noise ambient' } ] diff --git a/examples/search_output.txt b/examples/search_output.txt deleted file mode 100644 index d7c969e..0000000 --- a/examples/search_output.txt +++ /dev/null @@ -1,315 +0,0 @@ -Search query: Coeur de pirate. Item type: albums. 5 results. ------------------------------------------------------------- -{ items: - [ { type: 'album', - name: 'Blonde', - url: 'https://musique.coeurdepirate.com/album/blonde', - imageUrl: 'https://f4.bcbits.com/img/a1328452291_16.jpg', - artist: 'Cœur de pirate', - numTracks: 12, - duration: 2160, - releasedDate: '07 November 2011', - tags: 'amour, french pop, Canada, montreal, piano pop, french, Québec, Pop, coeur de pirate, Montréal, grosse boîte' }, - { type: 'album', - name: 'Child of Light', - url: 'https://musique.coeurdepirate.com/album/child-of-light', - imageUrl: 'https://f4.bcbits.com/img/a3984758353_16.jpg', - artist: 'Cœur de pirate', - numTracks: 18, - duration: 3000, - releasedDate: '29 April 2014', - tags: 'Québec, french pop, Pop, Canada, french, instrumental, video game music, piano pop, Montréal' }, - { type: 'album', - name: 'Coeur de pirate', - url: 'https://musique.coeurdepirate.com/album/coeur-de-pirate', - imageUrl: 'https://f4.bcbits.com/img/a2201751482_16.jpg', - artist: 'Cœur de pirate', - numTracks: 12, - duration: 1860, - releasedDate: '16 September 2008', - tags: 'Montréal, french, piano pop, french pop, Québec, Canada, Pop' }, - { type: 'album', - name: 'en cas de tempête, ce jardin sera fermé.', - url: 'https://musique.coeurdepirate.com/album/en-cas-de-temp-te-ce-jardin-sera-ferm', - imageUrl: 'https://f4.bcbits.com/img/a0248413645_16.jpg', - artist: 'Cœur de pirate', - numTracks: 10, - duration: 1920, - releasedDate: '01 June 2018', - tags: 'Canada, french pop, french, Québec, Pop, Montréal, piano pop' }, - { type: 'album', - name: 'Trauma', - url: 'https://musique.coeurdepirate.com/album/trauma', - imageUrl: 'https://f4.bcbits.com/img/a3799110102_16.jpg', - artist: 'Cœur de pirate', - numTracks: 12, - duration: 2760, - releasedDate: '14 January 2014', - tags: 'piano pop, Montréal, Québec, french pop, cover songs, amy winehouse, Canada, french, trauma, Dare To Care Records, Pop' } ], - totalPages: 1 } - -Search query: Coeur de pirate. Item type: all. 15 results. ----------------------------------------------------------- -{ items: - [ { type: 'artist', - name: 'Cœur de pirate', - url: 'https://musique.coeurdepirate.com', - imageUrl: 'https://f4.bcbits.com/img/0026415167_28.jpg', - location: 'Montréal, Québec', - genre: 'Pop', - tags: 'french, Pop, piano pop, french pop' }, - { type: 'album', - name: 'Blonde', - url: 'https://musique.coeurdepirate.com/album/blonde', - imageUrl: 'https://f4.bcbits.com/img/a1328452291_16.jpg', - artist: 'Cœur de pirate', - numTracks: 12, - duration: 2160, - releasedDate: '07 November 2011', - tags: 'Montréal, Québec, Canada, piano pop, grosse boîte, Pop, french, coeur de pirate, french pop, montreal, amour' }, - { type: 'album', - name: 'Child of Light', - url: 'https://musique.coeurdepirate.com/album/child-of-light', - imageUrl: 'https://f4.bcbits.com/img/a3984758353_16.jpg', - artist: 'Cœur de pirate', - numTracks: 18, - duration: 3000, - releasedDate: '29 April 2014', - tags: 'piano pop, Canada, video game music, Pop, Québec, instrumental, Montréal, french, french pop' }, - { type: 'album', - name: 'Coeur de pirate', - url: 'https://musique.coeurdepirate.com/album/coeur-de-pirate', - imageUrl: 'https://f4.bcbits.com/img/a2201751482_16.jpg', - artist: 'Cœur de pirate', - numTracks: 12, - duration: 1860, - releasedDate: '16 September 2008', - tags: 'Québec, Canada, french, french pop, Pop, piano pop, Montréal' }, - { type: 'album', - name: 'en cas de tempête, ce jardin sera fermé.', - url: 'https://musique.coeurdepirate.com/album/en-cas-de-temp-te-ce-jardin-sera-ferm', - imageUrl: 'https://f4.bcbits.com/img/a0248413645_16.jpg', - artist: 'Cœur de pirate', - numTracks: 10, - duration: 1920, - releasedDate: '01 June 2018', - tags: 'Pop, Montréal, piano pop, french pop, french, Québec, Canada' }, - { type: 'track', - name: 'Place de la république', - url: 'https://arts-crafts.bandcamp.com/track/place-de-la-r-publique', - imageUrl: 'https://f4.bcbits.com/img/a3618408300_16.jpg', - artist: 'Coeur De Pirate', - album: 'Live At Massey Hall Vol. 1', - releasedDate: '22 February 2019' }, - { type: 'track', - name: 'T\'es belle - Coeur de pirate', - url: 'https://pianovoix.bandcamp.com/track/tes-belle-coeur-de-pirate', - imageUrl: 'https://f4.bcbits.com/img/a3645401914_16.jpg', - artist: 'pianovoix', - releasedDate: '15 October 2020', - tags: 'chanson française, piano, IDF, France, Pop, Paris' }, - { type: 'track', - name: 'Francis (Coeur de Pirate cover)', - url: 'https://jeneiffel.bandcamp.com/track/francis-coeur-de-pirate-cover', - imageUrl: 'https://f4.bcbits.com/img/a0532659386_16.jpg', - artist: 'Jen Eiffel', - album: 'Covers', - releasedDate: '26 March 2014' }, - { type: 'track', - name: 'Dans tes rêves (ft. Coeur de pirate)', - url: 'https://omnikromofficiel.bandcamp.com/track/dans-tes-r-ves-ft-coeur-de-pirate', - imageUrl: 'https://f4.bcbits.com/img/a1848345881_16.jpg', - artist: 'Omnikrom', - album: 'Comme à la télévision', - releasedDate: '19 May 2009' }, - { type: 'track', - name: 'Jeter un sort (feat. Coeur de pirate)', - url: 'https://alexnevsky.bandcamp.com/track/jeter-un-sort-feat-coeur-de-pirate', - imageUrl: 'https://f4.bcbits.com/img/a3550838341_16.jpg', - artist: 'Alex Nevsky', - album: 'Nos Eldorados', - releasedDate: '11 November 2016' }, - { type: 'track', - name: 'Coeur De Pirate - Wicked Games Remix', - url: 'https://globalvortex.bandcamp.com/track/coeur-de-pirate-wicked-games-remix', - imageUrl: 'https://f4.bcbits.com/img/a0417515816_16.jpg', - artist: 'OneTwoKno', - album: 'Red Light Special lp', - releasedDate: '02 January 2012' }, - { type: 'track', - name: '06. Coeur de Pirate - Adieu (Horny F Remix)', - url: 'https://hornyf.bandcamp.com/track/06-coeur-de-pirate-adieu-horny-f-remix', - imageUrl: 'https://f4.bcbits.com/img/a2202690740_16.jpg', - artist: 'Horny F', - album: 'Horny F Remixes LP Vol.1', - releasedDate: '10 February 2013' }, - { type: 'track', - name: 'C\'était Salement Romantique (Coeur de Pirate)', - url: 'https://a-crow.bandcamp.com/track/c-tait-salement-romantique-coeur-de-pirate', - imageUrl: 'https://f4.bcbits.com/img/a1215277442_16.jpg', - artist: 'A-Crow', - releasedDate: '02 October 2018', - tags: 'Rock, Kentucky, United States, Paris' }, - { type: 'track', - name: 'Peace Sign - Lights + Coeur de Pirate Cover', - url: 'https://thesteadylungs.bandcamp.com/track/peace-sign-lights-coeur-de-pirate-cover', - imageUrl: 'https://f4.bcbits.com/img/a1448091967_16.jpg', - artist: 'The Steady Lungs', - album: 'Demos and Other Nice Stuff', - releasedDate: '23 January 2013' }, - { type: 'track', - name: 'Crier tout bas', - url: 'https://montrealsymphonique.bandcamp.com/track/crier-tout-bas', - imageUrl: 'https://f4.bcbits.com/img/a3972424743_16.jpg', - artist: 'Cœur de pirate, Orchestre symphonique de Montréal', - album: 'Montréal Symphonique', - releasedDate: '03 November 2017' } ], - totalPages: 1 } - -Search query: Coeur de pirate. Item type: artistsAndLabels. 1 results. ----------------------------------------------------------------------- -{ items: - [ { type: 'artist', - name: 'Cœur de pirate', - url: 'https://musique.coeurdepirate.com', - imageUrl: 'https://f4.bcbits.com/img/0026415167_28.jpg', - location: 'Montréal, Québec', - genre: 'Pop', - tags: 'french, Pop, piano pop, french pop' } ], - totalPages: 1 } - -Search query: Coeur de pirate. Item type: tracks. 18 results. -------------------------------------------------------------- -{ items: - [ { type: 'track', - name: 'Ne m\'appelle pas', - url: 'https://musique.coeurdepirate.com/track/ne-mappelle-pas', - imageUrl: 'https://f4.bcbits.com/img/a2603960871_16.jpg', - artist: 'Cœur de pirate', - releasedDate: '09 May 2019', - tags: 'french, Montréal, Canada, piano pop, french pop, Pop, Québec' }, - { type: 'track', - name: 'T\'es belle', - url: 'https://musique.coeurdepirate.com/track/tes-belle', - imageUrl: 'https://f4.bcbits.com/img/a0774650359_16.jpg', - artist: 'Cœur de pirate', - releasedDate: '01 October 2020', - tags: 'french, Montréal, Canada, piano pop, french pop, Pop, Québec' }, - { type: 'track', - name: 'Plan à trois', - url: 'https://musique.coeurdepirate.com/track/plan-trois', - imageUrl: 'https://f4.bcbits.com/img/a4236666126_16.jpg', - artist: 'Cœur de pirate', - releasedDate: '28 May 2021', - tags: 'piano pop, french pop, Pop, Québec, french, Montréal, Canada' }, - { type: 'track', - name: 'Adieu', - url: 'https://musique.coeurdepirate.com/track/adieu', - imageUrl: 'https://f4.bcbits.com/img/a2927576646_16.jpg', - artist: 'Cœur de pirate', - album: 'Blonde', - releasedDate: '19 September 2011', - tags: 'grosse boîte, amour, coeur de pirate, dare to care, montreal, franco' }, - { type: 'track', - name: 'Comme des enfants (Le Matos remix)', - url: 'https://musique.coeurdepirate.com/track/comme-des-enfants-le-matos-remix', - imageUrl: 'https://f4.bcbits.com/img/a1250129776_16.jpg', - artist: 'Cœur de pirate', - album: 'Comme des enfants (Version originale et remix par Le Matos)', - releasedDate: '07 September 2010' }, - { type: 'track', - name: 'Place de la république', - url: 'https://arts-crafts.bandcamp.com/track/place-de-la-r-publique', - imageUrl: 'https://f4.bcbits.com/img/a3618408300_16.jpg', - artist: 'Coeur De Pirate', - album: 'Live At Massey Hall Vol. 1', - releasedDate: '22 February 2019' }, - { type: 'track', - name: 'Comme des enfants', - url: 'https://mpourmontreal.bandcamp.com/track/comme-des-enfants', - imageUrl: 'https://f4.bcbits.com/img/a2364532545_16.jpg', - artist: 'Coeur de Pirate', - album: 'Franco M SiriusXM - Compilation 2006-2015', - releasedDate: '06 November 2015' }, - { type: 'track', - name: 'Heartbeats Accelerating', - url: 'https://moonmage.bandcamp.com/track/heartbeats-accelerating', - imageUrl: 'https://f4.bcbits.com/img/a1799905378_16.jpg', - artist: 'Cœur de pirate', - album: 'Bloodmoon', - releasedDate: '14 January 2014', - tags: 'Indie' }, - { type: 'track', - name: 'A la gare viger (cover)', - url: 'https://lilyfay.bandcamp.com/track/a-la-gare-viger-cover', - imageUrl: 'https://f4.bcbits.com/img/a1639461967_16.jpg', - artist: 'Coeur de pirate', - album: 'Live in Livingrooms (home) tour', - releasedDate: '17 January 2013' }, - { type: 'track', - name: 'Adieu (Lincoln Remix)', - url: 'https://lincolnplease.bandcamp.com/track/adieu-lincoln-remix', - imageUrl: 'https://f4.bcbits.com/img/a3583685498_16.jpg', - artist: 'Cœur de pirate', - releasedDate: '02 December 2014', - tags: 'Electronic, groove, Dance, future, Funk, piano, United States, bass, dark, House, California, Hip Hop, San Diego' }, - { type: 'track', - name: 'Comme Des Enfants Cle matis Andy CarMicheal remix', - url: 'https://djslim1.bandcamp.com/track/comme-des-enfants-cle-matis-andy-carmicheal-remix', - imageUrl: 'https://f4.bcbits.com/img/a4087143740_16.jpg', - artist: 'Coeur De Pirate', - album: 'Miami COntrol Pt.1', - releasedDate: '06 June 2013' }, - { type: 'track', - name: 'Coeur de pirate', - url: 'https://sergemonette.bandcamp.com/track/coeur-de-pirate', - imageUrl: 'https://f4.bcbits.com/img/a3469921391_16.jpg', - artist: 'Monette', - album: 'Sonnez l\'éveil', - releasedDate: '28 March 2020', - tags: 'folk rock, ONfr, AFO, ontario, franco, francophone' }, - { type: 'track', - name: 'Danse et danse', - url: 'https://promodaretocare.bandcamp.com/track/danse-et-danse', - imageUrl: 'https://f4.bcbits.com/img/a2578420227_16.jpg', - artist: 'Coeur de pirate', - album: 'Compilation de Noël 2012', - releasedDate: '01 December 2012', - tags: 'Grosse Boîte, piano, Pop, french, Montréal' }, - { type: 'track', - name: 'T\'es belle - Coeur de pirate', - url: 'https://pianovoix.bandcamp.com/track/tes-belle-coeur-de-pirate', - imageUrl: 'https://f4.bcbits.com/img/a3645401914_16.jpg', - artist: 'pianovoix', - releasedDate: '15 October 2020', - tags: 'IDF, France, Pop, Paris, chanson française, piano' }, - { type: 'track', - name: 'Francis (Coeur de Pirate cover)', - url: 'https://jeneiffel.bandcamp.com/track/francis-coeur-de-pirate-cover', - imageUrl: 'https://f4.bcbits.com/img/a0532659386_16.jpg', - artist: 'Jen Eiffel', - album: 'Covers', - releasedDate: '26 March 2014' }, - { type: 'track', - name: 'Dans tes rêves (ft. Coeur de pirate)', - url: 'https://omnikromofficiel.bandcamp.com/track/dans-tes-r-ves-ft-coeur-de-pirate', - imageUrl: 'https://f4.bcbits.com/img/a1848345881_16.jpg', - artist: 'Omnikrom', - album: 'Comme à la télévision', - releasedDate: '19 May 2009' }, - { type: 'track', - name: 'Jeter un sort (feat. Coeur de pirate)', - url: 'https://alexnevsky.bandcamp.com/track/jeter-un-sort-feat-coeur-de-pirate', - imageUrl: 'https://f4.bcbits.com/img/a3550838341_16.jpg', - artist: 'Alex Nevsky', - album: 'Nos Eldorados', - releasedDate: '11 November 2016' }, - { type: 'track', - name: 'Coeur De Pirate - Wicked Games Remix', - url: 'https://globalvortex.bandcamp.com/track/coeur-de-pirate-wicked-games-remix', - imageUrl: 'https://f4.bcbits.com/img/a0417515816_16.jpg', - artist: 'OneTwoKno', - album: 'Red Light Special lp', - releasedDate: '02 January 2012' } ], - totalPages: 2 } - diff --git a/examples/show/getShow.ts b/examples/show/getShow.ts new file mode 100644 index 0000000..d393928 --- /dev/null +++ b/examples/show/getShow.ts @@ -0,0 +1,12 @@ +import bcfetch from '../../'; +import util from 'util'; + +const showUrl = 'https://bandcamp.com/?show=421'; + +const params = { + showUrl +}; + +bcfetch.show.getShow(params).then((results) => { + console.log(util.inspect(results, false, null, false)); +}); diff --git a/examples/show/getShow_output.txt b/examples/show/getShow_output.txt new file mode 100644 index 0000000..b3e3533 --- /dev/null +++ b/examples/show/getShow_output.txt @@ -0,0 +1,450 @@ +{ + type: 'show', + name: 'The Hip-Hop Show', + url: 'https://bandcamp.com/?show=421', + publishedDate: '29 Jan 2021 00:00:00 GMT', + description: 'Ovrkast joins the show to discuss his latest album, Try Again. Plus sounds from Potatohead People, Isaiah Mclane, and Boom Baptist.', + shortDescription: 'Ovrkast discusses his latest album, Try Again, plus appearances from Boom Baptist and Isaiah Mclane', + imageCaption: 'Hosted by Stoney Creation. Illustration of Ovrkast by McKay Felt', + subtitle: '55 Degree Weather', + duration: 5133.84, + screenImageUrl: 'https://f4.bcbits.com/img/23443998_0', + streamUrl: { + 'mp3-128': 'https://bandcamp.com/stream_redirect?enc=mp3-128&track_id=1825658732&ts=1686408564&t=7ec9dff6158d6308e58503de7992ed6e0fc20cdd' + }, + imageUrl: 'https://f4.bcbits.com/img/23443998_25.jpg', + tracks: [ + { + name: 'time to go', + url: 'https://eighty9s.bandcamp.com/track/time-to-go', + seekPosition: 0, + artist: { + name: 'eighty9s', + url: 'https://eighty9s.bandcamp.com', + location: 'Oakland, California', + imageUrl: 'https://f4.bcbits.com/img/31431195_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a1082190285_9.jpg', + album: { + name: 'Past Time', + url: 'https://eighty9s.bandcamp.com/album/past-time' + } + }, + { + name: 'What It Feels Like (feat. T3, Illa J & Kapok)', + url: 'https://potatoheadpeople.bandcamp.com/track/what-it-feels-like-feat-t3-illa-j-kapok-2', + seekPosition: 106, + artist: { + name: 'Potatohead People', + url: 'https://potatoheadpeople.bandcamp.com', + location: 'Vancouver, British Columbia', + imageUrl: 'https://f4.bcbits.com/img/21755415_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a56254833_9.jpg', + album: { + name: 'Mellow Fantasy', + url: 'https://potatoheadpeople.bandcamp.com/album/mellow-fantasy' + } + }, + { + name: 'Translate It ft. B-Money', + url: 'https://juggaknots.bandcamp.com/track/translate-it-ft-b-money', + seekPosition: 313, + artist: { + name: 'Breeze Brewin, Juggaknots', + url: 'https://juggaknots.bandcamp.com', + location: 'New York, New York', + imageUrl: 'https://f4.bcbits.com/img/14673280_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a2530516806_9.jpg', + album: { + name: 'Breeze Brewin, Juggaknots - Hindsight', + url: 'https://juggaknots.bandcamp.com/album/breeze-brewin-juggaknots-hindsight' + } + }, + { + name: 'FistfulofGreens', + url: 'https://yungmorpheus.bandcamp.com/track/fistfulofgreens', + seekPosition: 460, + artist: { + name: 'YUNGMORPHEUS & ewonee', + url: 'https://yungmorpheus.bandcamp.com', + location: 'Los Angeles, California', + imageUrl: 'https://f4.bcbits.com/img/23396531_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a122830275_9.jpg', + album: { + name: 'Thumbing Thru Foliage', + url: 'https://yungmorpheus.bandcamp.com/album/thumbing-thru-foliage' + } + }, + { + name: "When I'm Gone", + url: 'https://aaronxfairchild.bandcamp.com/track/when-im-gone', + seekPosition: 575, + artist: { + name: 'Aaron Fairchild', + url: 'https://aaronxfairchild.bandcamp.com', + location: 'Portland, Oregon', + imageUrl: 'https://f4.bcbits.com/img/25703843_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a1569522923_9.jpg', + album: { + name: 'The Sun Comes Through', + url: 'https://aaronxfairchild.bandcamp.com/album/the-sun-comes-through' + } + }, + { + name: "U callin' Me (ft. pink siifu & akeema-zane) [prod. swarvy]", + url: 'https://lojii.bandcamp.com/track/u-callin-me-ft-pink-siifu-akeema-zane-prod-swarvy', + seekPosition: 780, + artist: { + name: 'lojii', + url: 'https://lojii.bandcamp.com', + location: 'Philadelphia, Pennsylvania', + imageUrl: 'https://f4.bcbits.com/img/22800934_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a23158227_9.jpg', + album: { + name: 'lo&behold', + url: 'https://lojii.bandcamp.com/album/lo-behold-2' + } + }, + { + name: 'Wings', + url: 'https://isaiahmostafa.bandcamp.com/track/wings', + seekPosition: 955, + artist: { + name: 'Isaiah Mostafa', + url: 'https://isaiahmostafa.bandcamp.com', + location: 'Brooklyn, New York', + imageUrl: 'https://f4.bcbits.com/img/31371613_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a231490898_9.jpg', + album: { + name: 'The 37 Week Challenge', + url: 'https://isaiahmostafa.bandcamp.com/album/the-37-week-challenge' + } + }, + { + name: 'Press Play', + url: 'https://ashiakarana.bandcamp.com/track/press-play', + seekPosition: 1123, + artist: { + name: 'Ashia Karana', + url: 'https://ashiakarana.bandcamp.com', + location: 'Atlanta, Georgia', + imageUrl: 'https://f4.bcbits.com/img/21083350_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a2001339741_9.jpg' + }, + { + name: '8thstrike_', + url: 'https://knxwledge.bandcamp.com/track/8thstrike', + seekPosition: 1342, + artist: { + name: 'Knxwledge.', + url: 'https://knxwledge.bandcamp.com', + location: 'Los Angeles, California', + imageUrl: 'https://f4.bcbits.com/img/20991183_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a1776099330_9.jpg', + album: { + name: 'VGM.8', + url: 'https://knxwledge.bandcamp.com/album/vgm-8' + } + }, + { + name: 'Henny Bottles', + url: 'https://shawcalhoune.bandcamp.com/track/henny-bottles', + seekPosition: 1538, + artist: { + name: 'Shaw Calhoune', + url: 'https://shawcalhoune.bandcamp.com', + location: 'Maryland', + imageUrl: 'https://f4.bcbits.com/img/22975182_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a3592642307_9.jpg', + album: { + name: 'The Rudy Tape', + url: 'https://shawcalhoune.bandcamp.com/album/the-rudy-tape' + } + }, + { + name: '3 WAY PHONE CALL (feat. 3WAYSLIM, BIG KAHUNA OG, MONDAY NIGHT)', + url: 'https://schemeteamallstars.bandcamp.com/track/3-way-phone-call-feat-3wayslim-big-kahuna-og-monday-night', + seekPosition: 1628, + artist: { + name: 'Big Kahuna OG & Monday Night', + url: 'https://schemeteamallstars.bandcamp.com', + location: 'Richmond, Virginia', + imageUrl: 'https://f4.bcbits.com/img/13872878_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a309077249_9.jpg', + album: { + name: 'SHARK REPORT', + url: 'https://schemeteamallstars.bandcamp.com/album/shark-report' + } + }, + { + name: 'Loonng Time Coming', + url: 'https://culturepower45.bandcamp.com/track/loonng-time-coming', + seekPosition: 1833, + artist: { + name: 'Primeridian x Rashid Hadee', + url: 'https://culturepower45.bandcamp.com', + location: 'Chicago, Illinois', + imageUrl: 'https://f4.bcbits.com/img/16005517_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a3904534507_9.jpg', + album: { + name: 'Prime Diesel', + url: 'https://culturepower45.bandcamp.com/album/prime-diesel' + } + }, + { + name: 'Toucan Wing', + url: 'https://boombaptist.bandcamp.com/track/toucan-wing', + seekPosition: 2047, + artist: { + name: 'BoomBaptist', + url: 'https://boombaptist.bandcamp.com', + location: 'Austin', + imageUrl: 'https://f4.bcbits.com/img/23197928_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a3906030308_9.jpg', + album: { + name: 'Komfort Food', + url: 'https://boombaptist.bandcamp.com/album/komfort-food' + } + }, + { + name: 'A Distant Night', + url: 'https://sweetsoularya.bandcamp.com/track/a-distant-night-2', + seekPosition: 2262, + artist: { + name: 'Arya', + url: 'https://sweetsoularya.bandcamp.com', + location: 'Milan, Italy', + imageUrl: 'https://f4.bcbits.com/img/28835079_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a2599977834_9.jpg', + album: { + name: 'Peace Of Mind', + url: 'https://sweetsoularya.bandcamp.com/album/peace-of-mind' + } + }, + { + name: 'Awards (Intro) feat. Noah Archangel', + url: 'https://bohup.bandcamp.com/track/awards-intro-feat-noah-archangel', + seekPosition: 2440, + artist: { + name: 'The Band of the Hawk & Yeaux Majesty', + url: 'https://bohup.bandcamp.com', + location: 'Houston, Texas', + imageUrl: 'https://f4.bcbits.com/img/20980289_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a3760799760_9.jpg', + album: { + name: 'Memorabilia', + url: 'https://bohup.bandcamp.com/album/memorabilia' + } + }, + { + name: 'AllPraise', + url: 'https://itsovrkast.bandcamp.com/track/allpraise', + seekPosition: 2601, + artist: { + name: 'ovrkast.', + url: 'https://itsovrkast.bandcamp.com', + location: 'Oakland, California', + imageUrl: 'https://f4.bcbits.com/img/30231264_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a4154433266_9.jpg', + album: { + name: 'Try Again', + url: 'https://itsovrkast.bandcamp.com/album/try-again' + } + }, + { + name: 'Face Ft. Navy Blue', + url: 'https://itsovrkast.bandcamp.com/track/face-ft-navy-blue', + seekPosition: 2856, + artist: { + name: 'ovrkast.', + url: 'https://itsovrkast.bandcamp.com', + location: 'Oakland, California', + imageUrl: 'https://f4.bcbits.com/img/30231264_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a4154433266_9.jpg', + album: { + name: 'Try Again', + url: 'https://itsovrkast.bandcamp.com/album/try-again' + } + }, + { + name: 'Church Ft. Demahjiae', + url: 'https://itsovrkast.bandcamp.com/track/church-ft-demahjiae', + seekPosition: 3210, + artist: { + name: 'ovrkast.', + url: 'https://itsovrkast.bandcamp.com', + location: 'Oakland, California', + imageUrl: 'https://f4.bcbits.com/img/30231264_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a4154433266_9.jpg', + album: { + name: 'Try Again', + url: 'https://itsovrkast.bandcamp.com/album/try-again' + } + }, + { + name: 'Try Again', + url: 'https://itsovrkast.bandcamp.com/track/try-again', + seekPosition: 3498, + artist: { + name: 'ovrkast.', + url: 'https://itsovrkast.bandcamp.com', + location: 'Oakland, California', + imageUrl: 'https://f4.bcbits.com/img/30231264_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a4154433266_9.jpg', + album: { + name: 'Try Again', + url: 'https://itsovrkast.bandcamp.com/album/try-again' + } + }, + { + name: 'Outro (its time)', + url: 'https://itsovrkast.bandcamp.com/track/outro-its-time', + seekPosition: 3788, + artist: { + name: 'ovrkast.', + url: 'https://itsovrkast.bandcamp.com', + location: 'Oakland, California', + imageUrl: 'https://f4.bcbits.com/img/30231264_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a4154433266_9.jpg', + album: { + name: 'Try Again', + url: 'https://itsovrkast.bandcamp.com/album/try-again' + } + }, + { + name: 'The Intro(vert)', + url: 'https://jyps.bandcamp.com/track/the-intro-vert', + seekPosition: 4060, + artist: { + name: 'Jypsy', + url: 'https://jyps.bandcamp.com', + location: 'Inkster, Michigan', + imageUrl: 'https://f4.bcbits.com/img/23396305_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a1841901895_9.jpg', + album: { + name: 'Born Wit It', + url: 'https://jyps.bandcamp.com/album/born-wit-it' + } + }, + { + name: 'swordfish', + url: 'https://jdreid.bandcamp.com/track/swordfish', + seekPosition: 4190, + artist: { + name: 'JD. REID', + url: 'https://jdreid.bandcamp.com', + location: 'London, UK', + imageUrl: 'https://f4.bcbits.com/img/22245447_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a3603942056_9.jpg', + album: { + name: "North West's Finest", + url: 'https://jdreid.bandcamp.com/album/north-wests-finest' + } + }, + { + name: 'high rise in newark', + url: 'https://afrolab9000.bandcamp.com/track/high-rise-in-newark', + seekPosition: 4286, + artist: { + name: 'R.A.P. Ferreira', + url: 'https://afrolab9000.bandcamp.com', + location: 'Nashville, Tennessee', + imageUrl: 'https://f4.bcbits.com/img/4451843_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a738960096_9.jpg', + album: { + name: "bob's son: R.A.P. Ferreira in the garden level cafe of the scallops hotel", + url: 'https://afrolab9000.bandcamp.com/album/bobs-son-r-a-p-ferreira-in-the-garden-level-cafe-of-the-scallops-hotel' + } + }, + { + name: 'Volvo', + url: 'https://zekeultra.bandcamp.com/track/volvo-2', + seekPosition: 4407, + artist: { + name: 'ZekeUltra', + url: 'https://zekeultra.bandcamp.com', + location: 'Delaware', + imageUrl: 'https://f4.bcbits.com/img/15204938_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a3185032099_9.jpg', + album: { + name: 'From Time', + url: 'https://zekeultra.bandcamp.com/album/from-time' + } + }, + { + name: 'if you slide', + url: 'https://baghead1.bandcamp.com/track/if-you-slide', + seekPosition: 4532, + artist: { + name: 'Baghead', + url: 'https://baghead1.bandcamp.com', + location: 'California', + imageUrl: 'https://f4.bcbits.com/img/13162500_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a1167110350_9.jpg' + }, + { + name: 'Out The Window', + url: 'https://lovillage.bandcamp.com/track/out-the-window', + seekPosition: 4613, + artist: { + name: 'Lo Village', + url: 'https://lovillage.bandcamp.com', + location: 'Maryland', + imageUrl: 'https://f4.bcbits.com/img/21829714_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a2948500240_9.jpg' + }, + { + name: 'PROTECT feat Jack Stephenson Oliver', + url: 'https://morriarchi.bandcamp.com/track/protect-feat-jack-stephenson-oliver', + seekPosition: 4841, + artist: { + name: 'Morriarchi', + url: 'https://morriarchi.bandcamp.com', + location: 'London, UK', + imageUrl: 'https://f4.bcbits.com/img/6940676_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a2544455082_9.jpg', + album: { + name: 'Geography of Peace', + url: 'https://morriarchi.bandcamp.com/album/geography-of-peace' + } + }, + { + name: 'Constellations (feat. King Isis)', + url: 'https://stari.bandcamp.com/track/constellations-feat-king-isis', + seekPosition: 4977, + artist: { + name: 'Stari', + url: 'https://stari.bandcamp.com', + location: 'Oakland, California', + imageUrl: 'https://f4.bcbits.com/img/22116052_21.jpg' + }, + imageUrl: 'https://f4.bcbits.com/img/a3631955375_9.jpg' + } + ] +} diff --git a/examples/show/list.ts b/examples/show/list.ts new file mode 100644 index 0000000..ef9e396 --- /dev/null +++ b/examples/show/list.ts @@ -0,0 +1,6 @@ +import bcfetch from '../../'; +import util from 'util'; + +bcfetch.show.list().then((results) => { + console.log(util.inspect(results, false, null, false)); +}); diff --git a/examples/show/list_output.txt b/examples/show/list_output.txt new file mode 100644 index 0000000..be20eac --- /dev/null +++ b/examples/show/list_output.txt @@ -0,0 +1,1104 @@ +[ + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=657', + publishedDate: '09 Jun 2023 00:00:00 GMT', + description: `Christian "Bakka" Larson from Necrofier stops by to chat about the scorching black metal of the Houston band's sophomore LP.`, + imageCaption: 'Hosted by Brad Sanders. Illustration of Necrofier by Paul Grelet.', + subtitle: 'Total Southern Darkness', + screenImageUrl: 'https://f4.bcbits.com/img/32617190_0', + imageUrl: 'https://f4.bcbits.com/img/32617190_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=656', + publishedDate: '06 Jun 2023 00:00:00 GMT', + description: 'Summertime scorchers and a dive into the world of Alvin Batiste and the super rare Southern University Jazz Ensemble LPs.', + imageCaption: 'Hosted by Andrew Jervis. Illustration of Alvin Batiste by Noopur Choksi.', + subtitle: 'Come and Dance with Me', + screenImageUrl: 'https://f4.bcbits.com/img/32587815_0', + imageUrl: 'https://f4.bcbits.com/img/32587815_25.jpg' + }, + { + type: 'show', + name: 'The Hip-Hop Show', + url: 'https://bandcamp.com/?show=655', + publishedDate: '02 Jun 2023 00:00:00 GMT', + description: `New York's very own Chuck Strangers joins the show to discuss his EP, "Boys & Girls". Plus some vibes from Chè Noir & Kinna Mone.`, + imageCaption: 'Hosted by Stoney Creation. Illustration of Chuck Strangers by McKay Felt.', + subtitle: 'Hello Boys & Girls', + screenImageUrl: 'https://f4.bcbits.com/img/32550591_0', + imageUrl: 'https://f4.bcbits.com/img/32550591_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=654', + publishedDate: '30 May 2023 00:00:00 GMT', + description: 'The mighty Luke Una hosts, sharing essential tracks including a dive into his brand new E Soul Cultura comp on Mr Bongo.', + imageCaption: 'Hosted by Luke Una. Illustration of Luke Una by Hsiao-Ron Cheng.', + subtitle: 'Luke Una Takeover', + screenImageUrl: 'https://f4.bcbits.com/img/32516665_0', + imageUrl: 'https://f4.bcbits.com/img/32516665_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=653', + publishedDate: '26 May 2023 00:00:00 GMT', + description: "George Apoladimas from the Greek power metal band Sacred Outcry stops by to discuss their epic new concept album, 'Towers of Gold.'", + imageCaption: 'Hosted by Brad Sanders. Illustration of Sacred Outcry by Natalie Foss.', + subtitle: 'The Flame Rekindled', + screenImageUrl: 'https://f4.bcbits.com/img/32478153_0', + imageUrl: 'https://f4.bcbits.com/img/32478153_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=652', + publishedDate: '23 May 2023 00:00:00 GMT', + description: "Warp's Kassa Overall is ready to ball as he hosts the Weekly on the eve of his killer debut LP drop. ", + imageCaption: 'Hosted by Kassa Overall. Illustration of Kassa Overall by Paul Grelet.', + subtitle: 'The Kassa Overall Takeover', + screenImageUrl: 'https://f4.bcbits.com/img/32454762_0', + imageUrl: 'https://f4.bcbits.com/img/32454762_25.jpg' + }, + { + type: 'show', + name: 'The Hip-Hop Show', + url: 'https://bandcamp.com/?show=651', + publishedDate: '19 May 2023 00:00:00 GMT', + description: 'Holding down Richmond, VA, 3waySlim joins the show to discuss his upcoming release. Plus tunes from Black Josh & Killer Mike.', + imageCaption: 'Hosted by Stoney Creation. Illustration of 3waySlim by McKay Felt.', + subtitle: '3 Ways', + screenImageUrl: 'https://f4.bcbits.com/img/32418712_0', + imageUrl: 'https://f4.bcbits.com/img/32418712_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=650', + publishedDate: '16 May 2023 00:00:00 GMT', + description: 'Producer Scrimshire stops by to talk about community, inspiration and his new record Paroxysm.\n', + imageCaption: 'Hosted by Aly Gillani. Illustration of Scrimshire by Noopur Choksi.', + subtitle: 'Beyond The Waves', + screenImageUrl: 'https://f4.bcbits.com/img/32392173_0', + imageUrl: 'https://f4.bcbits.com/img/32392173_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=649', + publishedDate: '12 May 2023 00:00:00 GMT', + description: 'Vocalist John Collett from the dissonant death metal band Nightmarer joins the show to talk about their punishing new LP.', + imageCaption: 'Hosted by Brad Sanders. Illustration of Nightmarer by Louise Pomeroy.', + subtitle: 'Suffering Beyond Death', + screenImageUrl: 'https://f4.bcbits.com/img/32350603_0', + imageUrl: 'https://f4.bcbits.com/img/32350603_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=648', + publishedDate: '09 May 2023 00:00:00 GMT', + description: 'Adventurous Brainfeeder artist Salami Rose Joe Louis guests, plus new gems by Tony Allen, Alogte Oho, Rahill, and Kwes.', + imageCaption: 'Andrew Jervis hosts. Illustration of Salami Rose Joe Lewis by Hsiao-Ron Cheng.', + subtitle: 'New Morning', + screenImageUrl: 'https://f4.bcbits.com/img/32326221_0', + imageUrl: 'https://f4.bcbits.com/img/32326221_25.jpg' + }, + { + type: 'show', + name: 'The Hip Hop Show', + url: 'https://bandcamp.com/?show=647', + publishedDate: '05 May 2023 00:00:00 GMT', + description: `The versatile genius, Lucy Camp, joins to discuss her release, "S'Mores Vol.1". Plus features from Thundercat & Phoniks.`, + imageCaption: 'Hosted by Stoney Creation. Illustration of Lucy Camp by McKay Felt.', + subtitle: 'The Campfire', + screenImageUrl: 'https://f4.bcbits.com/img/32283423_0', + imageUrl: 'https://f4.bcbits.com/img/32283423_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=646', + publishedDate: '02 May 2023 00:00:00 GMT', + description: 'South African guests Bokani Dyer and Asher Gamedze blur genres and get radical, plus new Yussef Dayes and Thundercat.', + imageCaption: 'Hosted by Andrew Jervis. Illustration of Asher Gamedze by Paul Grelet.', + subtitle: 'Spirit People', + screenImageUrl: 'https://f4.bcbits.com/img/32244142_0', + imageUrl: 'https://f4.bcbits.com/img/32244142_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=645', + publishedDate: '28 Apr 2023 00:00:00 GMT', + description: "Madeline Michelle and Jamison Palmer from Salt Lake City's Blood Star join the show to talk about their long-awaited debut album.", + imageCaption: 'Hosted by Brad Sanders. Illustration of Blood Star by Natalie Foss.', + subtitle: 'Blood of the Stars', + screenImageUrl: 'https://f4.bcbits.com/img/32206505_0', + imageUrl: 'https://f4.bcbits.com/img/32206505_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=644', + publishedDate: '25 Apr 2023 00:00:00 GMT', + description: 'Adventurous Toronto based producer Harrison, plus new tunes from Brandt Brauer Frick, Maleet, and Alabaster dePlume.', + imageCaption: 'Hosted by Andrew Jervis. Illustration of Harrison by Hsiao-Ron Cheng.', + subtitle: 'Sounds Beautiful', + screenImageUrl: 'https://f4.bcbits.com/img/32175362_0', + imageUrl: 'https://f4.bcbits.com/img/32175362_25.jpg' + }, + { + type: 'show', + name: 'The Hip-Hop Show', + url: 'https://bandcamp.com/?show=643', + publishedDate: '21 Apr 2023 00:00:00 GMT', + description: "Cincinnati's Brandon Isaac chats about his new LP about meaningful communication. Plus new Billy Woods & your best friend jippy.", + imageCaption: 'Hosted by Stoney Creation. Illustration of Brandon Isaac by McKay Felt.', + subtitle: 'All A Part Of The Evolution', + screenImageUrl: 'https://f4.bcbits.com/img/32138517_0', + imageUrl: 'https://f4.bcbits.com/img/32138517_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=642', + publishedDate: '18 Apr 2023 00:00:00 GMT', + description: 'Dave Okumu chats about his remarkable career in music and his new record I Came From Love.\n', + imageCaption: 'Hosted by Aly Gillani. Illustration of Dave Okumu by Paul Grelet.', + subtitle: 'Love Is The Message', + screenImageUrl: 'https://f4.bcbits.com/img/32106869_0', + imageUrl: 'https://f4.bcbits.com/img/32106869_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=641', + publishedDate: '14 Apr 2023 00:00:00 GMT', + description: 'Amaya Lopez-Carromero stops by to chat about her new band, Healthyliving, and how it compares to her long-running solo project Maud the Moth.', + imageCaption: 'Hosted by Brad Sanders. Illustration of healthy living by Natalie Foss.', + subtitle: 'Abundance and Grief', + screenImageUrl: 'https://f4.bcbits.com/img/32067902_0', + imageUrl: 'https://f4.bcbits.com/img/32067902_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=640', + publishedDate: '11 Apr 2023 00:00:00 GMT', + description: 'After releasing his stellar new LP, the mighty James Holden takes us on a deeply immersive audio adventure full of discoveries.', + imageCaption: 'Hosted by James Holden. Illustration of James Holden by Noopur Choksi.', + subtitle: 'James Holden Takeover!', + screenImageUrl: 'https://f4.bcbits.com/img/32037582_0', + imageUrl: 'https://f4.bcbits.com/img/32037582_25.jpg' + }, + { + type: 'show', + name: 'The Hip-Hop Show', + url: 'https://bandcamp.com/?show=639', + publishedDate: '07 Apr 2023 00:00:00 GMT', + description: 'The trio, Planet Giza, joins the show to discuss their new release. Plus some vibes from Kaisha & Passport Rav.', + imageCaption: 'Hosted by Stoney Creation. Illustration of Planet Giza by McKay Felt.', + subtitle: 'The Three Pyramids', + screenImageUrl: 'https://f4.bcbits.com/img/31988941_0', + imageUrl: 'https://f4.bcbits.com/img/31988941_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=638', + publishedDate: '31 Mar 2023 00:00:00 GMT', + description: 'Manne Engström from the resurrected Swedish death metal band Fatal Embrace stops by to talk about their first new album in 26 years.', + imageCaption: 'Hosted by Brad Sanders. Illustration of Fatal Embrace by Louise Pomeroy.', + subtitle: 'Save Yourself, Prometheus', + screenImageUrl: 'https://f4.bcbits.com/img/31904225_0', + imageUrl: 'https://f4.bcbits.com/img/31904225_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=637', + publishedDate: '28 Mar 2023 00:00:00 GMT', + description: 'Nubya Garcia chats about London Brew and the inspiration of Miles Davis, plus cult Manchester band A Certain Ratio talk 1982.', + imageCaption: 'Hosted by Andrew Jervis. Illustration of Nubya Garcia by Hsiao-Ron Cheng.', + subtitle: 'Time For The Trailblazers', + screenImageUrl: 'https://f4.bcbits.com/img/31881218_0', + imageUrl: 'https://f4.bcbits.com/img/31881218_25.jpg' + }, + { + type: 'show', + name: 'The Hip Hop Show', + url: 'https://bandcamp.com/?show=636', + publishedDate: '24 Mar 2023 00:00:00 GMT', + description: 'Ivan Ave joins the show to discuss his recent release, "All Season Gear". Plus tunes from Arrested Development & JUNE!.', + imageCaption: 'Hosted by Stoney Creation. Illustration of Ivan Ave by McKay Felt.', + subtitle: 'Geared All Season', + screenImageUrl: 'https://f4.bcbits.com/img/31839080_0', + imageUrl: 'https://f4.bcbits.com/img/31839080_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=635', + publishedDate: '21 Mar 2023 00:00:00 GMT', + description: 'Sunshine tunes from Ireke and the adventurous electronics of Marijus Aleksa. Plus Joel Holmes, Karen y Los Remedios, and The Lahaar.', + imageCaption: 'Hosted by Andrew Jervis. Illustration of Ireke by Paul Grelet.', + subtitle: 'A Totally Tropical Taste', + screenImageUrl: 'https://f4.bcbits.com/img/31806688_0', + imageUrl: 'https://f4.bcbits.com/img/31806688_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=634', + publishedDate: '17 Mar 2023 00:00:00 GMT', + description: 'Alicia Cordisco stops by to chat about the fiercely political debut album by her thrash band, Transgressive.', + imageCaption: 'Hosted by Brad Sanders. Illustration of Transgressive by Paul Grelet.', + subtitle: 'Extreme Transgression', + screenImageUrl: 'https://f4.bcbits.com/img/31768605_0', + imageUrl: 'https://f4.bcbits.com/img/31768605_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=633', + publishedDate: '14 Mar 2023 00:00:00 GMT', + description: 'Billy Valentine talks protest songs and spiritual jazz and we tune into the psychedelic soul of The South Hill Experiment.', + imageCaption: 'Hosted by Andrew Jervis. Illustration of Billy Valentine by Hsiao-Ron Cheng.', + subtitle: 'People Hold On', + screenImageUrl: 'https://f4.bcbits.com/img/31736837_0', + imageUrl: 'https://f4.bcbits.com/img/31736837_25.jpg' + }, + { + type: 'show', + name: 'The Hip-Hop Show', + url: 'https://bandcamp.com/?show=632', + publishedDate: '10 Mar 2023 00:00:00 GMT', + description: 'Liv.e joins the show to discuss her newest release, "Girl In The Half Pearl". Plus features from MELONYX & Trutha.', + imageCaption: 'Hosted by Stoney Creation. Illustration of Liv.e by McKay Felt.', + subtitle: 'The "E" Is Silent', + screenImageUrl: 'https://f4.bcbits.com/img/31694230_0', + imageUrl: 'https://f4.bcbits.com/img/31694230_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=631', + publishedDate: '07 Mar 2023 00:00:00 GMT', + description: "Featuring interviews with Lonnie Holley and Kahil El'Zabar and a dedication to the mighty Don Cherry.", + imageCaption: 'Hosted by Andrew Jervis. Illustration of Don Cherry by Noopur Choksi.', + subtitle: 'Ancient Rhythms and Future Children', + screenImageUrl: 'https://f4.bcbits.com/img/31662233_0', + imageUrl: 'https://f4.bcbits.com/img/31662233_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=630', + publishedDate: '03 Mar 2023 00:00:00 GMT', + description: "Donna Diane from the Chicago noise-rock duo Djunah joins the show to discuss the band's furious new album.", + imageCaption: 'Hosted by Brad Sanders. Illustration of Djunah by Louise Pomeroy.', + subtitle: 'My Voice, My Chariot', + screenImageUrl: 'https://f4.bcbits.com/img/31601617_0', + imageUrl: 'https://f4.bcbits.com/img/31601617_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=628', + publishedDate: '28 Feb 2023 00:00:00 GMT', + description: 'Eddie Chacon talks about his new record Sundown and Palmskin Productions has the lowdown on the new Trickle Up EP.', + imageCaption: 'Hosted by Aly Gillani. Illustration of Eddie Chacon by Paul Grelet.', + subtitle: 'Rising Sun', + screenImageUrl: 'https://f4.bcbits.com/img/31571114_0', + imageUrl: 'https://f4.bcbits.com/img/31571114_25.jpg' + }, + { + type: 'show', + name: 'The Hip Hop Show', + url: 'https://bandcamp.com/?show=627', + publishedDate: '24 Feb 2023 00:00:00 GMT', + description: 'Oddisee joins the show to discuss his most recent release, "To What End". Plus features from Ohbliv & Nappy Nina.', + imageCaption: 'Hosted by Stoney Creation. Illustration of Oddisee by McKay Felt.', + subtitle: 'To What End', + screenImageUrl: 'https://f4.bcbits.com/img/31525923_0', + imageUrl: 'https://f4.bcbits.com/img/31525923_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=626', + publishedDate: '21 Feb 2023 00:00:00 GMT', + description: 'IG Culture drops by with a New Sector Movements EP and Pedro Ricardo chats about his brilliant genre-blurring debut for Soundway.', + imageCaption: 'Hosted by Andrew Jervis. Illustration of Pedro Ricardo by Hsiao-Ron Cheng.', + subtitle: 'A View From the Sky', + screenImageUrl: 'https://f4.bcbits.com/img/31488807_0', + imageUrl: 'https://f4.bcbits.com/img/31488807_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=625', + publishedDate: '17 Feb 2023 00:00:00 GMT', + description: 'James McBain of the one-man project Hellripper stops by to talk about the dark Scottish folklore that inspired his new album.', + imageCaption: 'Hosted by Brad Sanders. Illustration of Hellripper by Natalie Foss.', + subtitle: 'Grim and Withered', + screenImageUrl: 'https://f4.bcbits.com/img/31442297_0', + imageUrl: 'https://f4.bcbits.com/img/31442297_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=624', + publishedDate: '14 Feb 2023 00:00:00 GMT', + description: 'Multitalented Lance Skiiiwalker talks about his new LP Audiodidactic, plus Nanna B explains her soulful obsession with flowers.', + imageCaption: 'Hosted by Aly Gillani. Illustration of Lanceskiiiwalker by Noopur Choksi.', + subtitle: 'We Must Be In Love', + screenImageUrl: 'https://f4.bcbits.com/img/31418020_0', + imageUrl: 'https://f4.bcbits.com/img/31418020_25.jpg' + }, + { + type: 'show', + name: 'The Hip Hop Show', + url: 'https://bandcamp.com/?show=623', + publishedDate: '10 Feb 2023 00:00:00 GMT', + description: 'J.Rocc joins the show to discuss his recent release, "Beatitudes". Plus some joints from Ivan Ave & Baby Rose.', + imageCaption: 'Hosted by Stoney Creation. Illustration of J.Rocc by McKay Felt.', + subtitle: 'Gospel Cuts', + screenImageUrl: 'https://f4.bcbits.com/img/31375233_0', + imageUrl: 'https://f4.bcbits.com/img/31375233_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=622', + publishedDate: '07 Feb 2023 00:00:00 GMT', + description: 'UK-selector Tina Edwards guest hosts with top-drawer tunes and a chat with multi-talented North London musician Louis VI.', + imageCaption: 'Hosted by Tina Edwards. Illustration of Louis VI by Paul Grelet.', + subtitle: 'Higher Elevations', + screenImageUrl: 'https://f4.bcbits.com/img/31342526_0', + imageUrl: 'https://f4.bcbits.com/img/31342526_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=621', + publishedDate: '03 Feb 2023 00:00:00 GMT', + description: 'The Hammer and the Relentless from Malleus join the show to discuss their debut LP, colonial America, Puritanism, and more.', + imageCaption: 'Hosted by Brad Sanders. Illustration of Malleus by Louise Pomeroy.', + subtitle: 'Into the Flesh', + screenImageUrl: 'https://f4.bcbits.com/img/31297857_0', + imageUrl: 'https://f4.bcbits.com/img/31297857_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=620', + publishedDate: '31 Jan 2023 00:00:00 GMT', + description: 'Dropping tunes from her forthcoming LP alongside recent faves, Yazmin Lacey guest hosts and chats with Alex Nut and Melo-Zed. ', + imageCaption: 'Hosted by Yazmin Lacey. Illustration of Yazmin Lacey by Hsiao-Ron Cheng.', + subtitle: 'Yazmin Lacey Takeover', + screenImageUrl: 'https://f4.bcbits.com/img/31248018_0', + imageUrl: 'https://f4.bcbits.com/img/31248018_25.jpg' + }, + { + type: 'show', + name: 'The Hip Hop Show', + url: 'https://bandcamp.com/?show=619', + publishedDate: '27 Jan 2023 00:00:00 GMT', + description: 'Dropping tunes from her forthcoming LP alongside recent faves, Yazmin Lacey guest hosts and chats with Alex Nut and Melo-Zed. ', + imageCaption: 'Hosted by Stoney Creation. Illustration of Sampa The Great by McKay Felt.', + subtitle: 'Yazmin Lacey Takeover', + screenImageUrl: 'https://f4.bcbits.com/img/31209337_0', + imageUrl: 'https://f4.bcbits.com/img/31209337_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=618', + publishedDate: '24 Jan 2023 00:00:00 GMT', + description: 'Top tunes from London Brew, Overmono, and New Sector Movements, plus guests Jonah Yano and Fabiano do Nascimento.', + imageCaption: 'Hosted by Andrew Jervis. Illustration of Jonah Yano by Paul Grelet.', + subtitle: 'The Speed of Sound', + screenImageUrl: 'https://f4.bcbits.com/img/31177053_0', + imageUrl: 'https://f4.bcbits.com/img/31177053_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=617', + publishedDate: '20 Jan 2023 00:00:00 GMT', + description: 'Claire Nunez from Dryad joins the Metal Show to talk about black metal, punk ethics, the threats facing our oceans, and more.', + imageCaption: 'Hosted by Brad Sanders. Illustration of Dryad by Natalie Foss.', + subtitle: 'In the Brine Pool', + screenImageUrl: 'https://f4.bcbits.com/img/31131482_0', + imageUrl: 'https://f4.bcbits.com/img/31131482_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=616', + publishedDate: '17 Jan 2023 00:00:00 GMT', + description: "Miami's Marcus Strickland talks jazz and the environment, and Montreal's Cots blends bossa with noir. Plus fresh cuts for '23.", + imageCaption: 'Hosted by Andrew Jervis. Illustration of Marcus Strickland by Noopur Choksi.', + subtitle: 'Cosmic Dawn', + screenImageUrl: 'https://f4.bcbits.com/img/31109716_0', + imageUrl: 'https://f4.bcbits.com/img/31109716_25.jpg' + }, + { + type: 'show', + name: 'The Hip Hop Show', + url: 'https://bandcamp.com/?show=615', + publishedDate: '13 Jan 2023 00:00:00 GMT', + description: 'For the first Hip-Hop show of the year, Cakes Da Killa joins Stoney to discuss "Svengali". Plus vibes from MIKE & Jamee Cornelia.', + imageCaption: 'Hosted by Stoney Creation. Illustration of Cakes Da Killa by McKay Felt.', + subtitle: 'More Cake Please', + screenImageUrl: 'https://f4.bcbits.com/img/31064241_0', + imageUrl: 'https://f4.bcbits.com/img/31064241_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=614', + publishedDate: '10 Jan 2023 00:00:00 GMT', + description: 'Kicking off 2023 with new cuts from Bantu Spaceship, IREKE, Casey Malone, Chunky, and special guests Me and My Friends.', + imageCaption: 'Hosted by Andrew Jervis. Illustration of Me and My Friends by Hsiao-Ron Cheng.', + subtitle: 'New Year, New Sounds', + screenImageUrl: 'https://f4.bcbits.com/img/31030805_0', + imageUrl: 'https://f4.bcbits.com/img/31030805_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=613', + publishedDate: '06 Jan 2023 00:00:00 GMT', + description: "Alee Karim from The Atomic Bomb Audition joins the show to talk about the long-dormant Bay Area band's surprise return.", + imageCaption: 'Hosted by Brad Sanders. Illustration of Alee Karim by Louise Pomeroy.', + subtitle: 'Take the Pulse', + screenImageUrl: 'https://f4.bcbits.com/img/30992116_0', + imageUrl: 'https://f4.bcbits.com/img/30992116_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=612', + publishedDate: '20 Dec 2022 00:00:00 GMT', + description: 'Best of the year cuts with appearances from Ezra Collective, Gilles Peterson, Sessa, and Nosaj Thing, plus all-killer no-filler 2022 faves!', + imageCaption: 'Hosted by Andrew Jervis and Aly Gillani. Illustration of Sessa by Noopur Choksi.', + subtitle: 'Best of 2022 part 2', + screenImageUrl: 'https://f4.bcbits.com/img/30841401_0', + imageUrl: 'https://f4.bcbits.com/img/30841401_25.jpg' + }, + { + type: 'show', + name: 'The Hip Hop Show', + url: 'https://bandcamp.com/?show=611', + publishedDate: '16 Dec 2022 00:00:00 GMT', + description: 'The last Hip-Hop show of 2022! Honey Blu joins to talk about "Master Of Tha Ceremony". Plus jams from Vooo & J.Rocc.', + imageCaption: 'Hosted by Stoney Creation. Illustration of Honey Blu by McKay Felt.', + subtitle: 'Master Of Ceremonies', + screenImageUrl: 'https://f4.bcbits.com/img/30802544_0', + imageUrl: 'https://f4.bcbits.com/img/30802544_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=610', + publishedDate: '13 Dec 2022 00:00:00 GMT', + description: "Beth Orton, Misha Panfilov, and Wax Machine guest in part 1 of the Weekly's Best of 2022, plus gems from Goat, SAULT, and Ada.", + imageCaption: 'Hosted by Andrew Jervis. Illustration of Beth Orton by Hsiao-Ron Cheng.', + subtitle: 'Best Of 2022 part 1', + screenImageUrl: 'https://f4.bcbits.com/img/30774427_0', + imageUrl: 'https://f4.bcbits.com/img/30774427_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=609', + publishedDate: '09 Dec 2022 00:00:00 GMT', + description: "Frank Albanese joins a supersized, year-end edition of the show to chat about Hath's latest LP and his favorite albums of 2022.", + imageCaption: 'Hosted by Brad Sanders. Illustration of Hath by Natalie Foss.', + subtitle: 'Best Metal of 2022', + screenImageUrl: 'https://f4.bcbits.com/img/30732453_0', + imageUrl: 'https://f4.bcbits.com/img/30732453_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=608', + publishedDate: '06 Dec 2022 00:00:00 GMT', + description: "Featuring an interview with Soyuz, plus features on BBNG's Leland Whitty and a soulful side of Philadelphia.", + imageCaption: 'Hosted by Andrew Jervis. Illustration of Soyuz by Noopur Choksi.', + subtitle: 'Winter Warmers', + screenImageUrl: 'https://f4.bcbits.com/img/30702993_0', + imageUrl: 'https://f4.bcbits.com/img/30702993_25.jpg' + }, + { + type: 'show', + name: 'The Hip Hop Show', + url: 'https://bandcamp.com/?show=607', + publishedDate: '02 Dec 2022 00:00:00 GMT', + description: 'Semiratruth joins the show to discuss their latest release, "loading..". Plus some cool tunes by Saedi & Juga-Naut.', + imageCaption: 'Hosted by Stoney Creation. Illustration of Semiratruth by McKay Felt.', + subtitle: 'The Raw Truth', + screenImageUrl: 'https://f4.bcbits.com/img/30656813_0', + imageUrl: 'https://f4.bcbits.com/img/30656813_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=605', + publishedDate: '25 Nov 2022 00:00:00 GMT', + description: 'G., the UK musician behind Spider God, joins the show to discuss black metal, pop music, true crime, and much more.', + imageCaption: 'Hosted by Brad Sanders. Illustration of Spider God by Louise Pomeroy.', + subtitle: 'Spiders and Flies', + screenImageUrl: 'https://f4.bcbits.com/img/30580122_0', + imageUrl: 'https://f4.bcbits.com/img/30580122_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=604', + publishedDate: '22 Nov 2022 00:00:00 GMT', + description: "Waajeed chats about Detroit, community, and his new LP 'Memoirs of Hi-Tech Jazz'. Plus Wau Wau Collectif's cross-border collaboration.", + imageCaption: 'Hosted by Aly Gillani. Illustration of Waajeed by Hsiao-Ron Cheng.', + subtitle: 'Motor City Jazz', + screenImageUrl: 'https://f4.bcbits.com/img/30550010_0', + imageUrl: 'https://f4.bcbits.com/img/30550010_25.jpg' + }, + { + type: 'show', + name: 'The Hip Hop Show', + url: 'https://bandcamp.com/?show=603', + publishedDate: '18 Nov 2022 00:00:00 GMT', + description: 'Contour joins the show to discuss his most recent release "Onwards". Plus some tunes from B. Well and Liv.e', + imageCaption: 'Hosted by Stoney Creation. Illustration of Contour by McKay Felt.', + subtitle: "Mo' Blues", + screenImageUrl: 'https://f4.bcbits.com/img/30511344_0', + imageUrl: 'https://f4.bcbits.com/img/30511344_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=602', + publishedDate: '15 Nov 2022 00:00:00 GMT', + description: 'Evergreen boundary-pusher Idris Ackamoor guests, plus gems from around the world by SAULT, John Roseboro, and Huguette Tolinga.', + imageCaption: 'Hosted by Andrew Jervis. Illustration of Idris Ackamoor by Paul Grelet.', + subtitle: 'Future Roots', + screenImageUrl: 'https://f4.bcbits.com/img/30479888_0', + imageUrl: 'https://f4.bcbits.com/img/30479888_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=601', + publishedDate: '11 Nov 2022 00:00:00 GMT', + description: 'The violinist and vocalist Sarah Pendleton stops by to discuss the end of SubRosa and the birth of her new band, The Otolith.', + imageCaption: 'Hosted by Brad Sanders. Illustration of The Otolith by Natalie Foss.', + subtitle: 'Sing No Coda', + screenImageUrl: 'https://f4.bcbits.com/img/30434143_0', + imageUrl: 'https://f4.bcbits.com/img/30434143_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=600', + publishedDate: '08 Nov 2022 00:00:00 GMT', + description: 'Sarathy Korwar takes over the Weekly with tasty selections from Morocco, Egypt, Bangladesh, and a couple from his stellar new LP.', + imageCaption: 'Hosted by Sarathy Korwar. Illustration of Sarathy Korwar by Noopur Choksi.', + subtitle: 'Sarathy Swings In', + screenImageUrl: 'https://f4.bcbits.com/img/30410140_0', + imageUrl: 'https://f4.bcbits.com/img/30410140_25.jpg' + }, + { + type: 'show', + name: 'The Hip Hop Show', + url: 'https://bandcamp.com/?show=599', + publishedDate: '04 Nov 2022 00:00:00 GMT', + description: "Celebrating the Hip Hop Show's 50th episode, we feature Cherele and Sasa Juste to discuss their most recent releases.", + imageCaption: 'Hosted by Stoney Creation. Illustration of Cherele and Sara Juste by McKay Felt.', + subtitle: 'The Big Five-O', + screenImageUrl: 'https://f4.bcbits.com/img/30361400_0', + imageUrl: 'https://f4.bcbits.com/img/30361400_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=598', + publishedDate: '01 Nov 2022 00:00:00 GMT', + description: 'This week Mount Kimbie stop by to talk about their split LP MK 3.5: Die Cuts | City Planning.', + imageCaption: 'Hosted by Aly Gillani. Illustration of Mount Kimble by Hsiao-Ron Cheng.', + subtitle: 'Two Sides Of A Story', + screenImageUrl: 'https://f4.bcbits.com/img/30321523_0', + imageUrl: 'https://f4.bcbits.com/img/30321523_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=597', + publishedDate: '28 Oct 2022 00:00:00 GMT', + description: 'On a supersized Halloween special, Chris Morrison and Corey Clark from Mother of Graves stop by to talk about their death/doom debut.', + imageCaption: 'Hosted by Brad Sanders. Illustration of Mother of Graves by Louise Pomeroy.', + subtitle: 'Of Solitude and Stone', + screenImageUrl: 'https://f4.bcbits.com/img/30281293_0', + imageUrl: 'https://f4.bcbits.com/img/30281293_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=596', + publishedDate: '25 Oct 2022 00:00:00 GMT', + description: 'Coco Maria drops a tasty tropical selection featuring Derya Yıldırım and Grup Şimşek.', + imageCaption: 'Hosted by Coco Maria. Illustration by Noopur Choksi.', + subtitle: 'Coco Maria Takeover', + screenImageUrl: 'https://f4.bcbits.com/img/30249395_0', + imageUrl: 'https://f4.bcbits.com/img/30249395_25.jpg' + }, + { + type: 'show', + name: 'The Hip Hop Show', + url: 'https://bandcamp.com/?show=595', + publishedDate: '21 Oct 2022 00:00:00 GMT', + description: 'Psalm One joins the show to discuss her most recent book and music releases. Plus some great vibes from Vicky Casis and Tee Peters.', + imageCaption: 'Hosted by Stoney Creation. Illustration of Psalm One by McKay Felt.', + subtitle: 'Setting It Straight', + screenImageUrl: 'https://f4.bcbits.com/img/30210734_0', + imageUrl: 'https://f4.bcbits.com/img/30210734_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=594', + publishedDate: '18 Oct 2022 00:00:00 GMT', + description: "Hagop Tchaparian chats about his debut LP on Four Tet's Text Records plus new Plaid, Mosca, Fracture, Paw Paw Rod and Liv.e", + imageCaption: 'Hosted by Andrew Jervis. Illustration of Hagop Tchaparian by Paul Grelet.', + subtitle: 'A Riot Going On', + screenImageUrl: 'https://f4.bcbits.com/img/30179579_0', + imageUrl: 'https://f4.bcbits.com/img/30179579_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=593', + publishedDate: '14 Oct 2022 00:00:00 GMT', + description: 'All four members of the Massachusetts wrecking crew Escuela Grind join the show to chat about their killer live show and new LP.', + imageCaption: 'Hosted by Brad Sanders. Illustration of Escuela Grind by Natalie Foss.', + subtitle: 'Close to the Edge', + screenImageUrl: 'https://f4.bcbits.com/img/30144506_0', + imageUrl: 'https://f4.bcbits.com/img/30144506_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=592', + publishedDate: '11 Oct 2022 00:00:00 GMT', + description: 'J/K Group, Surprise Chef, Don Glori, and Left Ear Records rep Melbourne, plus new SAULT, John Wizards, and Waajeed.', + imageCaption: 'Hosted by Andrew Jervis. Illustration of JK Group by Hsiao-Ron Cheng.', + subtitle: 'Melbourne Meltdown', + screenImageUrl: 'https://f4.bcbits.com/img/30112291_0', + imageUrl: 'https://f4.bcbits.com/img/30112291_25.jpg' + }, + { + type: 'show', + name: 'The Hip Hop Show', + url: 'https://bandcamp.com/?show=591', + publishedDate: '07 Oct 2022 00:00:00 GMT', + description: 'Open Mike Eagle joins the show to discuss his newest release. Plus some tunes from Greydon Square and Zion Garcia.', + imageCaption: 'Hosted by Stoney Creation. Illustration of Open Mike Eagle by McKay Felt.', + subtitle: 'Spirit of the Mixtape', + screenImageUrl: 'https://f4.bcbits.com/img/30064067_0', + imageUrl: 'https://f4.bcbits.com/img/30064067_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=590', + publishedDate: '04 Oct 2022 00:00:00 GMT', + description: 'Brazilian legend Joyce on her lost LP, along with Arima Ederra. Plus new Dezron Douglas, Dom Maker, and Madison McFerrin. ', + imageCaption: 'Hosted by Andrew Jervis. Illustration of Joyce Moreno by Noopur Choksi.', + subtitle: 'Re-Joyce!', + screenImageUrl: 'https://f4.bcbits.com/img/30028967_0', + imageUrl: 'https://f4.bcbits.com/img/30028967_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=589', + publishedDate: '30 Sep 2022 00:00:00 GMT', + description: 'Gwyn Strang and Sean Bilovecky from the Cleveland doom band Frayle join the show to discuss their cathartic new album.', + imageCaption: 'Hosted by Brad Sanders. Illustration of Frayle by Louise Pomeroy.', + subtitle: "There's Darkness in Me", + screenImageUrl: 'https://f4.bcbits.com/img/29984750_0', + imageUrl: 'https://f4.bcbits.com/img/29984750_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=588', + publishedDate: '27 Sep 2022 00:00:00 GMT', + description: 'Makaya McCraven and Say She She guest, plus we pay tribute to Pharoah Sanders and Pucho.', + imageCaption: 'Hosted by Andrew Jervis. Illustration of Say She She by Paul Grelet.', + subtitle: "You Gotta' have Peace and Love", + screenImageUrl: 'https://f4.bcbits.com/img/29959942_0', + imageUrl: 'https://f4.bcbits.com/img/29959942_25.jpg' + }, + { + type: 'show', + name: 'The Hip Hop Show', + url: 'https://bandcamp.com/?show=587', + publishedDate: '23 Sep 2022 00:00:00 GMT', + description: 'Sonnyjim & The Purist join the show to discuss their recent release, "White Girl Wasted." Plus features from Westside Gunn & Wiki.', + imageCaption: 'Hosted by Stoney Creation. Illustration of Sonny Jim & The Purist by McKay Felt.', + subtitle: 'FUN', + screenImageUrl: 'https://f4.bcbits.com/img/29923373_0', + imageUrl: 'https://f4.bcbits.com/img/29923373_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=586', + publishedDate: '20 Sep 2022 00:00:00 GMT', + description: 'Alhaji Waziri Oshomah beams in from Nigeria to spread the spiritual vibes, plus new gems by BCUC, Bjork, and Yanna Momina.', + imageCaption: 'Andrew Jervis hosts. Alhaji Waziri Oshomah by Hsiao-Ron Cheng.', + subtitle: 'It Began In Africa', + screenImageUrl: 'https://f4.bcbits.com/img/29892948_0', + imageUrl: 'https://f4.bcbits.com/img/29892948_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=585', + publishedDate: '16 Sep 2022 00:00:00 GMT', + description: "The indigenous American musician Sgah'gahsowáh stops by to talk about his debut album as Blackbraid on this all-black metal episode.", + imageCaption: `Hosted by Brad Sanders. Illustration of Sgah'gahsowáh by Natalie Foss.`, + subtitle: 'The River of Time', + screenImageUrl: 'https://f4.bcbits.com/img/29857950_0', + imageUrl: 'https://f4.bcbits.com/img/29857950_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=584', + publishedDate: '13 Sep 2022 00:00:00 GMT', + description: 'Brazilian musician Pedro Guinu pays homage to Rio, plus new Jonah Yano, dreamcastmoe, and Takuro Okada.', + imageCaption: 'Hosted by Andrew Jervis. Illustration of Guinu by Noopur Choksi.', + subtitle: 'When The Angels Sing', + screenImageUrl: 'https://f4.bcbits.com/img/29828342_0', + imageUrl: 'https://f4.bcbits.com/img/29828342_25.jpg' + }, + { + type: 'show', + name: 'The Hip Hop Show', + url: 'https://bandcamp.com/?show=583', + publishedDate: '09 Sep 2022 00:00:00 GMT', + description: 'Lukah joins the show to discuss his most recent releases, plus some features from Brainorchestra & Waxfactor.', + imageCaption: 'Hosted by Stoney Creation. Illustration of Lukah by McKay Felt.', + subtitle: 'Lukahverse', + screenImageUrl: 'https://f4.bcbits.com/img/29790791_0', + imageUrl: 'https://f4.bcbits.com/img/29790791_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=582', + publishedDate: '06 Sep 2022 00:00:00 GMT', + description: 'In the latest Bandcamp Weekly we chat to Eliza about her new album ‘A Sky Without Stars’.', + imageCaption: 'Hosted by Aly Gillani. Illustration of Eliza by Paul Grelet.', + subtitle: 'Star Gazing', + screenImageUrl: 'https://f4.bcbits.com/img/29759121_0', + imageUrl: 'https://f4.bcbits.com/img/29759121_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=581', + publishedDate: '02 Sep 2022 00:00:00 GMT', + description: "Mikael Stanne and Niclas Engelin stop by to discuss The Halo Effect's debut album and their three decades in the Gothenburg metal scene.", + imageCaption: 'Hosted by Brad Sanders. Illustration of The Halo Effect by Louise Pomeroy.', + subtitle: 'In Broken Trust', + screenImageUrl: 'https://f4.bcbits.com/img/29705276_0', + imageUrl: 'https://f4.bcbits.com/img/29705276_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=580', + publishedDate: '30 Aug 2022 00:00:00 GMT', + description: 'The Stepney Sisters preserve the legacy of trailblazer Charles Stepney and LEFTO bigs up the Belgian music scene.', + imageCaption: 'Hosted by Andrew Jervis. Charles Stepney’s Daughters by Hsiao-Ron Cheng.', + subtitle: 'Black Gold', + screenImageUrl: 'https://f4.bcbits.com/img/29676073_0', + imageUrl: 'https://f4.bcbits.com/img/29676073_25.jpg' + }, + { + type: 'show', + name: 'The Hip-Hop Show', + url: 'https://bandcamp.com/?show=579', + publishedDate: '26 Aug 2022 00:00:00 GMT', + description: 'Cadence Weapon joins the show to discuss his latest memoir release, "Bedroom Rapper" plus features from Peyton and Sasa Juste.', + imageCaption: 'Hosted by Stoney Creation. Illustration of Cadence Weapon by McKay Felt.', + subtitle: 'Bedroom Rapper', + screenImageUrl: 'https://f4.bcbits.com/img/29642421_0', + imageUrl: 'https://f4.bcbits.com/img/29642421_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=578', + publishedDate: '23 Aug 2022 00:00:00 GMT', + description: 'Thee Sacred Souls stop by to talk about sweet soul music, the importance of community and their debut album.', + imageCaption: 'Hosted by Aly Gillani. Illustration of Thee Sacred Souls by Hsiao-Ron Cheng.', + subtitle: 'Sweet Soul Music', + screenImageUrl: 'https://f4.bcbits.com/img/29609421_0', + imageUrl: 'https://f4.bcbits.com/img/29609421_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=577', + publishedDate: '19 Aug 2022 00:00:00 GMT', + description: 'Kyle McNeill joins the show to talk about his new project, Phantom Spell, plus proggy picks from Porcupine Tree and Kansas.\n', + imageCaption: 'Hosted by Brad Sanders. Illustration of Phantom Spell by Natalie Foss.', + subtitle: 'Up the Tower', + screenImageUrl: 'https://f4.bcbits.com/img/29570822_0', + imageUrl: 'https://f4.bcbits.com/img/29570822_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=576', + publishedDate: '16 Aug 2022 00:00:00 GMT', + description: 'Magical musical adventures with guests John Beltran and Kim Myhr, plus new Sarathy Korwar, Vanishing Twin, and Dayme Arocena.', + imageCaption: 'Hosted by Andrew Jervis. Illustration of John Beltran by Paul Grelet.', + subtitle: "It's a Kind of Magic", + screenImageUrl: 'https://f4.bcbits.com/img/29546967_0', + imageUrl: 'https://f4.bcbits.com/img/29546967_25.jpg' + }, + { + type: 'show', + name: 'The Hip-Hop Show', + url: 'https://bandcamp.com/?show=575', + publishedDate: '12 Aug 2022 00:00:00 GMT', + description: 'Spote Breeze joins the show to discuss his latest release, "Cascade Viewing". Plus features from Open Mike Eagle and Mejiwahn.', + imageCaption: 'Hosted by Stoney Creation. Illustration of Spote Beeze by McKay Felt', + subtitle: 'Spote Breeze', + screenImageUrl: 'https://f4.bcbits.com/img/29514018_0', + imageUrl: 'https://f4.bcbits.com/img/29514018_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=574', + publishedDate: '09 Aug 2022 00:00:00 GMT', + description: "Jam-packed with hot summer tunes, plus Eblis Alvarez from Meridian Brothers drops us deep into Bogota's buzzing scene.", + imageCaption: 'Hosted by Andrew Jervis. Illustration of Meridian Brothers by Hsiao-Ron Cheng.', + subtitle: 'Moving Forward, Respecting Tradition', + screenImageUrl: 'https://f4.bcbits.com/img/29482701_0', + imageUrl: 'https://f4.bcbits.com/img/29482701_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=573', + publishedDate: '05 Aug 2022 00:00:00 GMT', + description: 'Rachl "Raxx" Quinn and William Lloyd Walker of Graveshadow join the show to chat about their new symphonic power metal album.', + imageCaption: 'Hosted by Brad Sanders. Illustration of Graveshadow by Louise Pomeroy.', + subtitle: 'Becoming My Enemy ', + screenImageUrl: 'https://f4.bcbits.com/img/29448219_0', + imageUrl: 'https://f4.bcbits.com/img/29448219_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=572', + publishedDate: '02 Aug 2022 00:00:00 GMT', + description: "Ahead of their highly anticipated debut, members of London's Kokoroko host, with a selection of LP tracks and inspirations.", + imageCaption: 'Hosted by Kokoroko. Illustration of Kokoroko by Noopur Choksi.', + subtitle: 'Kokoroko Takeover', + screenImageUrl: 'https://f4.bcbits.com/img/29428145_0', + imageUrl: 'https://f4.bcbits.com/img/29428145_25.jpg' + }, + { + type: 'show', + name: 'The Hip-Hop Show', + url: 'https://bandcamp.com/?show=571', + publishedDate: '29 Jul 2022 00:00:00 GMT', + description: 'Jansport J joins the show to discuss his most recent release with AJ Snow. Plus some tunes from Lakim & Ojerime', + imageCaption: 'Hosted by Stoney Creation. Illustration of Jansport J by Hsiao-Ron Cheng.', + subtitle: 'The Player Sound', + screenImageUrl: 'https://f4.bcbits.com/img/29384388_0', + imageUrl: 'https://f4.bcbits.com/img/29384388_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=570', + publishedDate: '26 Jul 2022 00:00:00 GMT', + description: "Live in Nashville, Lambchop's Kurt Wagner drops an eclectic mix of inspirations, collaborations, and music from his new LP.", + imageCaption: 'Hosted by Kurt Wagner. Illustration of Kurt Wagner by Paul Grelet.', + subtitle: 'Lambchop Takeover!', + screenImageUrl: 'https://f4.bcbits.com/img/29353671_0', + imageUrl: 'https://f4.bcbits.com/img/29353671_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=569', + publishedDate: '22 Jul 2022 00:00:00 GMT', + description: 'Alasdair Dunn from black metal ensemble Ashenspire joins the show, plus new tunes by Scarcity and Crestfallen Dusk.\n', + imageCaption: 'Hosted by Brad Sanders. Illustration of Ashenspire by Natalie Foss.', + subtitle: 'Only The Great Many', + screenImageUrl: 'https://f4.bcbits.com/img/29319910_0', + imageUrl: 'https://f4.bcbits.com/img/29319910_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=568', + publishedDate: '19 Jul 2022 00:00:00 GMT', + description: 'Sweet summertime sounds plus a dive into the latest from Brazil with Bruno Berle and friends.', + imageCaption: 'Hosted by Andrew Jervis. Illustration of Bruno Berle by Hsiao-Ron Cheng.', + subtitle: 'Beauty in Brazil', + screenImageUrl: 'https://f4.bcbits.com/img/29288958_0', + imageUrl: 'https://f4.bcbits.com/img/29288958_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=567', + publishedDate: '12 Jul 2022 00:00:00 GMT', + description: 'Presenting a glimpse at the next wave and new sounds of British jazz with debuts from Jasmine Myra and Doom Cannon. ', + imageCaption: 'Hosted by Andrew Jervis. Illustration of Jasmine Myra by Noopur Choksi.', + subtitle: 'Debut Duo', + screenImageUrl: 'https://f4.bcbits.com/img/29221369_0', + imageUrl: 'https://f4.bcbits.com/img/29221369_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show ', + url: 'https://bandcamp.com/?show=566', + publishedDate: '08 Jul 2022 00:00:00 GMT', + description: "Dana Helmuth and Maria Geisbert stop by to talk about Yatra's death metal rebirth, plus a block of classic doom by Iron Man.", + imageCaption: 'Hosted by Brad Sanders . Illustration of Yatra by Louise Pomeroy.', + subtitle: 'Born Into Chaos', + screenImageUrl: 'https://f4.bcbits.com/img/29190387_0', + imageUrl: 'https://f4.bcbits.com/img/29190387_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=565', + publishedDate: '05 Jul 2022 00:00:00 GMT', + description: "New music by aja monet, Makaya McCraven, and Daphni, plus Oakland's Mejiwahn chats about his breezy and transportive LP.", + imageCaption: 'Hosted by Andrew Jervis. Illustration of Mejiwahn by Paul Grelet.', + subtitle: 'Fly Away', + screenImageUrl: 'https://f4.bcbits.com/img/29158632_0', + imageUrl: 'https://f4.bcbits.com/img/29158632_25.jpg' + }, + { + type: 'show', + name: 'The Hip-Hop Show', + url: 'https://bandcamp.com/?show=564', + publishedDate: '01 Jul 2022 00:00:00 GMT', + description: 'Quelle Chris discusses his most recent release, "DEATHFAME", plus new Sampa the Great & Homeboy Sandman.', + imageCaption: 'Hosted by Stoney Creation. Illustration of Quelle Chris by McKay Felt.', + subtitle: 'Fame After Death', + screenImageUrl: 'https://f4.bcbits.com/img/29126106_0', + imageUrl: 'https://f4.bcbits.com/img/29126106_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=563', + publishedDate: '28 Jun 2022 00:00:00 GMT', + description: "Errol and Alex Rita's Touching Bass drop by for our special London edition of the Bandcamp Weekly.", + imageCaption: 'Hosted by Aly Gillani. Illustration of Touching Bass by Hsiao-Ron Cheng.', + subtitle: "It's A London Thing", + screenImageUrl: 'https://f4.bcbits.com/img/29094747_0', + imageUrl: 'https://f4.bcbits.com/img/29094747_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=562', + publishedDate: '24 Jun 2022 00:00:00 GMT', + description: 'Mille Petrozza stops by to talk about the new Kreator album, plus music by Trauma Bond, Sunrise Patriot Motion, and more.', + imageCaption: 'Hosted by Brad Sanders. Illustration of Kreator by Natalie Foss.', + subtitle: 'Hate Is the Virus', + screenImageUrl: 'https://f4.bcbits.com/img/29062695_0', + imageUrl: 'https://f4.bcbits.com/img/29062695_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=561', + publishedDate: '21 Jun 2022 00:00:00 GMT', + description: 'Hollie Cook drops tunes from her latest supremely soulful reggae LP Happy Hour plus inspirational eclectic selections from peers.', + imageCaption: 'Hosted by Hollie Cook. Illustration of Hollie Cook by Noopur Choksi.', + subtitle: 'Hollie Cook Takeover', + screenImageUrl: 'https://f4.bcbits.com/img/29024512_0', + imageUrl: 'https://f4.bcbits.com/img/29024512_25.jpg' + }, + { + type: 'show', + name: 'The Hip-Hop Show', + url: 'https://bandcamp.com/?show=560', + publishedDate: '17 Jun 2022 00:00:00 GMT', + description: 'Inkswel joins the show to discuss his most recent release, "Chasing Infinity." Plus hot tunes by Chris Crack & Bishop Nehru.', + imageCaption: 'Hosted by Stoney Creation. Illustration of Inkswel by McKay Felt.', + subtitle: 'Manifestation', + screenImageUrl: 'https://f4.bcbits.com/img/28998233_0', + imageUrl: 'https://f4.bcbits.com/img/28998233_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=559', + publishedDate: '14 Jun 2022 00:00:00 GMT', + description: 'Tropicalia-inspired troubadour Sessa hosts and shines a light \n' + + 'on fellow Brazilians Ana Frango Elétrico, Tim Bernardes, and JADSA.', + imageCaption: 'Hosted by Andrew Jervis. Illustration of Sessa by Noopur Choksi.', + subtitle: 'Sessa Swings In', + screenImageUrl: 'https://f4.bcbits.com/img/28964815_0', + imageUrl: 'https://f4.bcbits.com/img/28964815_25.jpg' + }, + { + type: 'show', + name: 'The Metal Show', + url: 'https://bandcamp.com/?show=558', + publishedDate: '10 Jun 2022 00:00:00 GMT', + description: "Terry Savastano from Come to Grief stops by to talk about the sludge band's debut album and the healing power of depressing music.", + imageCaption: 'Hosted by Brad Sanders. Illustration of Come to Grief by Louise Pomeroy.', + subtitle: "Life's Curse", + screenImageUrl: 'https://f4.bcbits.com/img/28930916_0', + imageUrl: 'https://f4.bcbits.com/img/28930916_25.jpg' + }, + { + type: 'show', + name: 'Bandcamp Weekly', + url: 'https://bandcamp.com/?show=557', + publishedDate: '07 Jun 2022 00:00:00 GMT', + description: 'Featuring jaimie branch and Jason Nazary aka Anteloper, plus new discoveries by JP Borgneth, Emeka Ogboh, and OT X-TET.', + imageCaption: 'Hosted by Andrew Jervis. Illustration of Anteloper by Hsiao-Ron Cheng.', + subtitle: 'Play It Loud', + screenImageUrl: 'https://f4.bcbits.com/img/28898040_0', + imageUrl: 'https://f4.bcbits.com/img/28898040_25.jpg' + }, + { + type: 'show', + name: 'The Hip-Hop Show', + url: 'https://bandcamp.com/?show=556', + publishedDate: '03 Jun 2022 00:00:00 GMT', + description: 'Nigerian-born, Rome based producer and songwriter Shunaji joins the show, plus some tunes from Koolade and Theravada.', + imageCaption: 'Hosted by Stoney Creation. Illustration of Shunaji by McKay Felt.', + subtitle: 'Alien Alienated', + screenImageUrl: 'https://f4.bcbits.com/img/28865898_0', + imageUrl: 'https://f4.bcbits.com/img/28865898_25.jpg' + }, + ... 531 more items +] diff --git a/examples/tag/getAlbumHighlights.ts b/examples/tag/getAlbumHighlights.ts new file mode 100644 index 0000000..f7f50e2 --- /dev/null +++ b/examples/tag/getAlbumHighlights.ts @@ -0,0 +1,13 @@ +import bcfetch from '../../'; +import util from 'util'; + +const tagUrl = 'https://bandcamp.com/tag/noise'; + +const params = { + tagUrl, + imageFormat: 'art_app_large' +}; + +bcfetch.tag.getAlbumHighlights(params).then((results) => { + console.log(util.inspect(results, false, null, false)); +}); diff --git a/examples/tag/getAlbumHighlights_output.txt b/examples/tag/getAlbumHighlights_output.txt new file mode 100644 index 0000000..97fa3b5 --- /dev/null +++ b/examples/tag/getAlbumHighlights_output.txt @@ -0,0 +1,776 @@ +[ + { + name: 'new_releases', + title: 'new and notable', + items: [ + { + type: 'album', + name: 'Bhoot Ghar: Sounds Of The Kathmandu Horror House', + url: 'https://aarondilloway.bandcamp.com/album/bhoot-ghar-sounds-of-the-kathmandu-horror-house', + imageUrl: 'https://f4.bcbits.com/img/a1953552587_16.jpg', + genre: 'experimental', + artist: { + name: 'Aaron Dilloway', + url: 'https://aarondilloway.bandcamp.com' + }, + featuredTrack: { + name: 'Bhoot Ghar: Entrance Soundtrack', + streamUrl: 'https://t4.bcbits.com/stream/cd82f615cd6c495067545568e68b6fe7/mp3-128/3510304055?p=0&ts=1686495008&t=f7d6df29657be7d5e13c6d7d8d1a305d1678d900&token=1686495008_44011bff7332ede2698a6e359805fe22b6c509be' + } + }, + { + type: 'album', + name: 'Blurry Landscapes', + url: 'https://lajungle.bandcamp.com/album/blurry-landscapes', + imageUrl: 'https://f4.bcbits.com/img/a3508479022_16.jpg', + genre: 'alternative', + artist: { name: 'LA JUNGLE', url: 'https://lajungle.bandcamp.com' }, + featuredTrack: { + name: 'La Compagnie De La Chanson', + streamUrl: 'https://t4.bcbits.com/stream/33842feaca9b263627517e2dbe8c2483/mp3-128/453427226?p=0&ts=1686495008&t=1b5e7ce660d9069f0c8f72a0c493130b277f3364&token=1686495008_adc43eea6c0b0c1a2e551d811bd40877e1e480e0' + } + }, + { + type: 'album', + name: 'Split', + url: 'https://pcrv.bandcamp.com/album/split-4', + imageUrl: 'https://f4.bcbits.com/img/a1022068674_16.jpg', + genre: 'experimental', + artist: { + name: 'Human Fluid Rot / PCRV', + url: 'https://pcrv.bandcamp.com' + }, + featuredTrack: { + name: 'Chamber Maker', + streamUrl: 'https://t4.bcbits.com/stream/6d3e7e254a3234b4354e37deee4e6601/mp3-128/99949791?p=0&ts=1686495008&t=14a6aa60f99967ec6141a7b9164ce1d669fd7c18&token=1686495008_a06cb87d08961b803624fd7dd8dad794cae2dafd' + } + }, + { + type: 'album', + name: 'Satan', + url: 'https://fortevilfruit.bandcamp.com/album/satan', + imageUrl: 'https://f4.bcbits.com/img/a2058438844_16.jpg', + genre: 'experimental', + artist: { + name: 'Golem Mecanique', + url: 'https://fortevilfruit.bandcamp.com' + }, + featuredTrack: { + name: 'The False Prophet', + streamUrl: 'https://t4.bcbits.com/stream/a21eeb119659148b9824446549925b93/mp3-128/4274107030?p=0&ts=1686495008&t=713de78f51556129a7bce79062625430328405b9&token=1686495008_4b40faee07f48fb9666b0f062917b091d542fa1a' + } + }, + { + type: 'album', + name: 'Welcome to Fun City', + url: 'https://ademoninfuncity.bandcamp.com/album/welcome-to-fun-city', + imageUrl: 'https://f4.bcbits.com/img/a4151696182_16.jpg', + genre: 'ambient', + artist: { + name: 'A Demon In Fun City', + url: 'https://ademoninfuncity.bandcamp.com' + }, + featuredTrack: { + name: 'Welcome to Fun City', + streamUrl: 'https://t4.bcbits.com/stream/9eb5f939b6fd98a47047940a2709fa24/mp3-128/835754965?p=0&ts=1686495008&t=94c9047430a3bce3f0e91832d0af41702ba05c40&token=1686495008_7e37edd136e41eb863ec284de751f19fc09eb3f1' + } + }, + { + type: 'album', + name: '(And Lies In General)', + url: 'https://xqui.bandcamp.com/album/and-lies-in-general', + imageUrl: 'https://f4.bcbits.com/img/a3014948462_16.jpg', + genre: 'experimental', + artist: { name: 'Xqui', url: 'https://xqui.bandcamp.com' }, + featuredTrack: { + name: 'Betroth Thou Louis Fifteen (Collins Remix)', + streamUrl: 'https://t4.bcbits.com/stream/42b77faa5f31195a9c7456bc72224c1b/mp3-128/2704241393?p=0&ts=1686495008&t=857b6395243189023b0af1c783aaed7c822fb921&token=1686495008_6eeedf711e5f0122d9a8a8ebbf377c2ca56095dd' + } + }, + { + type: 'album', + name: 'Atlas', + url: 'https://bacfora.bandcamp.com/album/atlas', + imageUrl: 'https://f4.bcbits.com/img/a1079703575_16.jpg', + genre: 'experimental', + artist: { name: 'Bacfora', url: 'https://bacfora.bandcamp.com' }, + featuredTrack: { + name: 'Where i was', + streamUrl: 'https://t4.bcbits.com/stream/4a79c1d83e38e014282fa2ecced3de66/mp3-128/718414072?p=0&ts=1686495008&t=8314f511ecd77ead2cd4c0a907a9fcc3b1db3062&token=1686495008_362ae94c818abeb74770ecabd1d2738a63a8759b' + } + }, + { + type: 'album', + name: 'Longsword IV', + url: 'https://longsword.bandcamp.com/album/longsword-iv', + imageUrl: 'https://f4.bcbits.com/img/a2016551705_16.jpg', + genre: 'ambient', + artist: { name: 'Longsword', url: 'https://longsword.bandcamp.com' }, + featuredTrack: { + name: 'Champions of The Kingdom Pt. I', + streamUrl: 'https://t4.bcbits.com/stream/8c07f7fc8f850a5a9697fa4b2d0bdf18/mp3-128/4009095460?p=0&ts=1686495008&t=7b5daa73142872aaa22b041e0a166f3e92e83b82&token=1686495008_ecc5a7ec334ba39d076c5a29cfb55e9be871708b' + } + }, + { + type: 'album', + name: 'Live at the Stone', + url: 'https://decimus.bandcamp.com/album/live-at-the-stone', + imageUrl: 'https://f4.bcbits.com/img/a1705820893_16.jpg', + genre: 'electronic', + artist: { + name: 'Thurston Moore/Samara Lubelski/Bill Nace', + url: 'https://decimus.bandcamp.com' + }, + featuredTrack: { + name: 'Side A', + streamUrl: 'https://t4.bcbits.com/stream/2b9d93a698ee7385100029ba8c4976f3/mp3-128/3991613774?p=0&ts=1686495008&t=3568a15fce7920f001d26dc6947d832e0e28ee62&token=1686495008_78e5954a4b0d3d9126b78ef57f56a71c3a8cca64' + } + }, + { + type: 'album', + name: 'Thanatos [EP]', + url: 'https://kaitendaentai.bandcamp.com/album/thanatos-ep', + imageUrl: 'https://f4.bcbits.com/img/a102356257_16.jpg', + genre: 'experimental', + artist: { name: '廻転楕円体', url: 'https://kaitendaentai.bandcamp.com' }, + featuredTrack: { + name: 'Thanatos', + streamUrl: 'https://t4.bcbits.com/stream/8798e7cfaa9d8d00b216c78c853cb46a/mp3-128/1155149159?p=0&ts=1686495008&t=57d2a9ac85ad0d6e2bd1208eee1fec8182ae2c82&token=1686495008_d4b764c7f9b99a0af7290bee9d665c6c318aed49' + } + }, + { + type: 'album', + name: 'Diskretion. Funktionsmusik für Verrichtungsboxen', + url: 'https://aufabwegen.bandcamp.com/album/diskretion-funktionsmusik-f-r-verrichtungsboxen', + imageUrl: 'https://f4.bcbits.com/img/a2438580019_16.jpg', + genre: 'experimental', + artist: { + name: 'Statische Musen', + url: 'https://aufabwegen.bandcamp.com' + }, + featuredTrack: { + name: 'Box 1 (abgelehnt)', + streamUrl: 'https://t4.bcbits.com/stream/8e219c39b501cc2372551848ff1c991e/mp3-128/2742508817?p=0&ts=1686495008&t=cf1ac380dd68213a9317639c1ea28baa2ea1ef88&token=1686495008_716df912366feb191456aff9e7759490850e16a1' + } + }, + { + type: 'album', + name: 'Vampyrrissey / MexiKanye', + url: 'https://damienrecords666.bandcamp.com/album/vampyrrissey-mexikanye', + imageUrl: 'https://f4.bcbits.com/img/a3108815630_16.jpg', + genre: 'experimental', + artist: { + name: 'Eyes of a Dreamer', + url: 'https://damienrecords666.bandcamp.com' + }, + featuredTrack: { + name: 'Vampyrrissey', + streamUrl: 'https://t4.bcbits.com/stream/cbaebb36188a6f021175d918bd8e66fa/mp3-128/4220951645?p=0&ts=1686495008&t=d3381c4001786b0aaebde2ec17c4091c279c6af0&token=1686495008_15a5de73ab6630672bc814b38055a0fb05bc5dcd' + } + }, + { + type: 'album', + name: 'Down Hell', + url: 'https://gatesofhypnos.bandcamp.com/album/down-hell', + imageUrl: 'https://f4.bcbits.com/img/a434077486_16.jpg', + genre: 'experimental', + artist: { + name: 'Mass Graven Image', + url: 'https://gatesofhypnos.bandcamp.com' + }, + featuredTrack: { + name: 'Messianic Rapist', + streamUrl: 'https://t4.bcbits.com/stream/7048343f11315c995fb99798c19a0634/mp3-128/2723055589?p=0&ts=1686495008&t=525eb13fffe42d686c5a555c47bca82a8a98af02&token=1686495008_c8e39fd2285c0576c76e402eb08dafbb9bf83802' + } + }, + { + type: 'album', + name: 'Burning the Wickerman', + url: 'https://gatesofhypnos.bandcamp.com/album/burning-the-wickerman', + imageUrl: 'https://f4.bcbits.com/img/a3781170234_16.jpg', + genre: 'experimental', + artist: { + name: 'Crepuscular Rituals', + url: 'https://gatesofhypnos.bandcamp.com' + }, + featuredTrack: { + name: 'Burning the Wickerman', + streamUrl: 'https://t4.bcbits.com/stream/21667d2f301ba38714ddcde59c678575/mp3-128/3857896313?p=0&ts=1686495008&t=b4993545298376474546200779b7c5aa0c6c670e&token=1686495008_6f5e32f90224cd147928da7a3ec6d17614455932' + } + }, + { + type: 'album', + name: 'pianissimo etc', + url: 'https://triptickstapes.bandcamp.com/album/pianissimo-etc', + imageUrl: 'https://f4.bcbits.com/img/a2549247670_16.jpg', + genre: 'experimental', + artist: { + name: 'Carlo Costa & John McCowen', + url: 'https://triptickstapes.bandcamp.com' + }, + featuredTrack: { + name: 'I', + streamUrl: 'https://t4.bcbits.com/stream/6a5f246e56e0744a2fbe972c97c66d65/mp3-128/1619972177?p=0&ts=1686495008&t=2584524796de2413d7ba4b3616f254dba0f22ffb&token=1686495008_c26b3fd28e136c8f9f3990a6de94cdc18220f1bc' + } + } + ] + }, + { + name: 'trending_vinyl', + title: 'featured noise vinyl', + items: [ + { + type: 'album', + name: 'Live At The Piper', + url: 'https://thee-alcoholics.bandcamp.com/album/live-at-the-piper', + imageUrl: 'https://f4.bcbits.com/img/a917724657_16.jpg', + genre: 'alternative', + artist: { + name: 'Thee Alcoholics', + url: 'https://thee-alcoholics.bandcamp.com' + }, + featuredTrack: { + name: 'Power (Live)', + streamUrl: 'https://t4.bcbits.com/stream/8cca9602350e17e5d413d52c27236b7c/mp3-128/1894712039?p=0&ts=1686495008&t=e75d4564a8fd9c244323f1d2fb73d7900d14f2f4&token=1686495008_1ec98a48403c0cbda089be454cfded20fcc76520' + } + }, + { + type: 'album', + name: 'Blurry Landscapes', + url: 'https://lajungle.bandcamp.com/album/blurry-landscapes', + imageUrl: 'https://f4.bcbits.com/img/a3508479022_16.jpg', + genre: 'alternative', + artist: { name: 'LA JUNGLE', url: 'https://lajungle.bandcamp.com' }, + featuredTrack: { + name: 'La Compagnie De La Chanson', + streamUrl: 'https://t4.bcbits.com/stream/33842feaca9b263627517e2dbe8c2483/mp3-128/453427226?p=0&ts=1686495008&t=1b5e7ce660d9069f0c8f72a0c493130b277f3364&token=1686495008_adc43eea6c0b0c1a2e551d811bd40877e1e480e0' + } + }, + { + type: 'album', + name: 'Rabble EP', + url: 'https://an-i.bandcamp.com/album/rabble-ep', + imageUrl: 'https://f4.bcbits.com/img/a992530916_16.jpg', + genre: 'electronic', + artist: { name: 'AN-i', url: 'https://an-i.bandcamp.com' }, + featuredTrack: { + name: 'Rabble', + streamUrl: 'https://t4.bcbits.com/stream/115ee7904d75252841671ae2c95ad6e2/mp3-128/2213801005?p=0&ts=1686495008&t=63e35f490612fee9accee0bc1935eb674c8c206e&token=1686495008_4c917120020952aa92423cfdfac17f6e2623ef4c' + } + }, + { + type: 'album', + name: 'ꆀꃅꆈꌠꉙꇁꑵꃚ ꂱꆹꊨꌠ:ꉼꉹ 涼山彝族音樂 第一輯:口弦 Nuosu Music from Liangshan Vol. 1: Mouth Harp', + url: 'https://wvsorcerer.bandcamp.com/album/nuosu-music-from-liangshan-vol-1-mouth-harp', + imageUrl: 'https://f4.bcbits.com/img/a733119132_16.jpg', + genre: 'experimental', + artist: { + name: 'Various Artists', + url: 'https://wvsorcerer.bandcamp.com' + }, + featuredTrack: { + name: 'ꉼꊇꀉꐡꐥ 殘缺的土墻 Cob Wall In Debris', + streamUrl: 'https://t4.bcbits.com/stream/96c85e85d66a0e68c418c914919e514f/mp3-128/1058799769?p=0&ts=1686495008&t=dbd4d1d4a2512c1cebe5d890037bffc166f11549&token=1686495008_3fb809e474ded44ab363c1477c06c4c781eabb61' + } + }, + { + type: 'album', + name: 'Crushed Mealworm Ambience', + url: 'https://mridge.bandcamp.com/album/crushed-mealworm-ambience', + imageUrl: 'https://f4.bcbits.com/img/a4211637722_16.jpg', + genre: 'experimental', + artist: { name: 'Michael Ridge', url: 'https://mridge.bandcamp.com' }, + featuredTrack: { + name: 'Untitled', + streamUrl: 'https://t4.bcbits.com/stream/2355bac71ef78ee172717bec649a5269/mp3-128/3962435025?p=0&ts=1686495008&t=2f2625b04229cec8f872c9098bf9ecc2efe17c79&token=1686495008_8a048435a51e201cbf5de607da1ffca94ab05742' + } + }, + { + type: 'album', + name: 'Kusōzu : Nine Death Stages', + url: 'https://anarchiveslabel.bandcamp.com/album/kus-zu-nine-death-stages', + imageUrl: 'https://f4.bcbits.com/img/a2890249893_16.jpg', + genre: 'alternative', + artist: { + name: 'Archeus', + url: 'https://anarchiveslabel.bandcamp.com' + }, + featuredTrack: { + name: '膿爛相 nō ran sō', + streamUrl: 'https://t4.bcbits.com/stream/bf7bd1e7af5485328454f25bdaf90b23/mp3-128/728714797?p=0&ts=1686495008&t=50c347b0d1d38a330441b30a69e116a659499953&token=1686495008_f815c7d2ba3031b9e5707e26360396641ea6c775' + } + }, + { + type: 'album', + name: 'Repo', + url: 'https://urashima.bandcamp.com/album/repo', + imageUrl: 'https://f4.bcbits.com/img/a1877611708_16.jpg', + genre: 'electronic', + artist: { name: 'Incapacitants', url: 'https://urashima.bandcamp.com' }, + featuredTrack: { + name: 'Repo', + streamUrl: 'https://t4.bcbits.com/stream/048833435ae9ae887c5e21a375adf808/mp3-128/3271057889?p=0&ts=1686495008&t=8a7d80dd9bfa98f3ea3151fd28a626cd42b9eda5&token=1686495008_ad835c7ca993646b4b79a7a812feaeebf98d007a' + } + }, + { + type: 'album', + name: 'GREYMOUTH "Parked Up"', + url: 'https://sophomorelounge.bandcamp.com/album/greymouth-parked-up', + imageUrl: 'https://f4.bcbits.com/img/a3357958328_16.jpg', + genre: 'rock', + artist: { + name: 'Greymouth', + url: 'https://sophomorelounge.bandcamp.com' + }, + featuredTrack: { + name: 'Side B', + streamUrl: 'https://t4.bcbits.com/stream/6a0e82920f838d1fbaae29e0405fbfca/mp3-128/1806484851?p=0&ts=1686495008&t=b16434a38005a840515bbf5ac7d0427e2096adcf&token=1686495008_67d7c008c511b7e6545e11d436bc567fcb458994' + } + }, + { + type: 'album', + name: 'Vestal Spacy Ritual', + url: 'https://urashima.bandcamp.com/album/vestal-spacy-ritual', + imageUrl: 'https://f4.bcbits.com/img/a535131431_16.jpg', + genre: 'electronic', + artist: { name: 'Masonna', url: 'https://urashima.bandcamp.com' }, + featuredTrack: { + name: 'Untitled', + streamUrl: 'https://t4.bcbits.com/stream/c539b84c2647f0ba238588983d10e183/mp3-128/1817128543?p=0&ts=1686495008&t=921c41f817fbeaee90fe1729469dac3d63b0a3b6&token=1686495008_04c688755ebd1dc83e6d8e48185c74ea2718814a' + } + }, + { + type: 'album', + name: 'Space Metalizer', + url: 'https://urashima.bandcamp.com/album/space-metalizer', + imageUrl: 'https://f4.bcbits.com/img/a1286867520_16.jpg', + genre: 'electronic', + artist: { name: 'Merzbow', url: 'https://urashima.bandcamp.com' }, + featuredTrack: { + name: 'Space Metalizer Pt. 1', + streamUrl: 'https://t4.bcbits.com/stream/b364e21f9e6fd6a1279e6b227752af5b/mp3-128/3625280356?p=0&ts=1686495008&t=b5df5415d0626d02fb74d8f09d3d12d0a3c58c98&token=1686495008_2eef5fb17691852745d03d3726fcda431e29ec7d' + } + }, + { + type: 'album', + name: 'Dry Tears EP', + url: 'https://brutoindustries.bandcamp.com/album/dry-tears-ep', + imageUrl: 'https://f4.bcbits.com/img/a3103869468_16.jpg', + genre: 'electronic', + artist: { name: 'xKukum', url: 'https://brutoindustries.bandcamp.com' }, + featuredTrack: { + name: 'Dry Tears', + streamUrl: 'https://t4.bcbits.com/stream/d75c41937e8e0362549a8c000110d1ae/mp3-128/2336534978?p=0&ts=1686495008&t=2ac6182b6403ecde866190d46f90875290aa69a2&token=1686495008_5ff23cb7f111cf1adf124b161dcb7234077a54cf' + } + }, + { + type: 'album', + name: 'ANTIMATERIA', + url: 'https://grubenwehrfreiburg.bandcamp.com/album/antimateria', + imageUrl: 'https://f4.bcbits.com/img/a482994477_16.jpg', + genre: 'experimental', + artist: { + name: 'M.B. / Maurizio Bianchi and Coalminer + Allies', + url: 'https://grubenwehrfreiburg.bandcamp.com' + }, + featuredTrack: { + name: 'DISSECTION by Coalminer feat. Torturing Nurse and Grodock', + streamUrl: 'https://t4.bcbits.com/stream/3912be956a87f1e1956625655732d833/mp3-128/934016549?p=0&ts=1686495008&t=2f924b3572e24ea26fad3c259f4c65180e5d9728&token=1686495008_216d139146b33d4a6bebd0275b48049c5b04fdc2' + } + }, + { + type: 'album', + name: 'Electric Rag Live', + url: 'https://willguthrie.bandcamp.com/album/electric-rag-live', + imageUrl: 'https://f4.bcbits.com/img/a3007052395_16.jpg', + genre: 'experimental', + artist: { + name: 'Jean-Luc Guionnet & Will Guthrie', + url: 'https://willguthrie.bandcamp.com' + }, + featuredTrack: { + name: 'A2', + streamUrl: 'https://t4.bcbits.com/stream/79d0de7450566da0d2bdfda3fe51423e/mp3-128/1252401047?p=0&ts=1686495008&t=d385e9ba8bd37946df3398edbf31fe2b121307b8&token=1686495008_7d96ae1d95d7e4db532baab50ba89c9026c954ab' + } + }, + { + type: 'album', + name: 'Split', + url: 'https://order05records.bandcamp.com/album/split', + imageUrl: 'https://f4.bcbits.com/img/a2649102963_16.jpg', + genre: 'experimental', + artist: { + name: 'Nag / Astio', + url: 'https://order05records.bandcamp.com' + }, + featuredTrack: { + name: 'Passing', + streamUrl: 'https://t4.bcbits.com/stream/46acf007afba291abe370e22bbb33e07/mp3-128/3059182561?p=0&ts=1686495008&t=395cd400b4c5ddd683c8839777a7b0e7fe97c4cd&token=1686495008_8495699ee76f70f71c5bfd9c3a314bec1863a7a8' + } + }, + { + type: 'album', + name: 'Losing Circles', + url: 'https://marciabassettandthomasdimuzio.bandcamp.com/album/losing-circles', + imageUrl: 'https://f4.bcbits.com/img/a3311034560_16.jpg', + genre: 'electronic', + artist: { + name: 'Marcia Bassett and Thomas Dimuzio', + url: 'https://marciabassettandthomasdimuzio.bandcamp.com' + }, + featuredTrack: { + name: 'Admonition', + streamUrl: 'https://t4.bcbits.com/stream/8d36803958ef01284ba926cd74da7342/mp3-128/4277096877?p=0&ts=1686495008&t=61bdc18b6a26ea6b817749f67d40c633a8e8a43e&token=1686495008_0eef590917d2c514422ccd4f347e10adee9edaf8' + } + } + ] + }, + { + name: 'top_sellers', + title: 'all-time best selling noise', + items: [ + { + type: 'album', + name: 'SIGNALIS (ORIGINAL SOUNDTRACK)', + url: 'https://signalis-ost.bandcamp.com/album/signalis-original-soundtrack', + imageUrl: 'https://f4.bcbits.com/img/a510125997_16.jpg', + genre: 'soundtrack', + artist: { + name: 'Cicada Sirens & 1000 Eyes', + url: 'https://signalis-ost.bandcamp.com' + }, + featuredTrack: { + name: 'Die Toteninsel (Emptiness)', + streamUrl: 'https://t4.bcbits.com/stream/b162b3188ab9b5dc48704b0515895062/mp3-128/3392523895?p=0&ts=1686495008&t=ceffd2977ceea9c0b30efcfdc697f82313570941&token=1686495008_382a1e70fec4ff2ffaa3327688ff2995d1e82511' + } + }, + { + type: 'album', + name: 'Sounds Of France | France Sound Effects Library', + url: 'https://freetousesounds.bandcamp.com/album/sounds-of-france-france-sound-effects-library', + imageUrl: 'https://f4.bcbits.com/img/a2198695097_16.jpg', + genre: 'ambient', + artist: { + name: 'freetousesounds', + url: 'https://freetousesounds.bandcamp.com' + }, + featuredTrack: { + name: 'Sound Compilation Sounds Of France', + streamUrl: 'https://t4.bcbits.com/stream/c6ea66c07c69bff1b0aa0a8b92439ec4/mp3-128/2634037464?p=0&ts=1686495008&t=a288a3feb4e86abb223636bf337e80635f16f72b&token=1686495008_c7cf6251bb6294311b9179cb5c361cb8a8152175' + } + }, + { + type: 'album', + name: 'And In The Darkness, Hearts Aglow', + url: 'https://weyesblood.bandcamp.com/album/and-in-the-darkness-hearts-aglow', + imageUrl: 'https://f4.bcbits.com/img/a4032822447_16.jpg', + genre: 'alternative', + artist: { name: 'Weyes Blood', url: 'https://weyesblood.bandcamp.com' }, + featuredTrack: { + name: "It's Not Just Me, It's Everybody", + streamUrl: 'https://t4.bcbits.com/stream/2f04b711fe9c4a1052a6b54588510681/mp3-128/1086774697?p=0&ts=1686495008&t=e13cf12684829c9423544acfc34236860c014872&token=1686495008_50b16312494b015ff8b328a12a73a22ceeef86a0' + } + }, + { + type: 'album', + name: 'Peaceful as Hell', + url: 'https://blackdresses.bandcamp.com/album/peaceful-as-hell', + imageUrl: 'https://f4.bcbits.com/img/a3575771155_16.jpg', + genre: 'electronic', + artist: { + name: 'Black Dresses', + url: 'https://blackdresses.bandcamp.com' + }, + featuredTrack: { + name: 'BEAUTIFUL FRIENDSHIP', + streamUrl: 'https://t4.bcbits.com/stream/8d5064f6c97778093c531428ca3efa06/mp3-128/1285558574?p=0&ts=1686495008&t=e872033a3bfb777d171d6634430d6c0b2f78ea7e&token=1686495008_e19dba7830f7854d842d1c89015b7598a7981f4b' + } + }, + { + type: 'album', + name: '1995', + url: 'https://music.businesscasual.biz/album/1995', + imageUrl: 'https://f4.bcbits.com/img/a1681009058_16.jpg', + genre: 'electronic', + artist: { name: '☒', url: 'https://music.businesscasual.biz' }, + featuredTrack: { + name: 'Moon Way (feat. Chemical Hypnotist)', + streamUrl: 'https://t4.bcbits.com/stream/f796fd140e5e99346511e207225800f3/mp3-128/2165960685?p=0&ts=1686495008&t=6f1ec3fbd05dd8bb197f7ae37bb01200d3fa7da8&token=1686495008_4e1833b16a9cee37938a240d974e78ee2289ba0a' + } + }, + { + type: 'album', + name: 'fade', + url: 'https://boris.bandcamp.com/album/fade', + imageUrl: 'https://f4.bcbits.com/img/a3826169507_16.jpg', + genre: 'rock', + artist: { name: 'Boris', url: 'https://boris.bandcamp.com' }, + featuredTrack: { + name: '序章 三叉路', + streamUrl: 'https://t4.bcbits.com/stream/e514de403deeb9a8d3fc7c35a6eb8585/mp3-128/1793702739?p=0&ts=1686495008&t=70802532e633ed278724af8894545ca63aaa39e0&token=1686495008_fcd5a3dd9e30466c1a6ac95431d3b3663873e09d' + } + }, + { + type: 'album', + name: 'Dogsbody', + url: 'https://modelactriz.bandcamp.com/album/dogsbody', + imageUrl: 'https://f4.bcbits.com/img/a2228512258_16.jpg', + genre: 'experimental', + artist: { + name: 'Model/Actriz', + url: 'https://modelactriz.bandcamp.com' + }, + featuredTrack: { + name: 'Crossing Guard', + streamUrl: 'https://t4.bcbits.com/stream/7ebb0626b3b039f570dc4cbd4062771b/mp3-128/409743092?p=0&ts=1686495008&t=940029d35e2859b3b7c52f403e27912507033995&token=1686495008_07db560557b5bec929319b09e05a5333e21af5a0' + } + }, + { + type: 'album', + name: 'Machine 1', + url: 'https://thebugmusic.bandcamp.com/album/machine-1', + imageUrl: 'https://f4.bcbits.com/img/a477685434_16.jpg', + genre: 'experimental', + artist: { name: 'The Bug', url: 'https://thebugmusic.bandcamp.com' }, + featuredTrack: { + name: 'Annihilated(Force of Gravity)', + streamUrl: 'https://t4.bcbits.com/stream/d188a9d1beb2bbc19f27705f1cdf1fc2/mp3-128/3300393060?p=0&ts=1686495008&t=e981935170b834aa4304cc5c72093648d79ccfe4&token=1686495008_212347b4b1330d8d871add3002dfc081eeb1f0f6' + } + }, + { + type: 'album', + name: 'Jazz Codes', + url: 'https://moormother.bandcamp.com/album/jazz-codes', + imageUrl: 'https://f4.bcbits.com/img/a4226674197_16.jpg', + genre: 'experimental', + artist: { name: 'Moor Mother', url: 'https://moormother.bandcamp.com' }, + featuredTrack: { + name: 'RAP JASM (feat. AKAI SOLO & Justmadnice)', + streamUrl: 'https://t4.bcbits.com/stream/c2484d9cde26beb51c3aede857e806df/mp3-128/2796877322?p=0&ts=1686495008&t=b9a1e75825b42f24d10a5de4587162bd2cd1ff23&token=1686495008_baa3931b51e5c3010b5c64ca77127aa6fd5100cc' + } + }, + { + type: 'album', + name: 'believe anything, believe everything', + url: 'https://jkflesh.bandcamp.com/album/believe-anything-believe-everything', + imageUrl: 'https://f4.bcbits.com/img/a242932256_16.jpg', + genre: 'electronic', + artist: { + name: 'EXIT ELECTRONICS', + url: 'https://jkflesh.bandcamp.com' + }, + featuredTrack: { + name: 'YOUR LOT', + streamUrl: 'https://t4.bcbits.com/stream/c685b70a3091efb212ee97b3face69ad/mp3-128/2577528116?p=0&ts=1686495008&t=b2b0855771598228e014a78789d171391d9e5a91&token=1686495008_e64d1f5b447d96a4ab1ab03b226ac3137e394ef4' + } + }, + { + type: 'album', + name: 'The Way Through All Things (SG2396)', + url: 'https://orphx.bandcamp.com/album/the-way-through-all-things-sg2396', + imageUrl: 'https://f4.bcbits.com/img/a396548847_16.jpg', + genre: 'electronic', + artist: { name: 'Orphx', url: 'https://orphx.bandcamp.com' }, + featuredTrack: { + name: 'Man Of Sorrows', + streamUrl: 'https://t4.bcbits.com/stream/4c7a02cbcda19f040eed58585e9e2ab0/mp3-128/3018624612?p=0&ts=1686495008&t=f3eabbed4f2ef4e1e975895392a85530537733d7&token=1686495008_76371ade3a38be43801b67acb084ba5e410c610c' + } + }, + { + type: 'album', + name: 'Losing Grip on Reality', + url: 'https://geometriclullaby.bandcamp.com/album/losing-grip-on-reality', + imageUrl: 'https://f4.bcbits.com/img/a1041180441_16.jpg', + genre: 'electronic', + artist: { + name: 'Fiebertraum', + url: 'https://geometriclullaby.bandcamp.com' + }, + featuredTrack: { + name: 'Dancing in the Crypt of Sorrow', + streamUrl: 'https://t4.bcbits.com/stream/49264f911c3f9b2841b2d852e820a053/mp3-128/4271360547?p=0&ts=1686495008&t=f89a148681a4dc77a859a85eec5f7a59145712d9&token=1686495008_6c88c261a07a128456069a35f3276a33033e00c4' + } + }, + { + type: 'album', + name: 'leni spiritu ambulat ad lucem', + url: 'https://blankskies.com/album/leni-spiritu-ambulat-ad-lucem', + imageUrl: 'https://f4.bcbits.com/img/a1330779105_16.jpg', + genre: 'electronic', + artist: { name: 'Ghost On This Earth', url: 'https://blankskies.com' }, + featuredTrack: { + name: 'leni spiritu ambulat ad lucem Part I', + streamUrl: 'https://t4.bcbits.com/stream/50f026646920033d4d208215f3c8532a/mp3-128/1397038943?p=0&ts=1686495008&t=441a4dad796a607862ea79c611fb2e011db50929&token=1686495008_13526870d33a69d95480388a3fa45fa1c4a6bacc' + } + }, + { + type: 'album', + name: 'metempsychosis', + url: 'https://i-am-monodrone.bandcamp.com/album/metempsychosis', + imageUrl: 'https://f4.bcbits.com/img/a3095954567_16.jpg', + genre: 'experimental', + artist: { + name: 'Monodrone', + url: 'https://i-am-monodrone.bandcamp.com' + }, + featuredTrack: { + name: 'consciousness in an unfamiliar place', + streamUrl: 'https://t4.bcbits.com/stream/3c64b3567dd5c929adf29040ef4d0b36/mp3-128/115066593?p=0&ts=1686495008&t=a93d04335c0d6c67d8f55584f617de58d5b78b05&token=1686495008_84efd2b1e9f896d8d4390abeed2e1f40a5c71fc4' + } + }, + { + type: 'album', + name: 'artless', + url: 'https://ifidieinmississippi.bandcamp.com/album/artless-2', + imageUrl: 'https://f4.bcbits.com/img/a3831177236_16.jpg', + genre: 'acoustic', + artist: { + name: 'if i die in mississippi', + url: 'https://ifidieinmississippi.bandcamp.com' + }, + featuredTrack: { + name: 'goth kids by the carousel in the mall', + streamUrl: 'https://t4.bcbits.com/stream/340a5b2cd0489fedc8e3754d403a4b7f/mp3-128/1081232611?p=0&ts=1686495008&t=4b245e124029212d6ad6b2c7721ce922c0a09ae1&token=1686495008_e237fbd3ba9108a35089792805494b18e5db3fae' + } + }, + { + type: 'album', + name: 'Femina Furens', + url: 'https://djunah.bandcamp.com/album/femina-furens', + imageUrl: 'https://f4.bcbits.com/img/a3751537011_16.jpg', + genre: 'rock', + artist: { name: 'Djunah', url: 'https://djunah.bandcamp.com' }, + featuredTrack: { + name: 'Grooming', + streamUrl: 'https://t4.bcbits.com/stream/60d8617ace7b97fa6cfd95a2ab1078d6/mp3-128/2580329716?p=0&ts=1686495008&t=087572b03a4bad145486470d2ff91ef6c40b3e09&token=1686495008_a16e4798c1b6fe1cba28a340327b1cc3f047835d' + } + } + ] + }, + { + name: 'fan_reviews', + title: 'recommendations from fans', + items: [ + { + type: 'album', + name: 'Krun Macula', + url: 'https://jutegyte.bandcamp.com/album/krun-macula', + imageUrl: 'https://f4.bcbits.com/img/a1890193785_16.jpg', + genre: 'metal', + artist: { name: 'Jute Gyte', url: 'https://jutegyte.bandcamp.com' }, + featuredTrack: { + name: 'Dionysus Fluctus', + streamUrl: 'https://t4.bcbits.com/stream/3cd6d59e58ccbcc598e6b4796740f5ab/mp3-128/2644412281?p=0&ts=1686495009&t=19a1f348a7ed6e2c412c31c72acdd0647b4b681d&token=1686495009_616c9c883e348bfad9763da3cc15d4b638656480' + } + }, + { + type: 'album', + name: 'DISSECTING A ONE-WINGED BIRD', + url: 'https://ackodband.bandcamp.com/album/dissecting-a-one-winged-bird', + imageUrl: 'https://f4.bcbits.com/img/a2253539932_16.jpg', + genre: 'metal', + artist: { + name: 'A Constant Knowledge of Death', + url: 'https://ackodband.bandcamp.com' + }, + featuredTrack: { + name: 'THE HALLOWED CASTRATION OF THRONE', + streamUrl: 'https://t4.bcbits.com/stream/ee1edbaf27e012fefa55c07338cf3785/mp3-128/3465355863?p=0&ts=1686495009&t=290135a82a19b2658276a813e3f46e7d98051a98&token=1686495009_8154754d2fa9b1b52da394908829675ed06a7456' + } + }, + { + type: 'album', + name: 'Satan', + url: 'https://fortevilfruit.bandcamp.com/album/satan', + imageUrl: 'https://f4.bcbits.com/img/a2058438844_16.jpg', + genre: 'experimental', + artist: { + name: 'Golem Mecanique', + url: 'https://fortevilfruit.bandcamp.com' + }, + featuredTrack: { + name: 'The False Prophet', + streamUrl: 'https://t4.bcbits.com/stream/a21eeb119659148b9824446549925b93/mp3-128/4274107030?p=0&ts=1686495009&t=78dca823bf95fa1a21fb807d2447fd59281cff8b&token=1686495009_0b75c6e04a107d9585c44629b21db6924a041ee3' + } + }, + { + type: 'album', + name: 'Hatomatsuri', + url: 'https://dinzuartefacts.bandcamp.com/album/hatomatsuri', + imageUrl: 'https://f4.bcbits.com/img/a1179620564_16.jpg', + genre: 'experimental', + artist: { name: 'Merzbow', url: 'https://dinzuartefacts.bandcamp.com' }, + featuredTrack: { + name: 'Part I', + streamUrl: 'https://t4.bcbits.com/stream/babeaf5c814e80d824bc3118a5548ca1/mp3-128/2337588048?p=0&ts=1686495009&t=613589047d7f4f61b17b5351fc5f3bed8c646a88&token=1686495009_ca42317372b09896c409877c054a1b6add409115' + } + }, + { + type: 'album', + name: "A Worker's Guide To Transfiguration", + url: 'https://strangemono.bandcamp.com/album/a-workers-guide-to-transfiguration', + imageUrl: 'https://f4.bcbits.com/img/a2029116366_16.jpg', + genre: 'punk', + artist: { name: 'Truculent', url: 'https://strangemono.bandcamp.com' }, + featuredTrack: { + name: 'You In The Depths Of Heaven, An Eagle Of Power, Behold!', + streamUrl: 'https://t4.bcbits.com/stream/22e79d0efb0d9aacdc0910fa79a1ad4b/mp3-128/3588905207?p=0&ts=1686495009&t=025d049d8f5475d814bc1686a0d95d58df91e840&token=1686495009_702b004de01ad1fd4c69b1e5b06f37f9a816bf83' + } + }, + { + type: 'album', + name: 'Dreams In Splattered Lines', + url: 'https://wolf-eyes.bandcamp.com/album/dreams-in-splattered-lines', + imageUrl: 'https://f4.bcbits.com/img/a809163437_16.jpg', + genre: 'electronic', + artist: { name: 'Wolf Eyes', url: 'https://wolf-eyes.bandcamp.com' }, + featuredTrack: { + name: 'Car Wash Two w/ Short Hands', + streamUrl: 'https://t4.bcbits.com/stream/f4b94de29064a8e529daefc4c8b1b796/mp3-128/4065081837?p=0&ts=1686495009&t=ce0917608a47adb74957f32a188ab700dc922c61&token=1686495009_0a360d45ae5a4839fa708edf500ff411cbc44723' + } + }, + { + type: 'album', + name: 'Snacks Compilation', + url: 'https://clublatemusic.bandcamp.com/album/snacks-compilation', + imageUrl: 'https://f4.bcbits.com/img/a439746030_16.jpg', + genre: 'experimental', + artist: { + name: 'CLM x GUN', + url: 'https://clublatemusic.bandcamp.com' + }, + featuredTrack: { + name: 'Rebirth', + streamUrl: 'https://t4.bcbits.com/stream/632452c0e7d30a3d60765caa01414fe7/mp3-128/2470676124?p=0&ts=1686495009&t=472df945d8900e1e85f4dfde78d8a41def9dbc96&token=1686495009_a9a6c81b0f5705e1646f071b4bb5fcf65b9f8dbc' + } + }, + { + type: 'album', + name: 'Shrouded', + url: 'https://everydaydust.bandcamp.com/album/shrouded', + imageUrl: 'https://f4.bcbits.com/img/a1683608029_16.jpg', + genre: 'ambient', + artist: { + name: 'Everyday Dust', + url: 'https://everydaydust.bandcamp.com' + }, + featuredTrack: { + name: 'Broken Wings', + streamUrl: 'https://t4.bcbits.com/stream/80b8681b029fad1a0b830b4216fed2cb/mp3-128/2012156557?p=0&ts=1686495009&t=e85022f62b2f30741a0127050e3bf1ff96e1d9f4&token=1686495009_089cc4f25c92577fd34f5cf4fb66391aedf44aa2' + } + }, + { + type: 'album', + name: 'Ambient Works to Dissociate to, Vol. 2', + url: 'https://blankskies.com/album/ambient-works-to-dissociate-to-vol-2', + imageUrl: 'https://f4.bcbits.com/img/a1234048215_16.jpg', + genre: 'electronic', + artist: { name: 'Ernest Strauhal', url: 'https://blankskies.com' }, + featuredTrack: { + name: 'Intro', + streamUrl: 'https://t4.bcbits.com/stream/62f8936aee5b6ed3cb26e1808a5fff0e/mp3-128/545634706?p=0&ts=1686495009&t=7a09df1dcdc58bce06f1d8356e9b4ec42991c1cd&token=1686495009_639661c5d6297786cb68e5c0e1cbf4cf081d0541' + } + } + ] + } +] diff --git a/examples/tag/getInfo.ts b/examples/tag/getInfo.ts new file mode 100644 index 0000000..082ad53 --- /dev/null +++ b/examples/tag/getInfo.ts @@ -0,0 +1,8 @@ +import bcfetch from '../../'; +import util from 'util'; + +const tagUrl = 'https://bandcamp.com/tag/dark-ambient'; + +bcfetch.tag.getInfo(tagUrl).then((results) => { + console.log(util.inspect(results, false, null, false)); +}); diff --git a/examples/tag/getInfo_output.txt b/examples/tag/getInfo_output.txt new file mode 100644 index 0000000..1d6fb00 --- /dev/null +++ b/examples/tag/getInfo_output.txt @@ -0,0 +1,113 @@ +{ + type: 'tag', + name: 'dark ambient', + url: 'https://bandcamp.com/tag/dark-ambient', + value: 'dark-ambient', + related: [ + { + type: 'tag', + name: 'dungeon synth', + url: 'https://bandcamp.com/tag/dungeon-synth', + value: 'dungeon-synth', + isLocation: false + }, + { + type: 'tag', + name: 'drone ambient', + url: 'https://bandcamp.com/tag/drone-ambient', + value: 'drone-ambient', + isLocation: false + }, + { + type: 'tag', + name: 'drone', + url: 'https://bandcamp.com/tag/drone', + value: 'drone', + isLocation: false + }, + { + type: 'tag', + name: 'atmospheric', + url: 'https://bandcamp.com/tag/atmospheric', + value: 'atmospheric', + isLocation: false + }, + { + type: 'tag', + name: 'field recordings', + url: 'https://bandcamp.com/tag/field-recordings', + value: 'field-recordings', + isLocation: false + }, + { + type: 'tag', + name: 'soundscape', + url: 'https://bandcamp.com/tag/soundscape', + value: 'soundscape', + isLocation: false + }, + { + type: 'tag', + name: 'ambient', + url: 'https://bandcamp.com/tag/ambient', + value: 'ambient', + isLocation: false + }, + { + type: 'tag', + name: 'harsh noise', + url: 'https://bandcamp.com/tag/harsh-noise', + value: 'harsh-noise', + isLocation: false + }, + { + type: 'tag', + name: 'industrial', + url: 'https://bandcamp.com/tag/industrial', + value: 'industrial', + isLocation: false + }, + { + type: 'tag', + name: 'ambient electronic', + url: 'https://bandcamp.com/tag/ambient-electronic', + value: 'ambient-electronic', + isLocation: false + }, + { + type: 'tag', + name: 'noise', + url: 'https://bandcamp.com/tag/noise', + value: 'noise', + isLocation: false + }, + { + type: 'tag', + name: 'experimental electronic', + url: 'https://bandcamp.com/tag/experimental-electronic', + value: 'experimental-electronic', + isLocation: false + }, + { + type: 'tag', + name: 'soundtrack', + url: 'https://bandcamp.com/tag/soundtrack', + value: 'soundtrack', + isLocation: false + }, + { + type: 'tag', + name: 'black metal', + url: 'https://bandcamp.com/tag/black-metal', + value: 'black-metal', + isLocation: false + }, + { + type: 'tag', + name: 'Russia', + url: 'https://bandcamp.com/tag/russia', + value: 'russia', + isLocation: true + } + ] +} diff --git a/examples/tag/getReleases.ts b/examples/tag/getReleases.ts new file mode 100644 index 0000000..d97e8a5 --- /dev/null +++ b/examples/tag/getReleases.ts @@ -0,0 +1,19 @@ +import bcfetch from '../../'; +import util from 'util'; + +const tagUrl = 'https://bandcamp.com/tag/dark-ambient'; + +const params = { + tagUrl, + filters: { + tags: [ 'electronica' ], + sort: 'random' + }, + page: 2, + imageFormat: 2, + useHardcodedDefaultFilters: true +}; + +bcfetch.tag.getReleases(params).then((results) => { + console.log(util.inspect(results, false, null, false)); +}); diff --git a/examples/tag/getReleasesAvailableFilters.ts b/examples/tag/getReleasesAvailableFilters.ts new file mode 100644 index 0000000..74d439c --- /dev/null +++ b/examples/tag/getReleasesAvailableFilters.ts @@ -0,0 +1,8 @@ +import bcfetch from '../../'; +import util from 'util'; + +const tagUrl = 'https://bandcamp.com/tag/dark-ambient'; + +bcfetch.tag.getReleasesAvailableFilters(tagUrl).then((results) => { + console.log(util.inspect(results, false, null, false)); +}); diff --git a/examples/tag/getReleasesAvailableFilters_output.txt b/examples/tag/getReleasesAvailableFilters_output.txt new file mode 100644 index 0000000..91f9aa9 --- /dev/null +++ b/examples/tag/getReleasesAvailableFilters_output.txt @@ -0,0 +1,74 @@ +[ + { + name: 'sort', + options: [ + { + name: 'best-selling', + value: 'pop', + selected: true, + default: true + }, + { name: 'new arrivals', value: 'date' }, + { name: 'surprise me!', value: 'random' } + ] + }, + { + name: 'format', + options: [ + { + name: 'any format', + value: 'all', + selected: true, + default: true + }, + { name: 'vinyl', value: 'vinyl' }, + { name: 'compact disc', value: 'cd' }, + { name: 'cassette', value: 'cassette' } + ] + }, + { + name: 'tags', + options: [ { name: 'dark ambient', value: 'dark-ambient', selected: true } ] + }, + { + name: 'location', + options: [ + { name: 'any location', value: 0, selected: true, default: true }, + { name: 'amsterdam', value: 2759794 }, + { name: 'atlanta', value: 4180439 }, + { name: 'austin', value: 4671654 }, + { name: 'baltimore', value: 4347778 }, + { name: 'berlin', value: 2950159 }, + { name: 'boston', value: 4930956 }, + { name: 'brooklyn', value: 5110302 }, + { name: 'buenos aires', value: 3435907 }, + { name: 'chicago', value: 4887398 }, + { name: 'denver', value: 5419384 }, + { name: 'detroit', value: 4990729 }, + { name: 'dublin', value: 2964574 }, + { name: 'glasgow', value: 3333231 }, + { name: 'london', value: 2643743 }, + { name: 'los angeles', value: 5368361 }, + { name: 'madrid', value: 3117735 }, + { name: 'manchester', value: 2643123 }, + { name: 'melbourne', value: 2158177 }, + { name: 'mexico city', value: 3530597 }, + { name: 'miami', value: 4164138 }, + { name: 'minneapolis', value: 5037649 }, + { name: 'montreal', value: 6077243 }, + { name: 'nashville', value: 4644585 }, + { name: 'new orleans', value: 4335045 }, + { name: 'new york city', value: 5128581 }, + { name: 'oakland', value: 5378538 }, + { name: 'paris', value: 2988507 }, + { name: 'philadelphia', value: 4560349 }, + { name: 'portland', value: 5746545 }, + { name: 'san francisco', value: 5391959 }, + { name: 'seattle', value: 5809844 }, + { name: 'sydney', value: 2147714 }, + { name: 'toronto', value: 6167865 }, + { name: 'vancouver', value: 6173331 }, + { name: 'washington, dc', value: 4140963 } + ] + } +] diff --git a/examples/tag/getReleases_output.txt b/examples/tag/getReleases_output.txt new file mode 100644 index 0000000..97ee28a --- /dev/null +++ b/examples/tag/getReleases_output.txt @@ -0,0 +1,301 @@ +{ + items: [ + { + type: 'album', + name: 'Secret Sessions [24bit] (SBKWDIGI012)', + url: 'https://separatedbeats.bandcamp.com/album/secret-sessions-24bit-sbkwdigi012', + imageUrl: 'https://f4.bcbits.com/img/a4005533125_2.jpg', + genre: 'ambient', + artist: { name: '70db', url: 'https://separatedbeats.bandcamp.com' }, + featuredTrack: { + name: 'Ormolu', + position: 9, + streamUrl: 'https://t4.bcbits.com/stream/26e32e1568f99343dac8107b9ed97948/mp3-128/4092905456?p=0&ts=1686495035&t=b7244425596f24d97092df6d835d8702b1fbdb30&token=1686495035_b122c2effa25e2fee2c8a7a16947b8186342c5fe' + } + }, + { + type: 'album', + name: 'Im Staub Der Rebellion', + url: 'https://subculturerecords.bandcamp.com/album/im-staub-der-rebellion', + imageUrl: 'https://f4.bcbits.com/img/a2406753869_2.jpg', + genre: 'electronic', + artist: { + name: 'Weissglut', + url: 'https://subculturerecords.bandcamp.com' + }, + featuredTrack: { + name: 'Lichtbringer', + position: 4, + streamUrl: 'https://t4.bcbits.com/stream/04fd018f2f76acf41a1204c1af176210/mp3-128/3838988849?p=0&ts=1686495035&t=a46563786880d5d8d1b048e140a03108b5bb943a&token=1686495035_1f9815be476e67c873a4d731865e311cb69df9d4' + } + }, + { + type: 'album', + name: 'TestTracks Batch 07', + url: 'https://speaker1.bandcamp.com/album/testtracks-batch-07', + imageUrl: 'https://f4.bcbits.com/img/a2304904704_2.jpg', + genre: 'electronic', + artist: { name: 'COBELL', url: 'https://speaker1.bandcamp.com' }, + featuredTrack: { + name: 'Arch Destiny V', + position: 1, + streamUrl: 'https://t4.bcbits.com/stream/9e4eaa50d6738f62db11d965d66168ab/mp3-128/2451578386?p=0&ts=1686495035&t=240ca373dce50ed10e86187cfe49ecb3ecab8d05&token=1686495035_6d8961f47ce8fac2e4f13d5449940e87132ad726' + } + }, + { + type: 'album', + name: 'Compiurakenroff. Tuaken Gousk (2015) Tuaken Fakent (2022)', + url: 'https://subculturerecords.bandcamp.com/album/compiurakenroff-tuaken-gousk-2015-tuaken-fakent-2022', + imageUrl: 'https://f4.bcbits.com/img/a3776133419_2.jpg', + genre: 'electronic', + artist: { + name: 'Oxomaxoma', + url: 'https://subculturerecords.bandcamp.com' + }, + featuredTrack: { + name: 'Shot The Gun!', + position: 5, + streamUrl: 'https://t4.bcbits.com/stream/43f557c374dfe4b1f3f645c4e9d7fac9/mp3-128/3613894633?p=0&ts=1686495035&t=1263d3e7670c82ef27ffb17e22e461f488fa9d87&token=1686495035_21ec00c949264ad35891b87b9cada7d4cbe9433b' + } + }, + { + type: 'album', + name: 'Rust', + url: 'https://blvckceiling.bandcamp.com/album/rust', + imageUrl: 'https://f4.bcbits.com/img/a1671132631_2.jpg', + genre: 'electronic', + artist: { + name: 'BLVCK CEILING', + url: 'https://blvckceiling.bandcamp.com' + }, + featuredTrack: { + name: 'You Saved Me', + position: 2, + streamUrl: 'https://t4.bcbits.com/stream/199e97ebf8a69cab13242e027e0b4e22/mp3-128/2587732288?p=0&ts=1686495035&t=e6d54538975f676b315c0d6b683d11cef8a6993b&token=1686495035_e1b7b0934711db8a59a6fe0e392ef00af6562fb7' + } + }, + { + type: 'album', + name: 'Liminal, Vol. 2', + url: 'https://darkheartrecordings.bandcamp.com/album/liminal-vol-2', + imageUrl: 'https://f4.bcbits.com/img/a2074629043_2.jpg', + genre: '', + artist: { + name: 'Space Thug', + url: 'https://darkheartrecordings.bandcamp.com' + }, + featuredTrack: { + name: 'Luxe', + position: 1, + streamUrl: 'https://t4.bcbits.com/stream/2a260b59c8f3cea1f8d6724b173be184/mp3-128/1092638435?p=0&ts=1686495035&t=2571640fd9cd77f7d6a9a3f13c1844e5dc1c0479&token=1686495035_a9ca47509ec1604eb704ab346fcb5c8f8da6d60a' + } + }, + { + type: 'album', + name: 'ARBORIA', + url: 'https://alt3r3dstat3.bandcamp.com/album/arboria', + imageUrl: 'https://f4.bcbits.com/img/a2731714976_2.jpg', + genre: 'ambient', + artist: { + name: 'ALT3R3D STAT3', + url: 'https://alt3r3dstat3.bandcamp.com' + }, + featuredTrack: { + name: 'ARBORIA', + position: 1, + streamUrl: 'https://t4.bcbits.com/stream/66769c283b3c21954d73a30df0cfe35b/mp3-128/2284698916?p=0&ts=1686495035&t=d366f509dcb5c653b74d30cf27e84a7bb5198ffb&token=1686495035_799d476599ceccef6fdce7fb1e0c96687cec2915' + } + }, + { + type: 'album', + name: 'Love Train', + url: 'https://terrycobbs.bandcamp.com/album/love-train', + imageUrl: 'https://f4.bcbits.com/img/a1199985478_2.jpg', + genre: 'ambient', + artist: { name: 'Terry Cobbs', url: 'https://terrycobbs.bandcamp.com' }, + featuredTrack: { + name: '3am in the Forest', + position: 1, + streamUrl: 'https://t4.bcbits.com/stream/565e599b8b7442ff4c932754c2105119/mp3-128/3797252849?p=0&ts=1686495035&t=cb2851596cec735da80f5c46505dc753516edfa2&token=1686495035_bf334eaa2a8adc20f8c4e1d8cf1b96998daee5ba' + } + }, + { + type: 'album', + name: 'Ett år med kemisk obalans', + url: 'https://stalverk.bandcamp.com/album/ett-r-med-kemisk-obalans', + imageUrl: 'https://f4.bcbits.com/img/a3881240208_2.jpg', + genre: 'electronic', + artist: { name: 'LO', url: 'https://stalverk.bandcamp.com' }, + featuredTrack: { + name: 'STÄMNING', + position: 1, + streamUrl: 'https://t4.bcbits.com/stream/945fbcb1f9cd1b7aff4342464a64d324/mp3-128/374943719?p=0&ts=1686495035&t=67b84c2b5a79210cc487eb67cd00dca478b7d197&token=1686495035_36f3118a4b5f92ad9460c6444d55540a88026ff2' + } + }, + { + type: 'album', + name: 'Streams', + url: 'https://autumnofcommunion.bandcamp.com/album/streams', + imageUrl: 'https://f4.bcbits.com/img/a3985878002_2.jpg', + genre: 'ambient', + artist: { + name: 'Autumn Of Communion', + url: 'https://autumnofcommunion.bandcamp.com' + }, + featuredTrack: { + name: 'Streams Pt.1', + position: 1, + streamUrl: 'https://t4.bcbits.com/stream/28cab33b36c3852c6f49fafe58d866e8/mp3-128/2312988978?p=0&ts=1686495035&t=abcd5a614180accf741f08e28e988f2f9b706b75&token=1686495035_af242d596fc008be06631b82485215d21a2f4e86' + } + }, + { + type: 'album', + name: 'Ethnomite Pux And Friends', + url: 'https://spmusic5.bandcamp.com/album/ethnomite-pux-and-friends', + imageUrl: 'https://f4.bcbits.com/img/a1510578540_2.jpg', + genre: 'experimental', + artist: { name: 'Various Artists', url: 'https://spmusic5.bandcamp.com' }, + featuredTrack: { + name: 'Sordes (1)', + position: 1, + streamUrl: 'https://t4.bcbits.com/stream/770d2ed68b10b6b2df8705b032b61161/mp3-128/4203595718?p=0&ts=1686495035&t=e38976a1366f2ba37ace89ac9be796d58bf2d909&token=1686495035_aa12c119f6800d2149f53a2c35b0b121d44170ca' + } + }, + { + type: 'album', + name: 'Strange Lights (EP)', + url: 'https://mystryl.bandcamp.com/album/strange-lights-ep', + imageUrl: 'https://f4.bcbits.com/img/a2955001523_2.jpg', + genre: 'electronic', + artist: { name: 'Mystryl', url: 'https://mystryl.bandcamp.com' }, + featuredTrack: { + name: 'Strange Lights', + position: 1, + streamUrl: 'https://t4.bcbits.com/stream/97bfc6f77af7be8d25d9ac11971667b8/mp3-128/3083200542?p=0&ts=1686495035&t=717833ffd66495d2b63b69567f2ff3444e6e2aff&token=1686495035_a2de9ec49e24edd97b3ab3f3a9d77bdaee2e9efb' + } + }, + { + type: 'album', + name: 'ashes and weeds', + url: 'https://room291.bandcamp.com/album/ashes-and-weeds', + imageUrl: 'https://f4.bcbits.com/img/a1211916676_2.jpg', + genre: 'electronic', + artist: { name: 'Room 291', url: 'https://room291.bandcamp.com' }, + featuredTrack: { + name: 'into silence', + position: 3, + streamUrl: 'https://t4.bcbits.com/stream/95618a2f748ff44c002319b6b81a0341/mp3-128/3190092398?p=0&ts=1686495035&t=fb5a2ec9012305918c8b8231d6f04645cd2b423b&token=1686495035_65bbbca3a82da0c82bf7575ec58d488bc29ce2a4' + } + }, + { + type: 'album', + name: 'tʌntrə XXV', + url: 'https://neotantra.bandcamp.com/album/t-ntr-xxv', + imageUrl: 'https://f4.bcbits.com/img/a3021904891_2.jpg', + genre: 'ambient', + artist: { name: 'Various', url: 'https://neotantra.bandcamp.com' }, + featuredTrack: { + name: 'EugeneKha-Bright Green Waves(Live At Samara)', + position: 10, + streamUrl: 'https://t4.bcbits.com/stream/0b1437807f7d1645383233bc3fb60497/mp3-128/1904111050?p=0&ts=1686495035&t=93a89aeb8a15d8db2a9e592eabfb8331ee0f0fd9&token=1686495035_f6994ec73b0712f67b711fbfb2aec1dcfaa8513c' + } + }, + { + type: 'album', + name: 'Alnocys 3', + url: 'https://23stabwounds-records.bandcamp.com/album/alnocys-3', + imageUrl: 'https://f4.bcbits.com/img/a1993703500_2.jpg', + genre: 'experimental', + artist: { + name: 'Alnocys', + url: 'https://23stabwounds-records.bandcamp.com' + }, + featuredTrack: { + name: 'Reminiscencias extraviadas', + position: 1, + streamUrl: 'https://t4.bcbits.com/stream/e350bd06c2da4bb7b2e48fbba69e5dfc/mp3-128/191918074?p=0&ts=1686495035&t=1c94583fafa9b49505b7111ab4ca8d1eb5a696b7&token=1686495035_681ed005bc5e4d45c4da2fdef34a4301349aa379' + } + }, + { + type: 'album', + name: 'INTERCEPT', + url: 'https://spunoutofcontrol.bandcamp.com/album/intercept', + imageUrl: 'https://f4.bcbits.com/img/a3705697221_2.jpg', + genre: 'electronic', + artist: { + name: 'STEVE NOLAN & GREY FREQUENCY', + url: 'https://spunoutofcontrol.bandcamp.com' + }, + featuredTrack: { + name: 'IN PRIVATE', + position: 9, + streamUrl: 'https://t4.bcbits.com/stream/2ef7f8907f854b2f97b1565836571605/mp3-128/738363662?p=0&ts=1686495035&t=ff2d248f0055741ebbc98c95c8f26e362deb9920&token=1686495035_e4d2ea5d784a88240ab9218defd683ffeb05b2a5' + } + }, + { + type: 'album', + name: 'Valencia', + url: 'https://8-bitbear.bandcamp.com/album/valencia', + imageUrl: 'https://f4.bcbits.com/img/a2766359739_2.jpg', + genre: 'electronic', + artist: { name: '8-Bit Bear', url: 'https://8-bitbear.bandcamp.com' }, + featuredTrack: { + name: 'Autumn Field', + position: 7, + streamUrl: 'https://t4.bcbits.com/stream/11e424226bc965f8dd29287f84ca3184/mp3-128/2062775337?p=0&ts=1686495035&t=8ac5f5d1500f17d4b3be7b841e43f3a78767eddb&token=1686495035_496151e550fa7089dbb6ca7133355ce5e5808720' + } + }, + { + type: 'album', + name: 'Wasserstoff', + url: 'https://stefanschulzki.bandcamp.com/album/wasserstoff', + imageUrl: 'https://f4.bcbits.com/img/a1676170246_2.jpg', + genre: 'alternative', + artist: { + name: 'Stefan Schulzki', + url: 'https://stefanschulzki.bandcamp.com' + }, + featuredTrack: { + name: 'Wasserstoff', + position: 1, + streamUrl: 'https://t4.bcbits.com/stream/c4b86061e3e4efa7c47f583886d599cb/mp3-128/4127970454?p=0&ts=1686495035&t=2602a11ed05591df42c5b79ce29c341bfe652e52&token=1686495035_010f1038450c7985ea4e52038ed745fcc6520f85' + } + }, + { + type: 'album', + name: 'Compilation #2', + url: 'https://reeveswork.bandcamp.com/album/compilation-2-2', + imageUrl: 'https://f4.bcbits.com/img/a1444943785_2.jpg', + genre: 'electronic', + artist: { name: 'REEVE', url: 'https://reeveswork.bandcamp.com' }, + featuredTrack: { + name: 'The Sun Sets On Alita', + position: 1, + streamUrl: 'https://t4.bcbits.com/stream/66abd18006a352f13c427eef842dfbb2/mp3-128/2011849685?p=0&ts=1686495035&t=8f8f344e57c8329d8528defe1b01aa6343694091&token=1686495035_7e22738067675c965e25651c09c82cef1b0c2ead' + } + }, + { + type: 'album', + name: 'The Nightmare', + url: 'https://cristianbergagna.bandcamp.com/album/the-nightmare', + imageUrl: 'https://f4.bcbits.com/img/a2422405084_2.jpg', + genre: 'electronic', + artist: { + name: 'Cristian Bergagna', + url: 'https://cristianbergagna.bandcamp.com' + }, + featuredTrack: { + name: 'Morpheus', + position: 1, + streamUrl: 'https://t4.bcbits.com/stream/6aa9f9fce0a3b76c87b9b7d570040908/mp3-128/1812859960?p=0&ts=1686495035&t=39bfce8fc1153d23f1f14aa491d0e9a33d317003&token=1686495035_c1b8ae0e1b2a43928978d875e9e6e0284ba93255' + } + } + ], + hasMore: true, + filters: { + sort: 'random', + format: 'all', + tags: [ 'dark-ambient', 'electronica' ], + location: 0 + } +} diff --git a/examples/tag/list.ts b/examples/tag/list.ts new file mode 100644 index 0000000..acb68ea --- /dev/null +++ b/examples/tag/list.ts @@ -0,0 +1,6 @@ +import bcfetch from '../../'; +import util from 'util'; + +bcfetch.tag.list().then((results) => { + console.log(util.inspect(results, false, null, false)); +}); diff --git a/examples/tag/list_output.txt b/examples/tag/list_output.txt new file mode 100644 index 0000000..3b2946a --- /dev/null +++ b/examples/tag/list_output.txt @@ -0,0 +1,334 @@ +{ + tags: [ + { name: 'electronic', url: 'https://bandcamp.com/tag/electronic' }, + { + name: 'experimental', + url: 'https://bandcamp.com/tag/experimental' + }, + { + name: 'alternative', + url: 'https://bandcamp.com/tag/alternative' + }, + { name: 'rock', url: 'https://bandcamp.com/tag/rock' }, + { name: 'ambient', url: 'https://bandcamp.com/tag/ambient' }, + { + name: 'hip-hop/rap', + url: 'https://bandcamp.com/tag/hip-hop-rap' + }, + { name: 'metal', url: 'https://bandcamp.com/tag/metal' }, + { name: 'punk', url: 'https://bandcamp.com/tag/punk' }, + { name: 'indie', url: 'https://bandcamp.com/tag/indie' }, + { name: 'noise', url: 'https://bandcamp.com/tag/noise' }, + { name: 'techno', url: 'https://bandcamp.com/tag/techno' }, + { name: 'indie rock', url: 'https://bandcamp.com/tag/indie-rock' }, + { name: 'pop', url: 'https://bandcamp.com/tag/pop' }, + { + name: 'instrumental', + url: 'https://bandcamp.com/tag/instrumental' + }, + { name: 'hip hop', url: 'https://bandcamp.com/tag/hip-hop' }, + { name: 'drone', url: 'https://bandcamp.com/tag/drone' }, + { name: 'rap', url: 'https://bandcamp.com/tag/rap' }, + { name: 'house', url: 'https://bandcamp.com/tag/house' }, + { name: 'folk', url: 'https://bandcamp.com/tag/folk' }, + { name: 'acoustic', url: 'https://bandcamp.com/tag/acoustic' }, + { + name: 'electronica', + url: 'https://bandcamp.com/tag/electronica' + }, + { + name: 'psychedelic', + url: 'https://bandcamp.com/tag/psychedelic' + }, + { + name: 'singer-songwriter', + url: 'https://bandcamp.com/tag/singer-songwriter' + }, + { name: 'hardcore', url: 'https://bandcamp.com/tag/hardcore' }, + { + name: 'dark ambient', + url: 'https://bandcamp.com/tag/dark-ambient' + }, + { name: 'lo-fi', url: 'https://bandcamp.com/tag/lo-fi' }, + { name: 'industrial', url: 'https://bandcamp.com/tag/industrial' }, + { name: 'jazz', url: 'https://bandcamp.com/tag/jazz' }, + { + name: 'alternative rock', + url: 'https://bandcamp.com/tag/alternative-rock' + }, + { name: 'hip-hop', url: 'https://bandcamp.com/tag/hip-hop' }, + { name: 'indie pop', url: 'https://bandcamp.com/tag/indie-pop' }, + { + name: 'experimental electronic', + url: 'https://bandcamp.com/tag/experimental-electronic' + }, + { name: 'electro', url: 'https://bandcamp.com/tag/electro' }, + { name: 'world', url: 'https://bandcamp.com/tag/world' }, + { name: 'beats', url: 'https://bandcamp.com/tag/beats' }, + { name: 'lofi', url: 'https://bandcamp.com/tag/lofi' }, + { + name: 'black metal', + url: 'https://bandcamp.com/tag/black-metal' + }, + { name: 'soundtrack', url: 'https://bandcamp.com/tag/soundtrack' }, + { name: 'deep house', url: 'https://bandcamp.com/tag/deep-house' }, + { name: 'post-punk', url: 'https://bandcamp.com/tag/post-punk' }, + { name: 'post-rock', url: 'https://bandcamp.com/tag/post-rock' }, + { name: 'punk rock', url: 'https://bandcamp.com/tag/punk-rock' }, + { name: 'soul', url: 'https://bandcamp.com/tag/soul' }, + { name: 'downtempo', url: 'https://bandcamp.com/tag/downtempo' }, + { name: 'shoegaze', url: 'https://bandcamp.com/tag/shoegaze' }, + { + name: 'death metal', + url: 'https://bandcamp.com/tag/death-metal' + }, + { name: 'dance', url: 'https://bandcamp.com/tag/dance' }, + { name: 'funk', url: 'https://bandcamp.com/tag/funk' }, + { name: 'minimal', url: 'https://bandcamp.com/tag/minimal' }, + { + name: 'avant-garde', + url: 'https://bandcamp.com/tag/avant-garde' + }, + { name: 'synthwave', url: 'https://bandcamp.com/tag/synthwave' }, + { + name: 'improvisation', + url: 'https://bandcamp.com/tag/improvisation' + }, + { name: 'idm', url: 'https://bandcamp.com/tag/idm' }, + { name: 'vaporwave', url: 'https://bandcamp.com/tag/vaporwave' }, + { name: 'drum & bass', url: 'https://bandcamp.com/tag/drum-bass' }, + { name: 'blues', url: 'https://bandcamp.com/tag/blues' }, + { + name: 'progressive', + url: 'https://bandcamp.com/tag/progressive' + }, + { name: 'synthpop', url: 'https://bandcamp.com/tag/synthpop' }, + { name: 'dub', url: 'https://bandcamp.com/tag/dub' }, + { name: 'emo', url: 'https://bandcamp.com/tag/emo' }, + { name: 'indie folk', url: 'https://bandcamp.com/tag/indie-folk' }, + { name: 'synth', url: 'https://bandcamp.com/tag/synth' }, + { name: 'trap', url: 'https://bandcamp.com/tag/trap' }, + { + name: 'underground hip hop', + url: 'https://bandcamp.com/tag/underground-hip-hop' + }, + { + name: 'ambient electronic', + url: 'https://bandcamp.com/tag/ambient-electronic' + }, + { + name: 'underground', + url: 'https://bandcamp.com/tag/underground' + }, + { name: 'r&b', url: 'https://bandcamp.com/tag/r-b' }, + { name: 'folk rock', url: 'https://bandcamp.com/tag/folk-rock' }, + { name: 'piano', url: 'https://bandcamp.com/tag/piano' }, + { + name: 'electronic music', + url: 'https://bandcamp.com/tag/electronic-music' + }, + { + name: 'instrumental hip-hop', + url: 'https://bandcamp.com/tag/instrumental-hip-hop' + }, + { name: 'garage', url: 'https://bandcamp.com/tag/garage' }, + { name: 'tech house', url: 'https://bandcamp.com/tag/tech-house' }, + { name: 'dubstep', url: 'https://bandcamp.com/tag/dubstep' }, + { + name: 'harsh noise', + url: 'https://bandcamp.com/tag/harsh-noise' + }, + { name: 'chillout', url: 'https://bandcamp.com/tag/chillout' }, + { name: 'americana', url: 'https://bandcamp.com/tag/americana' }, + { name: 'pop rock', url: 'https://bandcamp.com/tag/pop-rock' }, + { name: 'classical', url: 'https://bandcamp.com/tag/classical' }, + { name: 'guitar', url: 'https://bandcamp.com/tag/guitar' }, + { name: 'chill', url: 'https://bandcamp.com/tag/chill' }, + { + name: 'psychedelic rock', + url: 'https://bandcamp.com/tag/psychedelic-rock' + }, + { name: 'soundscape', url: 'https://bandcamp.com/tag/soundscape' }, + { + name: 'hardcore punk', + url: 'https://bandcamp.com/tag/hardcore-punk' + }, + { + name: 'progressive rock', + url: 'https://bandcamp.com/tag/progressive-rock' + }, + { + name: 'atmospheric', + url: 'https://bandcamp.com/tag/atmospheric' + }, + { name: 'edm', url: 'https://bandcamp.com/tag/edm' }, + { name: 'pop punk', url: 'https://bandcamp.com/tag/pop-punk' }, + { + name: 'garage rock', + url: 'https://bandcamp.com/tag/garage-rock' + }, + { name: 'psytrance', url: 'https://bandcamp.com/tag/psytrance' }, + { name: 'grunge', url: 'https://bandcamp.com/tag/grunge' }, + { name: 'dream pop', url: 'https://bandcamp.com/tag/dream-pop' }, + { name: 'trance', url: 'https://bandcamp.com/tag/trance' }, + { name: 'reggae', url: 'https://bandcamp.com/tag/reggae' }, + { name: 'hard rock', url: 'https://bandcamp.com/tag/hard-rock' }, + { name: 'country', url: 'https://bandcamp.com/tag/country' }, + { name: 'hiphop', url: 'https://bandcamp.com/tag/hiphop' }, + { name: 'grindcore', url: 'https://bandcamp.com/tag/grindcore' }, + { name: 'bass', url: 'https://bandcamp.com/tag/bass' }, + { name: 'darkwave', url: 'https://bandcamp.com/tag/darkwave' }, + ... 232 more items + ], + locations: [ + { + name: 'United Kingdom', + url: 'https://bandcamp.com/tag/united-kingdom' + }, + { name: 'California', url: 'https://bandcamp.com/tag/california' }, + { name: 'England', url: 'https://bandcamp.com/tag/england' }, + { name: 'Germany', url: 'https://bandcamp.com/tag/germany' }, + { name: 'Canada', url: 'https://bandcamp.com/tag/canada' }, + { name: 'France', url: 'https://bandcamp.com/tag/france' }, + { name: 'New York', url: 'https://bandcamp.com/tag/new-york' }, + { name: 'Australia', url: 'https://bandcamp.com/tag/australia' }, + { name: 'Spain', url: 'https://bandcamp.com/tag/spain' }, + { name: 'Italy', url: 'https://bandcamp.com/tag/italy' }, + { name: 'USA', url: 'https://bandcamp.com/tag/usa' }, + { + name: 'Los Angeles', + url: 'https://bandcamp.com/tag/los-angeles' + }, + { name: 'Texas', url: 'https://bandcamp.com/tag/texas' }, + { name: 'Russia', url: 'https://bandcamp.com/tag/russia' }, + { name: 'London', url: 'https://bandcamp.com/tag/london' }, + { name: 'Illinois', url: 'https://bandcamp.com/tag/illinois' }, + { name: 'Ontario', url: 'https://bandcamp.com/tag/ontario' }, + { name: 'Japan', url: 'https://bandcamp.com/tag/japan' }, + { + name: 'Pennsylvania', + url: 'https://bandcamp.com/tag/pennsylvania' + }, + { name: 'Berlin', url: 'https://bandcamp.com/tag/berlin' }, + { name: 'Argentina', url: 'https://bandcamp.com/tag/argentina' }, + { name: 'Washington', url: 'https://bandcamp.com/tag/washington' }, + { name: 'Chicago', url: 'https://bandcamp.com/tag/chicago' }, + { name: 'Brazil', url: 'https://bandcamp.com/tag/brazil' }, + { name: 'Florida', url: 'https://bandcamp.com/tag/florida' }, + { + name: 'Netherlands', + url: 'https://bandcamp.com/tag/netherlands' + }, + { + name: 'Massachusetts', + url: 'https://bandcamp.com/tag/massachusetts' + }, + { name: 'Michigan', url: 'https://bandcamp.com/tag/michigan' }, + { name: 'Ohio', url: 'https://bandcamp.com/tag/ohio' }, + { name: 'Oregon', url: 'https://bandcamp.com/tag/oregon' }, + { name: 'Québec', url: 'https://bandcamp.com/tag/qu%C3%A9bec' }, + { name: 'Sweden', url: 'https://bandcamp.com/tag/sweden' }, + { name: 'Mexico', url: 'https://bandcamp.com/tag/mexico' }, + { name: 'VIC', url: 'https://bandcamp.com/tag/vic' }, + { name: 'Melbourne', url: 'https://bandcamp.com/tag/melbourne' }, + { name: 'Seattle', url: 'https://bandcamp.com/tag/seattle' }, + { name: 'Belgium', url: 'https://bandcamp.com/tag/belgium' }, + { name: 'Toronto', url: 'https://bandcamp.com/tag/toronto' }, + { + name: 'British Columbia', + url: 'https://bandcamp.com/tag/british-columbia' + }, + { name: 'Portland', url: 'https://bandcamp.com/tag/portland' }, + { name: 'Colorado', url: 'https://bandcamp.com/tag/colorado' }, + { + name: 'North Carolina', + url: 'https://bandcamp.com/tag/north-carolina' + }, + { name: 'Poland', url: 'https://bandcamp.com/tag/poland' }, + { name: 'Georgia', url: 'https://bandcamp.com/tag/georgia' }, + { name: 'New Jersey', url: 'https://bandcamp.com/tag/new-jersey' }, + { name: 'Tennessee', url: 'https://bandcamp.com/tag/tennessee' }, + { name: 'Portugal', url: 'https://bandcamp.com/tag/portugal' }, + { + name: 'Philadelphia', + url: 'https://bandcamp.com/tag/philadelphia' + }, + { name: 'Virginia', url: 'https://bandcamp.com/tag/virginia' }, + { name: 'Minnesota', url: 'https://bandcamp.com/tag/minnesota' }, + { name: 'Montreal', url: 'https://bandcamp.com/tag/montreal' }, + { + name: 'San Francisco', + url: 'https://bandcamp.com/tag/san-francisco' + }, + { name: 'Finland', url: 'https://bandcamp.com/tag/finland' }, + { name: 'UK', url: 'https://bandcamp.com/tag/uk' }, + { + name: 'Switzerland', + url: 'https://bandcamp.com/tag/switzerland' + }, + { name: 'Boston', url: 'https://bandcamp.com/tag/boston' }, + { name: 'Scotland', url: 'https://bandcamp.com/tag/scotland' }, + { name: 'Austin', url: 'https://bandcamp.com/tag/austin' }, + { name: 'NSW', url: 'https://bandcamp.com/tag/nsw' }, + { name: 'Greece', url: 'https://bandcamp.com/tag/greece' }, + { name: 'Arizona', url: 'https://bandcamp.com/tag/arizona' }, + { name: 'Maryland', url: 'https://bandcamp.com/tag/maryland' }, + { name: 'IDF', url: 'https://bandcamp.com/tag/idf' }, + { + name: 'Île-de-France', + url: 'https://bandcamp.com/tag/%C3%AEle-de-france' + }, + { name: 'CT', url: 'https://bandcamp.com/tag/ct' }, + { + name: 'New Zealand', + url: 'https://bandcamp.com/tag/new-zealand' + }, + { name: 'Ukraine', url: 'https://bandcamp.com/tag/ukraine' }, + { name: 'Chile', url: 'https://bandcamp.com/tag/chile' }, + { name: 'Brooklyn', url: 'https://bandcamp.com/tag/brooklyn' }, + { name: 'Missouri', url: 'https://bandcamp.com/tag/missouri' }, + { name: 'Atlanta', url: 'https://bandcamp.com/tag/atlanta' }, + { name: 'Paris', url: 'https://bandcamp.com/tag/paris' }, + { name: 'Vancouver', url: 'https://bandcamp.com/tag/vancouver' }, + { name: 'Indiana', url: 'https://bandcamp.com/tag/indiana' }, + { name: 'NRW', url: 'https://bandcamp.com/tag/nrw' }, + { name: 'Denmark', url: 'https://bandcamp.com/tag/denmark' }, + { name: 'Wisconsin', url: 'https://bandcamp.com/tag/wisconsin' }, + { name: 'Austria', url: 'https://bandcamp.com/tag/austria' }, + { name: 'Norway', url: 'https://bandcamp.com/tag/norway' }, + { name: 'Denver', url: 'https://bandcamp.com/tag/denver' }, + { name: 'Ireland', url: 'https://bandcamp.com/tag/ireland' }, + { name: 'Tokyo', url: 'https://bandcamp.com/tag/tokyo' }, + { name: 'Barcelona', url: 'https://bandcamp.com/tag/barcelona' }, + { name: 'CABA', url: 'https://bandcamp.com/tag/caba' }, + { + name: 'Minneapolis', + url: 'https://bandcamp.com/tag/minneapolis' + }, + { + name: 'Buenos Aires', + url: 'https://bandcamp.com/tag/buenos-aires' + }, + { name: 'Detroit', url: 'https://bandcamp.com/tag/detroit' }, + { name: 'Nashville', url: 'https://bandcamp.com/tag/nashville' }, + { name: 'Sydney', url: 'https://bandcamp.com/tag/sydney' }, + { name: 'Israel', url: 'https://bandcamp.com/tag/israel' }, + { + name: 'Connecticut', + url: 'https://bandcamp.com/tag/connecticut' + }, + { name: 'Hungary', url: 'https://bandcamp.com/tag/hungary' }, + { name: 'Alberta', url: 'https://bandcamp.com/tag/alberta' }, + { + name: 'South Africa', + url: 'https://bandcamp.com/tag/south-africa' + }, + { name: 'SP', url: 'https://bandcamp.com/tag/sp' }, + { name: 'Colombia', url: 'https://bandcamp.com/tag/colombia' }, + { name: 'Pittsburgh', url: 'https://bandcamp.com/tag/pittsburgh' }, + { name: 'San Diego', url: 'https://bandcamp.com/tag/san-diego' }, + { name: 'Oakland', url: 'https://bandcamp.com/tag/oakland' }, + { name: 'Manchester', url: 'https://bandcamp.com/tag/manchester' }, + ... 247 more items + ] +} diff --git a/examples/track/getInfo.ts b/examples/track/getInfo.ts new file mode 100644 index 0000000..df81c64 --- /dev/null +++ b/examples/track/getInfo.ts @@ -0,0 +1,15 @@ +import bcfetch from '../../'; +import util from 'util'; + +const trackUrl = 'https://musique.coeurdepirate.com/track/tes-belle'; + +const params = { + trackUrl, + albumImageFormat: 'art_app_large', + artistImageFormat: 'bio_featured', + includeRawData: false +}; + +bcfetch.track.getInfo(params).then((results) => { + console.log(util.inspect(results, false, null, false)); +}); diff --git a/examples/track/getInfo_output.txt b/examples/track/getInfo_output.txt new file mode 100644 index 0000000..24198df --- /dev/null +++ b/examples/track/getInfo_output.txt @@ -0,0 +1,16 @@ +{ + type: 'track', + name: "T'es belle", + url: 'https://musique.coeurdepirate.com/track/tes-belle', + imageUrl: 'https://f4.bcbits.com/img/a0774650359_16.jpg', + releaseDate: '01 Oct 2020 00:00:00 GMT', + streamUrl: 'https://t4.bcbits.com/stream/63315678db5ea41d2f0417ac7d4f5ca3/mp3-128/3387079907?p=0&ts=1686495112&t=a7968eecb9122b64f2575aeef7bf9a4c902b3f0e&token=1686495112_860fcdc1a34d6bc999ffad1adfecd227b435bd25', + artist: { name: 'Cœur de pirate', url: 'https://musique.coeurdepirate.com' }, + publisher: { + name: 'Cœur de pirate', + url: 'https://musique.coeurdepirate.com', + description: 'Plus d’une décennie s’est écoulée depuis que Béatrice Martin s’est incrustée dans le paysage sous le pseudonyme désormais coutumier de Cœur de pirate. Armée d’un talent digne de l’orfèvrerie, d’une poésie tantôt raffinée, tantôt subversive, et d’une aura insaisissable, elle séduit comme elle surprend, jaillissant là où on ne l’attend pas.', + imageUrl: 'https://f4.bcbits.com/img/0026415167_28.jpg' + }, + label: { name: 'Bravo musique', url: 'https://bravomusique.bandcamp.com' } +} diff --git a/fixup.sh b/fixup.sh new file mode 100644 index 0000000..a4c3aaf --- /dev/null +++ b/fixup.sh @@ -0,0 +1,11 @@ +cat >dist/cjs/package.json <dist/mjs/package.json < { - self._cache.ttl(key, ttl); - }) - } - self._ttl = ttl; - } - - setMaxEntries(type, maxEntries) { - this.reduceEntries(type, maxEntries); - this._maxEntries[type] = maxEntries; - } - - getMaxEntries(type) { - return this._maxEntries[type] !== undefined ? this._maxEntries[type] : -1; - } - - get(type, key) { - return this._cache.get(type + '.' + key); - } - - put(type, key, value) { - const maxEntries = this.getMaxEntries(); - if (maxEntries === 0) { - return false; - } - else if (maxEntries > 0) { - this.reduceEntries(maxEntries - 1); - } - return this._cache.set(type + '.' + key, value, this._ttl[type]); - } - - reduceEntries(type, reduceTo) { - if (reduceTo === undefined) { - reduceTo = this.getMaxEntries(type); - } - if (reduceTo < 0) { - return; - } - const keys = this.getKeys(type); - if (keys.length > reduceTo) { - for (let i = 0; i < keys.length - reduceTo; i++) { - this._cache.del(keys[i]); - } - } - } - - getKeys(type) { - return this._cache.keys().filter( key => key.startsWith(type + '.') ); - } - - clear(type) { - if (!type) { - this._cache.flushAll(); - } - else { - this.getKeys(type).forEach( key => { - this._cache.del(key); - }); - } - } - - async getOrSet(type, key, promiseCallback) { - const self = this; - const cachedValue = self.get(type, key); - if (cachedValue !== undefined) { - return cachedValue; - } - else if (promiseCallback) { - return promiseCallback().then( value => { - self.put(type, key, value); - return value; - }); - } - else { - return null; - } - } -} - -module.exports = Cache; \ No newline at end of file diff --git a/lib/index.js b/lib/index.js deleted file mode 100644 index 4a4933f..0000000 --- a/lib/index.js +++ /dev/null @@ -1,604 +0,0 @@ -const fetch = require('node-fetch'); -const Bottleneck = require('bottleneck'); -const utils = require('./utils.js'); -const parser = require('./parser.js'); -const Cache = require('./cache.js'); - -const _cache = new Cache({ constant: 3600, page: 300 }, { page: 10 }); - -async function discover(params, options = {}) { - const imageConstants = await _getImageConstants(); - const opts = { - imageBaseUrl: imageConstants.baseUrl, - albumImageFormat: await _parseImageFormatArg(options.albumImageFormat, 9), - artistImageFormat: await _parseImageFormatArg(options.artistImageFormat, 21) - }; - - let resultParams; - return sanitizeDiscoverParams(params) - .then( sanitizedParams => { - resultParams = Object.assign({}, sanitizedParams); - // Passing an 'all' type subgenre (e.g. 'all-metal') in the discover url - // actually returns far fewer / zero results than without. - // The Bandcamp site also does not seem to include it in its discover requests... - if (sanitizedParams.time !== undefined) { - // If 'time' exists in sanitized params, then we have an 'all' type subgenre - // - refer to sanitizeDiscoverParams() - delete sanitizedParams.subgenre; - } - return utils.getDiscoverUrl(sanitizedParams); - }) - .then( url => _fetchPage(url, true) ) - .then( json => { - const result = parser.parseDiscoverResults(json, opts); - result.params = resultParams; - return result; - }); -} - -async function sanitizeDiscoverParams(params) { - return getDiscoverOptions().then( options => { - const getOptionValue = (optArr, value, defaultIndex = 0) => { - if (value !== undefined && optArr) { - const opt = optArr.find( o => o.value == value ); - if (opt) { - return opt.value; - } - } - if (optArr) { - return optArr[defaultIndex].value; - } - else { - return null; - } - } - const sanitized = { - genre: getOptionValue(options.genres, params.genre), - sortBy: getOptionValue(options.sortBys, params.sortBy), - page: params.page || 0 - }; - if (sanitized.sortBy !== 'rec') { - // following only valid when sortBy is not 'rec' (artist-recommend) - const subgenreOptions = options.subgenres[sanitized.genre]; - if (subgenreOptions) { // false if genre is 'all' - sanitized.subgenre = getOptionValue(subgenreOptions, params.subgenre); - } - // 'Time' option only available when there is effectively no subgenre (e.g. genre is 'all' - // or subgenre is 'all-metal') - const timeAllowed = sanitized.subgenre === undefined || sanitized.subgenre == subgenreOptions[0].value; - if (timeAllowed) { - sanitized.time = getOptionValue(options.times, params.time, 1); - } - sanitized.location = getOptionValue(options.locations, params.location); - sanitized.format = getOptionValue(options.formats, params.format); - } - else { - sanitized.artistRecommendationType = getOptionValue(options.artistRecommendationTypes, params.artistRecommendationType); - } - - return sanitized; - }); -} - -async function getDiscoverOptions() { - return _cache.getOrSet('constant', 'discoverOptions', () => { - return _fetchPage(utils.getSiteUrl()).then( html => parser.parseDiscoverOptions(html) ); - }); -} - -async function getImageFormat(idOrName) { - const imageConstants = await _getImageConstants(); - let result = null; - imageConstants.formats.every( format => { - if ( (typeof idOrName === 'string' && format.name === idOrName) || - (Number.isInteger(idOrName) && format.id === idOrName) ) { - result = format; - return false; - } - return true; - }); - return result; -} - -async function getImageFormats(filter = '') { - return _getImageConstants().then( constants => { - if (filter === 'album') { - return constants.formats.filter( c => c.name.startsWith('art_') ); - } - else if (filter === 'bio' || filter === 'artist') { - return constants.formats.filter( c => c.name.startsWith('bio_') ); - } - else { - return constants.formats; - } - }); -} - -async function _getImageConstants() { - return _cache.getOrSet('constant', 'imageConstants', () => { - return _fetchPage(utils.getSiteUrl()).then( html => parser.parseImageConstants(html) ); - }); -} - -async function _parseImageFormatArg(arg, defaultId = null) { - let result; - if (typeof arg === 'string' || Number.isInteger(arg)) { - result = await getImageFormat(arg); - } - else if (typeof arg === 'object') { - result = arg; - } - else { - result = null; - } - if (result === null && defaultId !== null) { - result = await getImageFormat(defaultId); - } - return result; -}; - -async function getAlbumInfo(albumUrl, options = {}) { - const opts = { - albumImageFormat: await _parseImageFormatArg(options.albumImageFormat), - artistImageFormat: await _parseImageFormatArg(options.artistImageFormat), - includeRawData: options.includeRawData ? true : false - }; - return _fetchPage(albumUrl).then( html => parser.parseAlbumInfo(html, opts) ); -} - -async function getTrackInfo(trackUrl, options = {}) { - const imageConstants = await _getImageConstants(); - const opts = { - trackUrl, - imageBaseUrl: imageConstants.baseUrl, - albumImageFormat: await _parseImageFormatArg(options.albumImageFormat, 9), - artistImageFormat: await _parseImageFormatArg(options.artistImageFormat), - includeRawData: options.includeRawData ? true : false - }; - return _fetchPage(trackUrl).then( html => parser.parseTrackInfo(html, opts) ); -} - -async function getDiscography(artistOrLabelUrl, options = {}) { - const imageConstants = await _getImageConstants(); - const opts = { - imageBaseUrl: imageConstants.baseUrl, - artistOrLabelUrl, - imageFormat: await _parseImageFormatArg(options.imageFormat, 9) - }; - return _fetchPage(utils.getUrl('music', artistOrLabelUrl)) - .then( html => parser.parseDiscography(html, opts) ); -} - -async function getArtistOrLabelInfo(artistOrLabelUrl, options = {}) { - const opts = { - artistOrLabelUrl, - imageFormat: await _parseImageFormatArg(options.imageFormat) - }; - const _getUrl = (path) => { - let _url = path ? utils.getUrl(path, artistOrLabelUrl) : artistOrLabelUrl; - if (options.labelId) { - _url += '/?label=' + encodeURIComponent(options.labelId); - } - return _url; - }; - const _isInfoComplete = (data) => { - return data.name !== null && data.url !== null && - (data.type === 'label' || data.label !== null); - } - const _fillInfo = (toData, fromData) => { - if (toData.name === null) { - toData.name = fromData.name; - } - if (toData.url === null) { - toData.url = fromData.url; - } - if (toData.label === null) { - toData.label = fromData.label; - } - } - - let url = _getUrl(); - let html = await _fetchPage(url); - let result = parser.parseArtistOrLabelInfo(html, opts); - // Return if result is complete - if (_isInfoComplete(result)) { - return result; - } - - // Info lacking name or label (for artist) - try getting them from music page - url = _getUrl('music'); - html = await _fetchPage(url); - let info = parser.parseArtistOrLabelInfo(html, opts); - _fillInfo(result, info); - // Return if result is complete - if (_isInfoComplete(result)) { - return result; - } - - // Info is still lacking name or label (for artist) - last try with fetching - // from discog's first album or track - let discogItems = await getDiscography(artistOrLabelUrl, options); - if (discogItems[0]) { - url = discogItems[0].url; - html = await _fetchPage(url); - info = parser.parseArtistOrLabelInfo(html, opts); - _fillInfo(result, info); - } - - if (result.url === null) { - result.url = artistOrLabelUrl; - } - - return result; -} - -async function getLabelArtists(labelUrl, options = {}) { - const opts = { - labelUrl, - imageFormat: await _parseImageFormatArg(options.imageFormat) - }; - return _fetchPage(utils.getUrl('artists', labelUrl)) - .then( html => parser.parseLabelArtists(html, opts) ); -} - -async function search(params, options = {}) { - const opts = { - albumImageFormat: await _parseImageFormatArg(options.albumImageFormat), - artistImageFormat: await _parseImageFormatArg(options.artistImageFormat) - }; - return _fetchPage(utils.getSearchUrl(params)) - .then( html => parser.parseSearchResults(html, opts) ); -} - -async function getAlbumHighlightsByTag(tagUrl, options = {}) { - const imageConstants = await _getImageConstants(); - const opts = { - imageBaseUrl: imageConstants.baseUrl, - imageFormat: await _parseImageFormatArg(options.imageFormat, 9) - }; - - return _fetchPage(tagUrl) - .then( html => parser.parseAlbumHighlightsByTag(html, opts) ); -} - -async function getTags() { - return _fetchPage(utils.getUrl('tags')) - .then( html => parser.parseTags(html) ); -} - -async function getAllShows(options = {}) { - const imageConstants = await _getImageConstants(); - const opts = { - imageBaseUrl: imageConstants.baseUrl, - showImageFormat: await _parseImageFormatArg(options.showImageFormat, 25) - }; - return _fetchPage(utils.getAllShowsUrl(), true) - .then( json => parser.parseAllShows(json, opts) ); -} - -async function getShow(showUrl, options = {}) { - const imageConstants = await _getImageConstants(); - const opts = { - showUrl, - imageBaseUrl: imageConstants.baseUrl, - albumImageFormat: await _parseImageFormatArg(options.albumImageFormat, 9), - artistImageFormat: await _parseImageFormatArg(options.artistImageFormat, 21), - showImageFormat: await _parseImageFormatArg(options.showImageFormat, 25) - }; - return _fetchPage(showUrl) - .then( html => parser.parseShow(html, opts) ); -} - -async function getArticleCategories() { - return _fetchPage(utils.getDailyUrl()) - .then( html => parser.parseArticleCategories(html) ); -} - -async function getArticleList(params = {}, options = {}) { - if (params.categoryUrl == undefined) { - params.categoryUrl = utils.getUrl('latest', utils.getDailyUrl()); - } - const opts = { - imageFormat: await _parseImageFormatArg(options.imageFormat) - }; - return _fetchPage(utils.getDailyUrl(params)) - .then( html => parser.parseArticleList(html, opts) ); -} - -async function getArticle(articleUrl, options = {}) { - const imageConstants = await _getImageConstants(); - const opts = { - imageBaseUrl: imageConstants.baseUrl, - albumImageFormat: await _parseImageFormatArg(options.albumImageFormat, 9), - artistImageFormat: await _parseImageFormatArg(options.artistImageFormat, 21), - includeRawData: options.includeRawData ? true : false - }; - return _fetchPage(articleUrl) - .then( html => parser.parseArticle(html, opts) ); -} - -async function getTagInfo(tagUrl) { - return _fetchPage(tagUrl) - .then( html => parser.parseTagInfo(html, {tagUrl}) ); -} - -async function getReleasesByTagFilterOptions(tagUrl) { - return getReleasesByTagFilterValueNames(tagUrl) - .then( filterValueNames => { - const opts = { - filterValueNames - }; - return _fetchPage(tagUrl) - .then( html => parser.parseReleasesByTagFilterOptions(html, opts)); - }); -} - -async function getReleasesByTagFilterValueNames(tagUrl) { - return _fetchPage(utils.getReleasesByTagUrl(tagUrl)) - .then( html => parser.parseHubJSPath(html) ) - .then( path => { - return _fetchPage(path).then( js => { - return parser.parseHubJSFilterValueNames(js); - }); - }); -} - -async function getReleasesByTag(tagUrl, params = {}, options = {}) { - const imageConstants = await _getImageConstants(); - const opts = { - imageBaseUrl: imageConstants.baseUrl, - imageFormat: await _parseImageFormatArg(options.imageFormat, 9) - }; - - const _getDefaultFilters = tagUrl => { - if (options.useHardcodedDefaultFilters) { - const tagUrlPath = utils.splitUrl(tagUrl).path; - if (tagUrlPath.endsWith('/')) { - tagUrlPath = tagUrlPath.substr(0, tagUrlPath.length - 1); - } - const tagValue = tagUrlPath.split('/').pop(); - - return Promise.resolve({ - tags: [tagValue], - location: 0, - format: 'all', - sort: 'pop' - }); - } - else { - return getReleasesByTagFilterOptions(tagUrl) - .then( filterOptions => { - const defaultFilters = {}; - filterOptions.forEach( filter => { - let selectedOption = filter.options.find( o => o.selected ); - let defaultOption = filter.options.find( o => o.default ); - if (selectedOption) { - if (filter.name === 'tags') { - defaultFilters[filter.name] = [selectedOption.value]; - } - else { - defaultFilters[filter.name] = selectedOption.value; - } - } - else if (defaultOption) { - defaultFilters[filter.name] = defaultOption.value; - } - }); - - return defaultFilters; - }); - } - } - - return _getDefaultFilters(tagUrl) - .then( defaultFilters => { - const tagsFilter = defaultFilters.tags ? defaultFilters.tags.slice(0) : []; - if (params.filters && Array.isArray(params.filters.tags)) { - params.filters.tags.forEach( tag => { - if (!tagsFilter.includes(tag)) { - tagsFilter.push(tag); - } - }) - } - const paramFilters = params.filters ? Object.assign(defaultFilters, params.filters, {tags: tagsFilter}) : defaultFilters; - - return { - filters: paramFilters, - page: params.page || 1 - }; - }) - .then( postData => { - return _fetchPage(utils.getDigDeeperUrl(), true, _getPostFetchOptions(postData)) - .then( json => parser.parseReleasesByTag(json, opts)); - }); -} - -async function searchTag(params) { - const postData = { - search_term: params.q, - count: params.limit - }; - return _fetchPage(utils.getSearchTagUrl(), true, _getPostFetchOptions(postData)) - .then( json => parser.parseSearchTagResults(json)); -} - -async function searchLocation(params) { - const postData = { - q: params.q, - n: params.limit, - geocoder_fallback: true - }; - return _fetchPage(utils.getSearchLocationUrl(), true, _getPostFetchOptions(postData)) - .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 getFanCollection(usernameOrContinuationToken, options = {}) { - try { - return await _commonGetFanItems(usernameOrContinuationToken, options, { - defaultImageFormat: 9, - continuationUrl: utils.getFanCollectionContinuationUrl(), - parsePageFn: parser.parseFanCollectionFromPage, - parseContinuationFn: parser.parseFanCollectionFromContinuation - }); - } catch (error) { - throw new Error('getFanCollection(): ' + error.message); - } -} - -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); - return doFetch.then( res => { - if (res.status === 429) { - const err = new Error('429 Too Many Requests'); - err.code = '429'; - throw err; - } - else { - return json ? res.json() : res.text(); - } - }); - }); -} - -function _getPostFetchOptions(postData) { - return { - method: 'POST', - body: JSON.stringify(postData), - headers: { 'Content-Type': 'application/x-www-form-urlencoded' } - }; -} - -// Cache functions -const cache = { - setTTL: _cache.setTTL.bind(_cache), - setMaxPages: (maxPages) => { - _cache.setMaxEntries('page', maxPages); - }, - clear: _cache.clear.bind(_cache) -}; - -// Exported functions -const _exportFn = { - discover, - getDiscoverOptions, - sanitizeDiscoverParams, - getImageFormats, - getImageFormat, - getAlbumInfo, - getTrackInfo, - getDiscography, - getArtistOrLabelInfo, - getLabelArtists, - search, - getAlbumHighlightsByTag, - getTags, - getAllShows, - getShow, - getArticleCategories, - getArticleList, - getArticle, - getTagInfo, - getReleasesByTagFilterOptions, - getReleasesByTag, - searchTag, - searchLocation, - getFanInfo, - getFanCollection, - getFanWishlist, - getFanFollowingArtistsAndLabels, - getFanFollowingGenres -}; - -// Bottleneck limiter -const _limiter = new Bottleneck({ - maxConcurrent: 5, - minTime: 200 -}); -const limiter = {}; -for (const [fnName, fn] of Object.entries(_exportFn)) { - limiter[fnName] = _limiter.wrap(fn); -} -limiter.updateSettings = _limiter.updateSettings.bind(_limiter); - -// Module exports -module.exports = Object.assign({}, _exportFn, { cache, limiter }); diff --git a/lib/parser.js b/lib/parser.js deleted file mode 100644 index 757c786..0000000 --- a/lib/parser.js +++ /dev/null @@ -1,1597 +0,0 @@ -const cheerio = require('cheerio'); -const {decode} = require('html-entities'); -const utils = require('./utils.js'); -const {EOL} = require('os'); -const safeEval = require('safe-eval'); - -// https://github.com/masterT/bandcamp-scraper/blob/master/lib/htmlParser.js -function assignProps(objFrom, objTo, propNames) { - propNames.forEach( propName => { - objTo[propName] = objFrom[propName]; - }) - return objTo; -} - -function parseDiscoverResults(json, opts) { - if (typeof json === 'object' && Array.isArray(json.items)) { - const results = { - items: [] - }; - json.items.forEach(function (item) { - if (item.type === 'a') { - const album = { - type: 'album', - name: item.primary_text, - url: '', - imageUrl: '', - genre: item.genre_text, - artist: { - name: item.secondary_text - }, - location: item.location_text, - featuredTrack: '' - }; - if (item.url_hints) { - album.artist.url = 'https://' + item.url_hints.subdomain + '.bandcamp.com'; - } - if (album.artist.url) { - album.url = album.artist.url + '/album/' + item.url_hints.slug; - } - if (item.art_id) { - album.imageUrl = opts.imageBaseUrl + '/img/a' + item.art_id + '_' + opts.albumImageFormat.id + '.jpg'; - } - if (item.featured_track) { - album.featuredTrack = { - name: item.featured_track.title, - duration: item.featured_track.duration || null, - streamUrl: item.featured_track.file || null - }; - } - if (item.bio_image) { - album.artist.imageUrl = opts.imageBaseUrl + '/img/' + item.bio_image.image_id + '_' + opts.artistImageFormat.id + '.jpg'; - } - results.items.push(album); - } - }) - results.total = json.total_count; - return results; - } - else { - console.log('Failed to parse discover results'); - return null; - } -} - -function parseDiscoverOptions(html) { - const $ = cheerio.load(html); - const blob = $('#pagedata[data-blob]').attr('data-blob'); - const parsed = JSON.parse(blob); - if (typeof parsed === 'object' && - typeof parsed.discover_2015 === 'object' && - typeof parsed.discover_2015.options === 'object') { - const options = parsed.discover_2015.options - const result = { - genres: [], - subgenres: {}, - sortBys: [], - artistRecommendationTypes: [], - locations: [], - formats: [], - times: [] - } - if (Array.isArray(options.r)) { - result.artistRecommendationTypes = options.r.map( r => assignProps(r, {}, ['value', 'name']) ); - } - if (Array.isArray(options.l)) { - result.locations = options.l.map( l => assignProps(l, {}, ['value', 'name']) ); - } - if (Array.isArray(options.w)) { - result.times = options.w.map( w => assignProps(w, {}, ['value', 'name', 'title']) ); - } - if (Array.isArray(options.f)) { - result.formats = options.f.map( f => assignProps(f, {}, ['value', 'name']) ); - } - if (Array.isArray(options.s)) { - result.sortBys = options.s.map( s => assignProps(s, {}, ['value', 'name']) ); - } - if (typeof options.t === 'object') { - for (const [genre, subgenres] of Object.entries(options.t)) { - if (Array.isArray(subgenres)) { - result.subgenres[genre] = subgenres.map(function (sg) { - return assignProps(sg, {}, ['value', 'name']) - }) - } - } - } - if (Array.isArray(options.g)) { - result.genres = options.g.map( g => assignProps(g, {}, ['value', 'name']) ); - } - return result; - } - else { - console.log('Failed to parse discover options'); - return null; - } -} - -function parseImageConstants(html) { - const $ = cheerio.load(html); - const vars = decode($('script[data-vars]').attr('data-vars')); - const parsed = JSON.parse(vars); - if (typeof parsed === 'object' && parsed.client_template_globals) { - return { - baseUrl: parsed.client_template_globals.image_siteroot_https, - formats: parsed.client_template_globals.image_formats - }; - } - else { - console.log('Failed to parse image constants'); - return null; - } -} - -function parseAlbumInfo(html, opts) { - const $ = cheerio.load(html); - const rawBasic = $('script[type="application/ld+json"]').html(); - const rawExtra = decode($('script[data-tralbum]').attr('data-tralbum')); - - const basic = JSON.parse(rawBasic); - const extra = JSON.parse(rawExtra); - if (typeof extra === 'object' && typeof basic === 'object') { - const album = { - type: 'album', - name: basic.name, - url: basic['@id'], - numTracks: basic.numTracks, - imageUrl: utils.reformatImageUrl(basic.image, opts.albumImageFormat), - keywords: basic.keywords, - description: basic.description || '', - releaseDate: extra.album_release_date, - artist: { - name: basic.byArtist.name, - url: basic.byArtist['@id'] || null, - description: null, - imageUrl: null, - }, - publisher: null, - label: _parseBackToLabelLink($), - releases: [], - tracks: [] - }; - _setPublisher(album, basic, opts.artistImageFormat); - - if (Array.isArray(basic.albumRelease)) { - basic.albumRelease.filter( release => release.musicReleaseFormat ).forEach( release => { - const releaseItem = { - name: release.name, - url: null, - format: release.musicReleaseFormat, - description: release.description || '', - imageUrl: null - } - if (release['@id']) { - releaseItem.url = !utils.isAbsoluteUrl(release['@id']) ? utils.getUrl(release['@id'], album.url) : release['@id']; - } - if (release.image) { - if (Array.isArray(release.image) && release.image[0]) { - releaseItem.imageUrl = release.image[0]; - } - } - else { - let releaseImageArtId = getAdditionalPropertyValue(release, 'art_id'); - if (releaseImageArtId) { - opts.imageBaseUrl + '/img/a' + releaseImageArtId + '_' + opts.albumImageFormat.id + '.jpg' - } - } - album.releases.push(releaseItem); - }); - } - if (Array.isArray(extra.trackinfo)) { - extra.trackinfo.forEach( track => { - let trackUrl = track.title_link; - if (!utils.isAbsoluteUrl(trackUrl)) { - trackUrl = utils.getUrl(trackUrl, album.url); - } - album.tracks.push({ - position: track.track_num, - name: track.title, - url: trackUrl, - duration: track.duration, - streamUrl: (track.file && track.file['mp3-128']) || null - }); - }); - } - if (opts.includeRawData) { - album.raw = { basic, extra }; - } - - return album; - } - else { - console.log('Failed to parse album info'); - return null; - } -} - -function parseTrackInfo(html, opts) { - // Some tracks don't have a dedicated '/track' url, - // but take this form instead: {albumUrl}#t{x}, where 'x' is the - // track position. These tracks are not displayed as links nor playable. - // Since the album page is actually loaded, we can return the track info - // from the album data returned by parseAlbumInfo(). - const { path: trackUrlPath, hash: trackUrlHash } = utils.splitUrl(opts.trackUrl); - if (trackUrlPath && trackUrlHash) { - const matchTrackPosInUrl = /^\/(album)\/(.+)#t(\d+)/.exec(trackUrlPath + trackUrlHash); - if (matchTrackPosInUrl && matchTrackPosInUrl[3]) { - return parseTrackInfoFromAlbum(html, opts, matchTrackPosInUrl[3]); - } - } - - const $ = cheerio.load(html); - const rawBasic = $('script[type="application/ld+json"]').html(); - const rawExtra = decode($('script[data-tralbum]').attr('data-tralbum')); - - const basic = JSON.parse(rawBasic); - const extra = JSON.parse(rawExtra); - if (typeof extra === 'object' && typeof basic === 'object') { - const track = { - type: 'track', - name: basic.name, - url: basic['@id'], - imageUrl: opts.imageBaseUrl + '/img/a' + extra.art_id + '_' + opts.albumImageFormat.id + '.jpg', - releaseDate: extra.current.release_date, - duration: getAdditionalPropertyValue(basic, 'duration_secs'), - streamUrl: extra.trackinfo && extra.trackinfo[0] && extra.trackinfo[0].file && extra.trackinfo[0].file['mp3-128'] ? extra.trackinfo[0].file['mp3-128'] : null, - artist: null, - publisher: null, - label: _parseBackToLabelLink($), - album: null - } - let byArtist; - if (basic.inAlbum && basic.inAlbum.byArtist) { - byArtist = basic.inAlbum.byArtist; - } - else { - byArtist = basic.byArtist; - } - track.artist = { - name: byArtist.name, - url: byArtist['@id'] || null, - description: null, - imageUrl: null - } - _setPublisher(track, basic, opts.artistImageFormat); - - if (basic.inAlbum && basic.inAlbum['@id']) { - track.album = { - name: basic.inAlbum.name, - url: basic.inAlbum['@id'], - releaseDate: extra.album_release_date - } - track.releaseDate = extra.album_release_date; - } - if (opts.includeRawData) { - track.raw = { basic, extra }; - } - - return track; - } - else { - console.log('Failed to parse track info'); - return null; - } -} - -function parseTrackInfoFromAlbum(html, opts, trackPosition) { - const album = parseAlbumInfo(html, opts); - let trackData = album.tracks[trackPosition - 1] || {}; - const track = { - type: 'track', - name: trackData.name, - url: trackData.url, - imageUrl: album.imageUrl, - releaseDate: album.releaseDate, - duration: trackData.duration, - streamUrl: trackData.streamUrl, - artist: album.artist, - publisher: album.publisher, - label: album.label, - album: { - name: album.name, - url: album.url, - releaseDate: album.releaseDate - } - }; - return track; -} - -function getAdditionalPropertyValue(o, propName) { - if (Array.isArray(o.additionalProperty)) { - const p = o.additionalProperty.find( prop => prop.name === propName ); - if (p && p.value !== undefined) { - return p.value; - } - } - return undefined; -} - -function parseDiscography(html, opts) { - const $ = cheerio.load(html); - - // One-album / one-track artists don't have a discography page. - // The page for the album or track will be loaded instead. - // Check if this is the case and handle accordingly - const currentAlbumOrTrack = $('script[type="application/ld+json"]'); - let isOneTrack = false, - isOneAlbum = false; - if (currentAlbumOrTrack.length) { - currentAlbumOrTrackData = JSON.parse(currentAlbumOrTrack.html()); - if (typeof currentAlbumOrTrackData === 'object') { - // Check if there is a 'discography' element and, if there is, whether - // it is hidden or has only one track / album child - const discographyEl = $('#discography'); - if (discographyEl.length === 0 || discographyEl.css('display') === 'none' || discographyEl.find('li').length === 1) { - currentAlbumOrTrackUrl = utils.splitUrl(currentAlbumOrTrackData['@id']); - isOneTrack = currentAlbumOrTrackUrl.path.startsWith('/track/'); - isOneAlbum = currentAlbumOrTrackUrl.path.startsWith('/album/'); - } - } - } - if (isOneTrack || isOneAlbum) { - const newOpts = { - imageBaseUrl: opts.imageBaseUrl, - albumImageFormat: opts.imageFormat, - artistImageFormat: null, - includeRawData: false - }; - let info = isOneTrack ? parseTrackInfo(html, newOpts) : parseAlbumInfo(html, newOpts); - return [{ - url: info.url, - type: info.type, - name: info.name || '', - imageUrl: info.imageUrl || null, - artist: info.artist.name - }]; - } - - const allLinks = $('a'); - const items = {}; - const defaultArtistName = $('#band-name-location').find('.title').text(); - allLinks.each( (index, link) => { - link = $(link); - const href = link.attr('href'); - if (typeof href !== 'string' || href === '') { - return true; - } - let host, pathname; - // regex taken from: - // https://github.com/masterT/bandcamp-scraper/blob/master/lib/htmlParser.js - if (/^\/(track|album)\/(.+)$/.exec(href)) { // relative url starting with '/track' or '/album' - host = opts.artistOrLabelUrl; - pathname = href; - } - else { // full url (label discography) - try { - const _url = utils.splitUrl(href); - if (/^\/(track|album)\/(.+)$/.exec(_url.path)) { - host = _url.base; - pathname = _url.path; - } - } catch (e) { - return true; - } - } - if (host !== undefined && pathname !== undefined) { - const url = utils.getUrl(pathname, host); - if (items[url] === undefined) { - items[url] = { - type: pathname.startsWith('/track/') ? 'track' : 'album' - }; - } - // Link element wraps around img and title - const img = link.find('img'); - if (img.length) { - let imgSrc = img.attr('data-original') || img.attr('src'); - items[url].imageUrl = utils.reformatImageUrl(imgSrc, opts.imageFormat); - } - const title = link.find('.title'); - if (title.length) { - // For labels, title element contains artist name (when it doesn't, then artist = label). - // For artists, title element may also contain an artist name which overrides the default - const artistName = title.find('.artist-override'); - if (artistName.length) { - const artist = artistName.text().trim(); - artistName.remove(); - items[url].artist = artist; - } - else { - items[url].artist = defaultArtistName; - } - items[url].name = title.text().trim(); - } - - if (!img.length && !title.length) { - items[url].name = link.text().trim(); - } - } - }); - const results = []; - for (const [url, props] of Object.entries(items)) { - const item = { - url, - type: props.type, - name: props.name || '', - imageUrl: props.imageUrl || null, - artist: props.artist || defaultArtistName - }; - results.push(item); - } - return results; -} - -function parseArtistOrLabelInfo(html, opts) { - const $ = cheerio.load(html); - const bandData = JSON.parse(decode($('script[data-band]').attr('data-band'))); - - let bioText = $('#bio-text'); - let description; - if (bioText.length) { - let bioTextMore = bioText.find('.peekaboo-text'); - if (bioTextMore.length) { - bioTextMore.find('.lightweightBreak').remove(); - bioText.find('.peekaboo-text, .peekaboo-link').remove(); - description = (bioText.html().trim() + ' ' + bioTextMore.html()).trim(); - } - else { - description = bioText.html().trim(); - } - description = utils.stripLineBreaks(description); - description = utils.brToNewLine(description); - description = utils.stripTags(description); - description = decode(description); - } - else { - description = ''; - } - - let isLabel = bandData.is_label; - const result = { - type: isLabel ? 'label' : 'artist', - name: bandData.name || null, - url: bandData.url || null, - description: description, - location: $('#band-name-location').find('.location').text() || '', - imageUrl: utils.reformatImageUrl($('img.band-photo').attr('src'), opts.imageFormat) - }; - if (!isLabel) { - result.label = _parseBackToLabelLink($); - } - else { - result.labelId = bandData.id; - } - - return result; -} - -function parseLabelArtists(html, opts) { - const $ = cheerio.load(html); - const artistsList = $('li.featured-item, li.artists-grid-item'); - const results = []; - artistsList.each( (index, artistListItem) => { - artistListItem = $(artistListItem); - const img = artistListItem.find('img'); - const imgSrc = img.attr('data-original') || img.attr('src'); - const artist = { - name: artistListItem.find('.featured-grid-name, .artists-grid-name').text(), - url: utils.splitUrl(artistListItem.find('a').attr('href')).base, - location: artistListItem.find('.artists-grid-location').text(), - imageUrl: utils.reformatImageUrl(imgSrc, opts.imageFormat) - }; - results.push(artist); - }); - return results; -} - -function parseSearchResults(html, opts) { - const $ = cheerio.load(html); - const resultsList = $('li.searchresult'); - const results = []; - resultsList.each( (index, resultListItem) => { - resultListItem = $(resultListItem); - const resultInfo = resultListItem.find('.result-info'); - const resultType = resultInfo.children('.itemtype').text().trim().toLowerCase(); - const imgSrc = $('.art img', resultListItem).attr('src'); - const heading = $('.heading a', resultInfo); - const result = { - type: resultType, - name: heading.text().trim(), - url: resultInfo.find('.itemurl').text().trim(), - imageUrl: utils.reformatImageUrl(imgSrc, resultType === 'album' || resultType === 'track' ? opts.albumImageFormat : opts.artistImageFormat) - }; - resultInfo.find('.subhead, .genre, .tags, .released, .length').each( (index, info) => { - info = $(info); - if (info.hasClass('subhead')) { - if (resultType === 'artist' || resultType === 'label') { - result.location = info.text().trim(); - } - else if (resultType === 'album' || resultType === 'track') { - const infoText = info.text(); - const artist = utils.substrAfter(infoText, 'by '); - if (artist) { - result.artist = artist.trim(); - - if (resultType === 'track') { - let album = utils.substrBefore(infoText, ' by'); - if (album) { - album = utils.substrAfter(album, 'from '); - if (album) { - result.album = album.trim(); - } - } - } - } - } - return true; - } - if (info.hasClass('genre')) { - const genre = utils.substrAfter(info.text(), 'genre: '); - if (genre) { - result.genre = genre.trim(); - } - return true; - } - if (info.hasClass('tags')) { - const tags = utils.substrAfter(info.text(), 'tags:'); - if (tags) { - result.tags = utils.stripLineBreaks(utils.stripMultipleWhitespaces(tags)).trim(); - } - return true; - } - if (info.hasClass('released')) { - const released = utils.substrAfter(info.text(), 'released '); - if (released) { - result.releasedDate = released.trim(); - } - return true; - } - if (info.hasClass('length')) { - const lengthParts = info.text().split(','); - const tracksText = lengthParts[0]; - const minutesText = lengthParts[1]; - const numTracks = tracksText ? utils.substrBefore(tracksText, 'tracks') : null; - if (numTracks) { - result.numTracks = parseInt(numTracks, 10); - } - const minutes = minutesText ? utils.substrBefore(minutesText, 'minutes') : null; - if (minutes) { - result.duration = parseInt(minutes, 10) * 60; - } - } - }); - results.push(result); - }); - - let totalPages = parseInt($('.pagelist').find('.pagenum').last().text(), 10); - if (isNaN(totalPages)) { - totalPages = 1; - } - - return { - items: results, - totalPages - }; -} - -function parseAlbumHighlightsByTag(html, opts) { - const $ = cheerio.load(html); - const blob = decode($('#pagedata[data-blob]').attr('data-blob')); - const parsed = JSON.parse(blob); - const collections = []; - if (typeof parsed === 'object' && parsed.hub && - parsed.hub.tabs && parsed.hub.tabs[0].collections) { - - parsed.hub.tabs[0].collections.forEach( collection => { - const collectionRes = { - name: collection.name, - title: collection.render.title, - items: [] - }; - collection.items.forEach( item => { - if (item.item_type === 'a') { - const album = { - type: 'album', - name: item.title, - url: item.tralbum_url, - imageUrl: '', - genre: item.genre, - artist: { - name: item.artist, - url: item.band_url - } - }; - if (item.art_id) { - album.imageUrl = opts.imageBaseUrl + '/img/a' + item.art_id + '_' + opts.imageFormat.id + '.jpg'; - } - if (item.featured_track_title) { - album.featuredTrack = { - name: item.featured_track_title, - streamUrl: item.audio_url['mp3-128'] - }; - } - collectionRes.items.push(album); - } - }); - if (collectionRes.items.length) { - collections.push(collectionRes); - } - }); - } - return collections; -} - -function parseTags(html) { - const $ = cheerio.load(html); - - const _findTag = (tagUrl, tagName, tags) => { - return tags.find( t => t.url === tagUrl && t.name === tagName); - } - - const _parseCloud = (id) => { - const cloud = $(`#${id}`); - const tagsInCloud = []; - cloud.find('a.tag').each( (index, link) => { - link = $(link); - const name = link.text().trim(); - const url = utils.getUrl(link.attr('href')); - if (name && link.attr('href') !== '/tag/' && !_findTag(url, name, tagsInCloud)) { // Skip blank or repeating tags - tagsInCloud.push({ - name, - url - }); - } - }); - return tagsInCloud; - }; - - return { - tags: _parseCloud('tags_cloud'), - locations: _parseCloud('locations_cloud') - }; -} - -function parseAllShows(json, opts) { - const shows = []; - if (typeof json === 'object' && Array.isArray(json.results)) { - json.results.forEach( show => { - shows.push({ - type: 'show', - name: show.title, - url: utils.getShowUrl(show.id), - publishedDate: show.published_date, - description: show.desc, - imageCaption: show.image_caption, - subtitle: show.subtitle, - imageUrl: opts.imageBaseUrl + '/img/' + show.v2_image_id + '_' + opts.showImageFormat.id + '.jpg', - screenImageUrl: opts.imageBaseUrl + '/img/' + show.v2_image_id + '_0' - }) - }) - } - return shows; -} - -function parseShow(html, opts) { - const $ = cheerio.load(html); - const blob = decode($('#pagedata[data-blob]').attr('data-blob')); - const parsed = JSON.parse(blob); - - if (typeof parsed === 'object' && parsed.bcw_data) { - const showInfo = parsed.bcw_data[utils.getShowIdFromUrl(opts.showUrl)]; - if (showInfo) { - const show = { - type: 'show', - name: showInfo.title, - url: utils.getShowUrl(showInfo.show_id), - publishedDate: showInfo.published_date, - description: showInfo.desc, - shortDescription: showInfo.short_desc, - imageCaption: showInfo.image_caption, - subtitle: showInfo.subtitle, - duration: showInfo.audio_duration, - imageUrl: opts.imageBaseUrl + '/img/' + showInfo.show_v2_image_id + '_' + opts.showImageFormat.id + '.jpg', - screenImageUrl: opts.imageBaseUrl + '/img/' + showInfo.show_v2_image_id + '_0', - streamUrl: showInfo.audio_stream, - tracks: [] - } - showInfo.tracks.forEach( track => { - const trackItem = { - name: track.title, - url: track.track_url, - imageUrl: opts.imageBaseUrl + '/img/a' + track.track_art_id + '_' + opts.albumImageFormat.id + '.jpg', - seekPosition: track.timecode, - artist: { - name: track.artist, - url: 'https://' + track.url_hints.subdomain + '.bandcamp.com', - imageUrl: opts.imageBaseUrl + '/img/' + track.bio_image_id + '_' + opts.artistImageFormat.id + '.jpg', - location: track.location_text - }, - album: null - }; - if (track.album_title) { - trackItem.album = { - name: track.album_title, - url: track.album_url - } - } - show.tracks.push(trackItem); - }); - return show; - } - } - return null; -} - -function parseArticleCategories(html) { - const $ = cheerio.load(html); - const dailyUrl = utils.getDailyUrl(); - const _parseSection = (section) => { - const h = section.prev('h2'); - const title = h.length ? h.text() : ''; - const s = { - name: section.attr('class'), - title, - sections: [], - categories: [] - } - section.children().each( (i, c) => { - const tag = c.tagName; - c = $(c); - if (tag === 'section') { - const parsed = _parseSection($(c)); - if (parsed !== null) { - s.sections.push(parsed); - } - } - else if (tag === 'div') { - c.find('a').each( (i, a) => { - a = $(a); - let url = a.attr('href'); - if (!utils.isAbsoluteUrl(url)) { - url = utils.getUrl(url, dailyUrl); - } - s.categories.push({ - url, - name: a.text() - }); - }); - - } - }); - if (s.sections.length === 0) { - delete s.sections; - } - if (s.categories.length === 0) { - delete s.categories; - } - if (!s.sections && !s.categories) { - return null; - } - else { - return s; - } - }; - - const sections = $('#daily-view-all').children('section'); - const results = []; - sections.each( (i, section) => { - const parsed = _parseSection($(section)); - if (parsed !== null) { - results.push(parsed); - } - }); - - return results; -} - -function parseArticleList(html, opts) { - const $ = cheerio.load(html); - const dailyUrl = utils.getDailyUrl(); - const results = { - articles: [], - total: 0, - start: 0, - end: 0 - }; - - $('articles-list').each( (i, list) => { - $('.list-article', $(list)).each( (i, article) => { - article = $(article); - const imageUrl = article.find('img').attr('src') || null; - // category - const infoText = article.find('.article-info-text'); - const infoTextCategoryLink = infoText.find('a.franchise'); - const infoTextMiddot = infoText.find('.middot'); - const category = { - url: infoTextCategoryLink.attr('href') || null, - name: infoTextCategoryLink.text() || '' - }; - if (!utils.isAbsoluteUrl(category.url)) { - category.url = utils.getUrl(category.url, dailyUrl); - } - // date - infoTextCategoryLink.remove(); - infoTextMiddot.remove(); - const date = utils.stripLineBreaks(infoText.text()).trim(); - // title and url - const titleLink = article.find('a.title'); - const title = titleLink.text(); - let url = titleLink.attr('href'); - if (!utils.isAbsoluteUrl(url)) { - url = utils.getUrl(url, dailyUrl); - } - - if (titleLink) { - results.articles.push({ - url, - title, - date, - imageUrl: utils.reformatImageUrl(imageUrl, opts.imageFormat), - category, - }); - } - }); - }); - - const resultsText = utils.stripLineBreaks($('#num-results').text()).trim(); - const rtm = resultsText.match(/(\d+)(?:\s*to\s*)(\d+)(?:\s*of\s*)(\d+)/); - if (rtm.length === 4) { - results.total = parseInt(rtm[3], 10); - results.start = parseInt(rtm[1], 10); - results.end = parseInt(rtm[2], 10); - } - return results; -} - -function parseArticle(html, opts) { - const $ = cheerio.load(html); - const basic = JSON.parse($('script[type="application/ld+json"]').html()); - const players = JSON.parse(decode($('#p-daily-article').attr('data-player-infos'))); - - const article = { - title: basic.headline, - description: basic.description, - url: basic['@id'], - imageUrl: basic.image, - date: basic.datePublished, - category: { - name: basic.articleSection, - url: null - }, - genre: null, - author: { - name: basic.author.name, - url: basic.author['@id'] - }, - mediaItems: [], - sections: {} - }; - - // get genre - const genreLink = $('.genre a'); - if (genreLink.length > 0) { - article.genre = { - name: genreLink.text(), - url: genreLink.attr('href') - }; - - const genreReadMoreLink = $('.moreingenre a'); - if (genreReadMoreLink.length > 0) { - article.genre.readMoreUrl = genreReadMoreLink.attr('href'); - if (!utils.isAbsoluteUrl(article.genre.readMoreUrl)) { - article.genre.readMoreUrl = utils.getUrl(article.genre.readMoreUrl, utils.getDailyUrl()); - } - } - } - - // get category url - const categoryLink = $('article-type a'); - if (categoryLink.length > 0) { - article.category.url = categoryLink.attr('href'); - if (!utils.isAbsoluteUrl(article.category.url)) { - article.category.url = utils.getUrl(article.category.url, utils.getDailyUrl()); - } - } - - // get media items (albums and tracks featured in article) - if (Array.isArray(players)) { - players.forEach( player => { - const mediaItem = { - type: 'unknown', - name: player.title, - url: player.tralbum_url, - imageUrl: '', - featuredTrackPosition: player.featured_track_number, - artist: { - name: player.band_name, - url: player.band_url, - imageUrl: '', - location: player.band_location - }, - tracks: [], - mediaItemRef: player.player_id - }; - if (player.parent_tralbum_type === 'a') { - mediaItem.type = 'album'; - } - else if (player.parent_tralbum_type === 't') { - mediaItem.type = 'track'; - } - if (player.art_id) { - mediaItem.imageUrl = opts.imageBaseUrl + '/img/a' + player.art_id + '_' + opts.albumImageFormat.id + '.jpg'; - } - if (player.band_image_id) { - mediaItem.artist.imageUrl = opts.imageBaseUrl + '/img/' + player.band_image_id + '_' + opts.artistImageFormat.id + '.jpg'; - } - if (Array.isArray(player.tracklist)) { - player.tracklist.forEach( trackInfo => { - const track = { - position: trackInfo.track_number, - name: trackInfo.track_title, - duration: trackInfo.audio_track_duration, - streamUrl: trackInfo.audio_url['mp3-128'] - } - mediaItem.tracks.push(track); - }); - } - - article.mediaItems.push(mediaItem); - }); - } - - // Function that returns a section corresponding to a media item - const _getSectionByPlayer = player => { - const section = { - heading: null, - html: '', - text: '', - mediaItemRef: null - }; - - // Get heading - const heading = player.prevUntil('bamplayer-art', 'h3, h2').first(); - if (heading.length > 0) { - section.heading = { - html: heading.html(), - text: utils.stripTags(utils.brToNewLine(heading.html())).trim() - }; - } - - // Get html and text - const paragraphs = player.nextUntil('bamplayer-art, h3, h5, article-end', 'p'); - paragraphs.each( (i, p) => { - p = $(p); - section.html += (section.html !== '' ? EOL : '') + p.html(); - section.text += (section.text !== '' ? EOL + EOL : '') + p.text(); - }); - - // get mediaItemRef - const playerIdMatch = player.attr('data-bind').match(/playerMap\["(.+?)"]/); - section.mediaItemRef = playerIdMatch[1] || null; - - return section; - } - - // Function that returns the introductory paragraph(s) of the article - const _getIntroSection = articleBody => { - const firstPlayer = articleBody.find('bamplayer-art').first(); - const paragraphs = firstPlayer.length > 0 ? firstPlayer.prevAll('p') : articleBody.find('p'); - if (paragraphs.length > 0) { - const section = { - html: '', - text: '' - }; - paragraphs.each( (i, p) => { - p = $(p); - section.html += (section.html !== '' ? EOL : '') + p.html(); - section.text += (section.text !== '' ? EOL + EOL : '') + p.text(); - }); - return section; - } - else { - return null; - } - } - - // sections - const articleBody = $('#p-daily-article article'); - const sections = []; - const introSection = _getIntroSection(articleBody); - if (introSection) { - sections.push(introSection); - } - const bcplayers = articleBody.find('bamplayer-art'); - bcplayers.each( (i, player) => { - sections.push(_getSectionByPlayer($(player))); - }); - article.sections = sections; - - if (opts.includeRawData) { - article.raw = { - basic, - mediaItems: players, - body: articleBody.html() - }; - } - - return article; -} - -function parseTagInfo(html, opts) { - const $ = cheerio.load(html); - const blob = decode($('#pagedata[data-blob]').attr('data-blob')); - const parsed = JSON.parse(blob); - if (typeof parsed === 'object' && parsed.hub) { - const tag = { - type: 'tag', - name: parsed.hub.name, - url: opts.tagUrl, - value: parsed.hub.norm_name, - relatedTags: [] - }; - if (Array.isArray(parsed.hub.related_tags)) { - parsed.hub.related_tags.forEach( related => { - const relatedTag = { - type: 'tag', - name: related.name, - url: utils.getUrl(related.url), - value: related.norm_name, - isLocation: related.isloc - }; - tag.relatedTags.push(relatedTag); - }); - } - return tag; - } - else { - console.log('Failed to parse tag info'); - return null; - } -} - -function parseHubJSPath(html) { - const jsMatch = /src="((?:.+?)hub-(?:.+?).js)"/g.exec(html); - return jsMatch[1] || null; -} - -function parseHubJSFilterValueNames(js) { - const filterValueNames = {}; - const tObj = /"hubs\/digdeeper\/filter_value":(.+?)}\),/gs.exec(js); - if (tObj[1]) { - const t = safeEval(tObj[1]); - if (t && t[0] && Array.isArray(t[0].blocks)) { - const _getValFromBlockAttachment = attachment => { - if (typeof attachment === 'object' && attachment.type === 'translate') { - return utils.stripLineBreaks(attachment.nodelist[0]).trim(); - } - else if (typeof attachment === 'string') { - return utils.stripLineBreaks(attachment).trim(); - } - else { - return ''; - } - }; - t[0].blocks.forEach( filterBlock => { - const filter = safeEval(filterBlock.expression.split('==')[1]); - if (filter) { - filterBlock - .attachment.find( a => a.blocks ) - .blocks.filter( block => block.expression ) - .forEach( valueBlock => { - const value = safeEval(valueBlock.expression.split('==')[1]); - if (value != null && valueBlock.attachment) { - let valueName = valueBlock.attachment.reduce( (a, c) => { - cVal = utils.stripLineBreaks(_getValFromBlockAttachment(c)).trim(); - if (cVal !== '') { - return a !== '' ? a + ' ' + cVal : cVal; - } - else { - return a; - } - }, ''); - //console.log('value name: ' + valueName); - if (valueName) { - if (!filterValueNames[filter]) { - filterValueNames[filter] = {}; - } - filterValueNames[filter][value] = valueName; - } - } - }); - } - }); - } - } - return filterValueNames; -} - -function parseReleasesByTagFilterOptions(html, opts) { - const $ = cheerio.load(html); - const blob = decode($('#pagedata[data-blob]').attr('data-blob')); - const parsed = JSON.parse(blob); - const filters = []; - if (typeof parsed === 'object' && parsed.hub && Array.isArray(parsed.hub.tabs)) { - const tab = parsed.hub.tabs[1]; // All releases - - const _setOrAdd = (f, t, prop) => { - const target = f.options.find( f => f.value === t.value ); - if (target) { - target[prop] = true; - } - else if (t.value && t.name) { - const tAdd = { - value: t.value, - name: t.name, - }; - tAdd[prop] = true; - f.options.push(tAdd); - } - } - - if (tab && tab.dig_deeper && typeof tab.dig_deeper.filters === 'object') { - const filterKeys = Object.keys(tab.dig_deeper.filters); - filterKeys.forEach( filterName => { - const filter = { - name: filterName, - options: [] - } - const filterData = tab.dig_deeper.filters[filterName]; - if (Array.isArray(filterData.options)) { - filterData.options.forEach( filterOption => { - const valueName = opts.filterValueNames[filterName] && opts.filterValueNames[filterName][filterOption.value] ? opts.filterValueNames[filterName][filterOption.value] : filterOption.name || filterOption.value; - filter.options.push({ - value: filterOption.value, - name: valueName - }) - }); - } - if (typeof filterData.selected === 'object' && !Array.isArray(filterData.selected)) { - _setOrAdd(filter, filterData.selected, 'selected'); - } - else if (Array.isArray(filterData.selected)) { - filterData.selected.forEach( s => { - _setOrAdd(filter, s, 'selected'); - }) - } - if (filterData.default) { - _setOrAdd(filter, filterData.default, 'default'); - } - - filters.push(filter); - }); - } - } - return filters; -} - -function parseReleasesByTag(json, opts) { - if (typeof json === 'object' && Array.isArray(json.items)) { - const results = { - items: [] - }; - json.items.forEach(function (item) { - const mediaItem = { - type: 'unknown', - name: item.title, - url: item.tralbum_url, - imageUrl: '', - genre: item.genre, - artist: { - name: item.artist, - url: item.band_url - }, - featuredTrack: null - }; - if (item.item_type === 'a') { - mediaItem.type = 'album'; - } - else if (item.item_type === 't') { - mediaItem.type = 'track'; - } - if (item.art_id) { - mediaItem.imageUrl = opts.imageBaseUrl + '/img/a' + item.art_id + '_' + opts.imageFormat.id + '.jpg'; - } - if (item.featured_track_title) { - mediaItem.featuredTrack = { - name: item.featured_track_title, - position: item.featured_track_number, - streamUrl: (item.audio_url ? item.audio_url['mp3-128'] : null) || null - }; - } - results.items.push(mediaItem); - }); - results.hasMore = json.more_available; - results.filters = JSON.parse(json.filters); - return results; - } - else { - console.log('Failed to parse releases by tag'); - return null; - } -} - -function parseSearchTagResults(json) { - if (typeof json === 'object' && Array.isArray(json.matching_tags)) { - const results = []; - json.matching_tags.forEach( match => { - results.push({ - count: match.count, - value: match.tag_norm_name, - name: match.tag_name - }); - }); - return results; - } - else { - console.log('Failed to parse search tag results'); - return null; - } -} - -function parseSearchLocationResults(json) { - if (typeof json === 'object' && Array.isArray(json.results)) { - const results = []; - json.results.forEach( match => { - results.push({ - value: match.id, - name: match.name, - fullName: match.fullname - }); - }); - return results; - } - else { - console.log('Failed to parse search location results'); - return null; - } -} - -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.collection_data) { - result.collectionItemCount = parsed.collection_data.item_count || 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: 'collection' / '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 parseFanCollectionFromPage(html, opts) { - return _commonParseFanPageItems(html, opts, { - itemType: 'collection', - parseFn: _parseFanCollectionItem - }); -} - -function parseFanCollectionFromContinuation(json, continuationToken, opts) { - return _commonParseFanContinuationItems(json, continuationToken, opts, { - listKey: 'items', - parseFn: _parseFanCollectionItem - }); -} - -function _parseFanCollectionItem(data, opts, tracklists) { - return _parseFanWishlistItem(data, opts, tracklists); -} - -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) { - let linkText = labelLink.find('.back-link-text').html(); - label = { - name: utils.substrAfter(linkText, '
') || - utils.substrAfter(linkText, '
') || - utils.substrBefore(linkText, ' に戻る') || - utils.substrBefore(linkText, ' のアイテムをもっと聴く'), - url: utils.splitUrl(labelLink.attr('href')).base - }; - return label; - } - - return null; -} - -function _setPublisher(target, json, imageFormat) { - if (json.publisher) { - let publisher = { - name: json.publisher.name, - url: json.publisher['@id'], - description: json.publisher.description, - imageUrl: utils.reformatImageUrl(json.publisher.image, imageFormat) - } - target.publisher = publisher; - - // For backward compatibility - if (target.artist) { - if (target.artist.url === null) { - target.artist.url = target.publisher.url; - } - target.artist.description = target.publisher.description; - target.artist.imageUrl = target.publisher.imageUrl; - } - } - else { - target.publisher = null; - } -} - -module.exports = { - parseDiscoverResults, - parseDiscoverOptions, - parseImageConstants, - parseAlbumInfo, - parseTrackInfo, - parseDiscography, - parseArtistOrLabelInfo, - parseLabelArtists, - parseSearchResults, - parseAlbumHighlightsByTag, - parseTags, - parseAllShows, - parseShow, - parseArticleCategories, - parseArticleList, - parseArticle, - parseTagInfo, - parseHubJSPath, - parseHubJSFilterValueNames, - parseReleasesByTagFilterOptions, - parseReleasesByTag, - parseSearchTagResults, - parseSearchLocationResults, - parseFanInfo, - parseFanCollectionFromPage, - parseFanCollectionFromContinuation, - parseFanWishlistFromPage, - parseFanWishlistFromContinuation, - parseFanFollowingBandsFromPage, - parseFanFollowingBandsFromContinuation, - parseFanFollowingGenresFromPage, - parseFanFollowingGenresFromContinuation -}; diff --git a/lib/utils.js b/lib/utils.js deleted file mode 100644 index e8c404c..0000000 --- a/lib/utils.js +++ /dev/null @@ -1,218 +0,0 @@ -const querystring = require('querystring'); -const {URL} = require('url'); - -function getUrl(href, baseUrl) { - if (baseUrl === undefined) { - baseUrl = getSiteUrl(); - } - return new URL(href, baseUrl).toString(); -} - -function getSiteUrl() { - return 'https://bandcamp.com'; -} - -function getDiscoverUrl(params = {}) { - const qs = { - s: params.sortBy || 'top', - p: params.page || 0, - }; - if (params.genre) { - qs.g = params.genre; - - if (params.subgenre) { - qs.t = params.subgenre; - } - } - if (params.location !== undefined) { - qs.gn = params.location; - } - if (params.format) { - qs.f = params.format; - } - if (qs.s === 'rec' && params.artistRecommendationType) { - qs.r = params.artistRecommendationType; - } - if (params.time !== undefined) { - qs.w = params.time; - } - return getSiteUrl() + '/api/discover/3/get_web?' + querystring.encode(qs); -} - -function reformatImageUrl(imageUrl, imageFormat) { - if (typeof imageUrl === 'string' && imageFormat !== null) { - // regex taken from: - // https://github.com/masterT/bandcamp-scraper/blob/master/lib/htmlParser.js - return imageUrl.replace(/_\d{1,3}\./, `_${imageFormat.id}.`); - } - else if (typeof imageUrl === 'string') { - return imageUrl; - } - else { - return null; - } -} - -function stripTags(str) { - // https://css-tricks.com/snippets/javascript/strip-html-tags-in-javascript/ - return str.replace(/(<([^>]+)>)/gi, ''); -} - -function stripLineBreaks(str) { - return str.replace(/(\r\n|\n|\r)/gm, ' '); -} - -function brToNewLine(str) { - // https://stackoverflow.com/questions/5959415/jquery-javascript-regex-replace-br-with-n - return str.replace(//gi, '\n'); -} - -function substrAfter(str, after) { - let afterIndex = str.indexOf(after); - if (afterIndex >= 0) { - return str.substr(afterIndex + after.length); - } - else { - return null; - } -} - -function substrBefore(str, before) { - let beforeIndex = str.indexOf(before); - if (beforeIndex >= 0) { - return str.substr(0, beforeIndex); - } - else { - return null; - } -} - -function splitUrl(url) { - const _url = new URL(url); - return { - base: _url.protocol + '//' + _url.host, - path: _url.pathname, - query: _url.search, - hash: _url.hash - }; -} - -function getSearchUrl(params = {}) { - const qs = { - q: params.query, - page: params.page || 1, - }; - switch(params.itemType) { - case 'artistsAndLabels': - qs.item_type = 'b'; - break; - case 'albums': - qs.item_type = 'a'; - break; - case 'tracks': - qs.item_type = 't'; - break; - default: - } - return getSiteUrl() + '/search?' + querystring.encode(qs); -} - -function stripMultipleWhitespaces(str) { - return str.replace(/\s+/g, ' '); -} - -function isAbsoluteUrl(url) { - const isAbsolute = new RegExp('^([a-z]+://|//)', 'i'); - return isAbsolute.test(url); -} - -function getAllShowsUrl() { - return getSiteUrl() + '/api/bcweekly/3/list'; -} - -function getShowIdFromUrl(showUrl) { - const _url = splitUrl(showUrl); - let qs = _url.query; - if (qs.startsWith('?')) { - qs = qs.substr(1); - } - const pqs = querystring.parse(qs); - return pqs && pqs.show ? pqs.show : null; -} - -function getShowUrl(showId) { - return getSiteUrl() + '/?show=' + showId; -} - -function getDailyUrl(params = {}) { - let url = params.categoryUrl || 'https://daily.bandcamp.com'; - if (params.page) { - url += '?page=' + params.page; - } - return url; -} - -function getReleasesByTagUrl(tagUrl) { - return `${tagUrl}?tab=all_releases`; -} - -function getDigDeeperUrl() { - return 'https://bandcamp.com/api/hub/2/dig_deeper'; -} - -function getSearchTagUrl() { - return 'https://bandcamp.com/api/fansignup/1/search_tag'; -} - -function getSearchLocationUrl() { - return 'https://bandcamp.com/api/location/1/geoname_search'; -} - -function getFanPageUrl(username) { - return getSiteUrl() + '/' + username; -} - -function getFanCollectionContinuationUrl() { - return 'https://bandcamp.com/api/fancollection/1/collection_items'; -} - -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, - getDiscoverUrl, - reformatImageUrl, - stripTags, - stripLineBreaks, - brToNewLine, - substrAfter, - substrBefore, - splitUrl, - getSearchUrl, - stripMultipleWhitespaces, - isAbsoluteUrl, - getAllShowsUrl, - getShowIdFromUrl, - getShowUrl, - getDailyUrl, - getReleasesByTagUrl, - getDigDeeperUrl, - getSearchTagUrl, - getSearchLocationUrl, - getFanPageUrl, - getFanCollectionContinuationUrl, - getFanWishlistContinuationUrl, - getFanFollowingBandsContinuationUrl, - getFanFollowingGenresContinuationUrl -}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..2f7f6ac --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4188 @@ +{ + "name": "bandcamp-fetch", + "version": "1.0.0-dev", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "bandcamp-fetch", + "version": "1.0.0-dev", + "license": "MIT", + "dependencies": { + "bottleneck": "^2.19.5", + "cheerio": "^1.0.0-rc.5", + "eval5": "^1.4.7", + "html-entities": "^2.0.2", + "node-cache": "^5.1.2", + "node-fetch": "^2.6.9" + }, + "devDependencies": { + "@types/node": "^14.18.38", + "@types/node-fetch": "^2.6.4", + "@typescript-eslint/eslint-plugin": "^5.56.0", + "@typescript-eslint/parser": "^5.56.0", + "eslint": "^8.36.0", + "eslint-plugin-tsdoc": "^0.2.17", + "ts-node": "^10.9.1", + "typedoc": "^0.24.0", + "typedoc-plugin-markdown": "^3.14.0", + "typedoc-plugin-rename-defaults": "^0.6.4", + "typescript": "^4.9.5" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@babel/runtime": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.3.tgz", + "integrity": "sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ==", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.5.2", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz", + "integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@microsoft/tsdoc": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", + "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", + "dev": true + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", + "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.14.2", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/acorn": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", + "integrity": "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "0.0.41", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.41.tgz", + "integrity": "sha512-rIAmXyJlqw4KEBO7+u9gxZZSQHaCNnIzYrnNmYVpgfJhxTqO0brCX0SYpqUTkVI5mwwUwzmtspLBGBKroMeynA==" + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "14.18.47", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.47.tgz", + "integrity": "sha512-OuJi8bIng4wYHHA3YpKauL58dZrPxro3d0tabPHyiNF8rKfGKuVfr83oFlPLmKri1cX+Z3cJP39GXmnqkP11Gw==", + "dev": true + }, + "node_modules/@types/node-fetch": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz", + "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "node_modules/@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.59.6", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.6.tgz", + "integrity": "sha512-sXtOgJNEuRU5RLwPUb1jxtToZbgvq3M6FPpY4QENxoOggK+UpTxUBpj6tD8+Qh2g46Pi9We87E+eHnUw8YcGsw==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.59.6", + "@typescript-eslint/type-utils": "5.59.6", + "@typescript-eslint/utils": "5.59.6", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.59.6", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.6.tgz", + "integrity": "sha512-7pCa6al03Pv1yf/dUg/s1pXz/yGMUBAw5EeWqNTFiSueKvRNonze3hma3lhdsOrQcaOXhbk5gKu2Fludiho9VA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.59.6", + "@typescript-eslint/types": "5.59.6", + "@typescript-eslint/typescript-estree": "5.59.6", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.59.6", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.6.tgz", + "integrity": "sha512-gLbY3Le9Dxcb8KdpF0+SJr6EQ+hFGYFl6tVY8VxLPFDfUZC7BHFw+Vq7bM5lE9DwWPfx4vMWWTLGXgpc0mAYyQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.6", + "@typescript-eslint/visitor-keys": "5.59.6" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.59.6", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.6.tgz", + "integrity": "sha512-A4tms2Mp5yNvLDlySF+kAThV9VTBPCvGf0Rp8nl/eoDX9Okun8byTKoj3fJ52IJitjWOk0fKPNQhXEB++eNozQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.59.6", + "@typescript-eslint/utils": "5.59.6", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.59.6", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.6.tgz", + "integrity": "sha512-tH5lBXZI7T2MOUgOWFdVNUILsI02shyQvfzG9EJkoONWugCG77NDDa1EeDGw7oJ5IvsTAAGVV8I3Tk2PNu9QfA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.59.6", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.6.tgz", + "integrity": "sha512-vW6JP3lMAs/Tq4KjdI/RiHaaJSO7IUsbkz17it/Rl9Q+WkQ77EOuOnlbaU8kKfVIOJxMhnRiBG+olE7f3M16DA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.6", + "@typescript-eslint/visitor-keys": "5.59.6", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.59.6", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.6.tgz", + "integrity": "sha512-vzaaD6EXbTS29cVH0JjXBdzMt6VBlv+hE31XktDRMX1j3462wZCJa7VzO2AxXEXcIl8GQqZPcOPuW/Z1tZVogg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.59.6", + "@typescript-eslint/types": "5.59.6", + "@typescript-eslint/typescript-estree": "5.59.6", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.59.6", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.6.tgz", + "integrity": "sha512-zEfbFLzB9ETcEJ4HZEEsCR9HHeNku5/Qw1jSS5McYJv5BR+ftYXwFFAH5Al+xkGaZEqowMwl7uoJjQb1YSPF8Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.6", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-sequence-parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.0.tgz", + "integrity": "sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ==", + "dev": true + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "node_modules/bottleneck": { + "version": "2.19.5", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz", + "integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.41.0", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-tsdoc": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.17.tgz", + "integrity": "sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.14.2", + "@microsoft/tsdoc-config": "0.16.2" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/espree": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eval5": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/eval5/-/eval5-1.4.7.tgz", + "integrity": "sha512-GtNl5TyxeIE1VkZ8p4vgzeLkdh7UEn9qzLnSIh/aXKYiJc31i1tNGB0CZhPaXReIZIE59xTV39IsAdWY7P93UA==", + "dependencies": { + "@babel/runtime": "^7.8.4", + "@types/acorn": "^4.0.5", + "@types/estree": "0.0.41", + "acorn": "^7.1.0" + } + }, + "node_modules/eval5/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/html-entities": { + "version": "2.0.2", + "integrity": "sha512-4IHIARvqG6F1gACykQUXc24KuAM8vbmKHq738CIVsPNBIp/xqmYFbIEEeA51SdjF+mxk/I52reZMNVK8Kzq9Ig==" + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/node-cache": { + "version": "5.1.2", + "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", + "dependencies": { + "clone": "2.x" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "dependencies": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shiki": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.2.tgz", + "integrity": "sha512-ltSZlSLOuSY0M0Y75KA+ieRaZ0Trf5Wl3gutE7jzLuIcWxLp5i/uEnLoQWNvgKXQ5OMpGkJnVMRLAuzjc0LJ2A==", + "dev": true, + "dependencies": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typedoc": { + "version": "0.24.7", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.7.tgz", + "integrity": "sha512-zzfKDFIZADA+XRIp2rMzLe9xZ6pt12yQOhCr7cD7/PBTjhPmMyMvGrkZ2lPNJitg3Hj1SeiYFNzCsSDrlpxpKw==", + "dev": true, + "dependencies": { + "lunr": "^2.3.9", + "marked": "^4.3.0", + "minimatch": "^9.0.0", + "shiki": "^0.14.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 14.14" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x" + } + }, + "node_modules/typedoc-plugin-markdown": { + "version": "3.15.3", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.15.3.tgz", + "integrity": "sha512-idntFYu3vfaY3eaD+w9DeRd0PmNGqGuNLKihPU9poxFGnATJYGn9dPtEhn2QrTdishFMg7jPXAhos+2T6YCWRQ==", + "dev": true, + "dependencies": { + "handlebars": "^4.7.7" + }, + "peerDependencies": { + "typedoc": ">=0.24.0" + } + }, + "node_modules/typedoc-plugin-rename-defaults": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/typedoc-plugin-rename-defaults/-/typedoc-plugin-rename-defaults-0.6.5.tgz", + "integrity": "sha512-DwkgwRMxgu3UrDR3VUAdnF9jYzM6p7rw6UcVIh4MD7yjEmFDR8WWyOlk6oYgELmRYHxTDx0f0GK6iSgoxSh/Qw==", + "dev": true, + "peerDependencies": { + "typedoc": "0.22.x || 0.23.x || 0.24.x" + } + }, + "node_modules/typedoc/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typedoc/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true + }, + "node_modules/vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@babel/runtime": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.3.tgz", + "integrity": "sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ==", + "requires": { + "regenerator-runtime": "^0.13.11" + } + }, + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + } + }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.5.2", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + } + }, + "@eslint/js": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz", + "integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==", + "dev": true + }, + "@humanwhocodes/config-array": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@microsoft/tsdoc": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", + "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", + "dev": true + }, + "@microsoft/tsdoc-config": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", + "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.14.2", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "@types/acorn": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", + "integrity": "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==", + "requires": { + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "0.0.41", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.41.tgz", + "integrity": "sha512-rIAmXyJlqw4KEBO7+u9gxZZSQHaCNnIzYrnNmYVpgfJhxTqO0brCX0SYpqUTkVI5mwwUwzmtspLBGBKroMeynA==" + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/node": { + "version": "14.18.47", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.47.tgz", + "integrity": "sha512-OuJi8bIng4wYHHA3YpKauL58dZrPxro3d0tabPHyiNF8rKfGKuVfr83oFlPLmKri1cX+Z3cJP39GXmnqkP11Gw==", + "dev": true + }, + "@types/node-fetch": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz", + "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==", + "dev": true, + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.59.6", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.6.tgz", + "integrity": "sha512-sXtOgJNEuRU5RLwPUb1jxtToZbgvq3M6FPpY4QENxoOggK+UpTxUBpj6tD8+Qh2g46Pi9We87E+eHnUw8YcGsw==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.59.6", + "@typescript-eslint/type-utils": "5.59.6", + "@typescript-eslint/utils": "5.59.6", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "@typescript-eslint/parser": { + "version": "5.59.6", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.6.tgz", + "integrity": "sha512-7pCa6al03Pv1yf/dUg/s1pXz/yGMUBAw5EeWqNTFiSueKvRNonze3hma3lhdsOrQcaOXhbk5gKu2Fludiho9VA==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.59.6", + "@typescript-eslint/types": "5.59.6", + "@typescript-eslint/typescript-estree": "5.59.6", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.59.6", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.6.tgz", + "integrity": "sha512-gLbY3Le9Dxcb8KdpF0+SJr6EQ+hFGYFl6tVY8VxLPFDfUZC7BHFw+Vq7bM5lE9DwWPfx4vMWWTLGXgpc0mAYyQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.6", + "@typescript-eslint/visitor-keys": "5.59.6" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.59.6", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.6.tgz", + "integrity": "sha512-A4tms2Mp5yNvLDlySF+kAThV9VTBPCvGf0Rp8nl/eoDX9Okun8byTKoj3fJ52IJitjWOk0fKPNQhXEB++eNozQ==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "5.59.6", + "@typescript-eslint/utils": "5.59.6", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "dependencies": { + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "@typescript-eslint/types": { + "version": "5.59.6", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.6.tgz", + "integrity": "sha512-tH5lBXZI7T2MOUgOWFdVNUILsI02shyQvfzG9EJkoONWugCG77NDDa1EeDGw7oJ5IvsTAAGVV8I3Tk2PNu9QfA==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.59.6", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.6.tgz", + "integrity": "sha512-vW6JP3lMAs/Tq4KjdI/RiHaaJSO7IUsbkz17it/Rl9Q+WkQ77EOuOnlbaU8kKfVIOJxMhnRiBG+olE7f3M16DA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.6", + "@typescript-eslint/visitor-keys": "5.59.6", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "@typescript-eslint/utils": { + "version": "5.59.6", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.6.tgz", + "integrity": "sha512-vzaaD6EXbTS29cVH0JjXBdzMt6VBlv+hE31XktDRMX1j3462wZCJa7VzO2AxXEXcIl8GQqZPcOPuW/Z1tZVogg==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.59.6", + "@typescript-eslint/types": "5.59.6", + "@typescript-eslint/typescript-estree": "5.59.6", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.59.6", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.6.tgz", + "integrity": "sha512-zEfbFLzB9ETcEJ4HZEEsCR9HHeNku5/Qw1jSS5McYJv5BR+ftYXwFFAH5Al+xkGaZEqowMwl7uoJjQb1YSPF8Q==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.6", + "eslint-visitor-keys": "^3.3.0" + } + }, + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-sequence-parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.0.tgz", + "integrity": "sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "bottleneck": { + "version": "2.19.5", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "requires": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + } + }, + "cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "requires": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + } + }, + "clone": { + "version": "2.1.2", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==" + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + }, + "domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "requires": { + "domelementtype": "^2.3.0" + } + }, + "domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "requires": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + } + }, + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz", + "integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.41.0", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "dependencies": { + "eslint-scope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "eslint-plugin-tsdoc": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.17.tgz", + "integrity": "sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.14.2", + "@microsoft/tsdoc-config": "0.16.2" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true + }, + "espree": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "dev": true, + "requires": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + } + }, + "esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "eval5": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/eval5/-/eval5-1.4.7.tgz", + "integrity": "sha512-GtNl5TyxeIE1VkZ8p4vgzeLkdh7UEn9qzLnSIh/aXKYiJc31i1tNGB0CZhPaXReIZIE59xTV39IsAdWY7P93UA==", + "requires": { + "@babel/runtime": "^7.8.4", + "@types/acorn": "^4.0.5", + "@types/estree": "0.0.41", + "acorn": "^7.1.0" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + } + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "html-entities": { + "version": "2.0.2", + "integrity": "sha512-4IHIARvqG6F1gACykQUXc24KuAM8vbmKHq738CIVsPNBIp/xqmYFbIEEeA51SdjF+mxk/I52reZMNVK8Kzq9Ig==" + }, + "htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-core-module": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node-cache": { + "version": "5.1.2", + "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", + "requires": { + "clone": "2.x" + } + }, + "node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "requires": { + "boolbase": "^1.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "requires": { + "entities": "^4.4.0" + } + }, + "parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "requires": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "shiki": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.2.tgz", + "integrity": "sha512-ltSZlSLOuSY0M0Y75KA+ieRaZ0Trf5Wl3gutE7jzLuIcWxLp5i/uEnLoQWNvgKXQ5OMpGkJnVMRLAuzjc0LJ2A==", + "dev": true, + "requires": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "typedoc": { + "version": "0.24.7", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.7.tgz", + "integrity": "sha512-zzfKDFIZADA+XRIp2rMzLe9xZ6pt12yQOhCr7cD7/PBTjhPmMyMvGrkZ2lPNJitg3Hj1SeiYFNzCsSDrlpxpKw==", + "dev": true, + "requires": { + "lunr": "^2.3.9", + "marked": "^4.3.0", + "minimatch": "^9.0.0", + "shiki": "^0.14.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "typedoc-plugin-markdown": { + "version": "3.15.3", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.15.3.tgz", + "integrity": "sha512-idntFYu3vfaY3eaD+w9DeRd0PmNGqGuNLKihPU9poxFGnATJYGn9dPtEhn2QrTdishFMg7jPXAhos+2T6YCWRQ==", + "dev": true, + "requires": { + "handlebars": "^4.7.7" + } + }, + "typedoc-plugin-rename-defaults": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/typedoc-plugin-rename-defaults/-/typedoc-plugin-rename-defaults-0.6.5.tgz", + "integrity": "sha512-DwkgwRMxgu3UrDR3VUAdnF9jYzM6p7rw6UcVIh4MD7yjEmFDR8WWyOlk6oYgELmRYHxTDx0f0GK6iSgoxSh/Qw==", + "dev": true, + "requires": {} + }, + "typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true + }, + "uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "dev": true, + "optional": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true + }, + "vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/package.json b/package.json index 8d94d6e..3cf081a 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,65 @@ { "name": "bandcamp-fetch", - "version": "0.3.1-b.1", - "description": "JS library for scraping Bandcamp content", - "main": "lib/index.js", + "version": "1.0.0", + "description": "Scrape Bandcamp content", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "build": "npm run prepare", + "build:esm": "npx tsc -p tsconfig-esm.json", + "build:cjs": "npx tsc -p tsconfig.json", + "prepare": "rm -rf dist && npm run build:esm && npm run build:cjs && bash fixup.sh", + "lint": "npx eslint ./src && npx eslint ./example", + "lint:fix": "npx eslint ./src --fix && npx eslint ./examples --fix", + "doc": "npx typedoc", + "example": "npx ts-node -P ./tsconfig-esm.json --esm ./example" }, + "main": "./dist/cjs/index-cjs.js", + "module": "./dist/mjs/index.js", + "types": "./dist/mjs/index.d.ts", + "exports": { + ".": { + "import": { + "types": "./dist/mjs/index.d.ts", + "default": "./dist/mjs/index.js" + }, + "require": { + "types": "./dist/cjs/index.d.ts", + "default": "./dist/cjs/index-cjs.js" + } + } + }, + "author": "Patrick Kan (https://github.com/patrickkfkan)", "repository": { "type": "git", "url": "https://github.com/patrickkfkan/bandcamp-fetch.git" }, + "license": "MIT", + "directories": { + "dist": "./dist" + }, + "engines": { + "node": ">=14" + }, + "devDependencies": { + "@types/node": "^14.18.38", + "@types/node-fetch": "^2.6.4", + "@typescript-eslint/eslint-plugin": "^5.56.0", + "@typescript-eslint/parser": "^5.56.0", + "eslint": "^8.36.0", + "eslint-plugin-tsdoc": "^0.2.17", + "ts-node": "^10.9.1", + "typedoc": "^0.24.0", + "typedoc-plugin-markdown": "^3.14.0", + "typedoc-plugin-rename-defaults": "^0.6.4", + "typescript": "^4.9.5" + }, + "dependencies": { + "bottleneck": "^2.19.5", + "cheerio": "^1.0.0-rc.5", + "eval5": "^1.4.7", + "html-entities": "^2.0.2", + "node-cache": "^5.1.2", + "node-fetch": "^2.6.9" + }, "keywords": [ "bandcamp", "scrape", @@ -18,15 +68,5 @@ "album", "track", "artist" - ], - "author": "Patrick Kan", - "license": "MIT", - "dependencies": { - "bottleneck": "^2.19.5", - "cheerio": "^1.0.0-rc.5", - "html-entities": "^2.0.2", - "node-cache": "^5.1.2", - "node-fetch": "^2.6.1", - "safe-eval": "^0.4.1" - } + ] } diff --git a/src/index-cjs.ts b/src/index-cjs.ts new file mode 100644 index 0000000..b7cc269 --- /dev/null +++ b/src/index-cjs.ts @@ -0,0 +1,4 @@ +import { default as main } from './index.js'; +import * as bcfetch from './index.js'; + +export = Object.assign(main, { ...bcfetch }); diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..811b0a2 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,109 @@ +import AlbumAPI, { LimiterAlbumAPI } from './lib/album/AlbumAPI'; +import ArticleAPI, { LimiterArticleAPI } from './lib/article/ArticleAPI'; +import BandAPI, { LimiterBandAPI } from './lib/band/BandAPI'; +import DiscoveryAPI, { LimiterDiscoveryAPI } from './lib/discovery/DiscoveryAPI'; +import FanAPI, { LimiterFanAPI } from './lib/fan/FanAPI'; +import ImageAPI, { LimiterImageAPI } from './lib/image/ImageAPI'; +import ShowAPI, { LimiterShowAPI } from './lib/show/ShowAPI'; +import TagAPI, { LimiterTagAPI } from './lib/tag/TagAPI'; +import TrackAPI, { LimiterTrackAPI } from './lib/track/TrackAPI'; +import SearchAPI, { LimiterSearchAPI } from './lib/search/SearchAPI'; +import AutocompleteAPI, { LimiterAutocompleteAPI } from './lib/autocomplete/AutocompleteAPI'; +import Limiter from './lib/utils/Limiter'; +import { CacheDataType, Cache as CacheImpl } from './lib/utils/Cache'; + +export { default as AlbumAPI } from './lib/album/AlbumAPI'; +export { default as ArticleAPI } from './lib/article/ArticleAPI'; +export { default as BandAPI } from './lib/band/BandAPI'; +export { default as DiscoveryAPI } from './lib/discovery/DiscoveryAPI'; +export { default as FanAPI } from './lib/fan/FanAPI'; +export { default as ImageAPI } from './lib/image/ImageAPI'; +export { default as ShowAPI } from './lib/show/ShowAPI'; +export { default as TagAPI } from './lib/tag/TagAPI'; +export { default as TrackAPI } from './lib/track/TrackAPI'; +export { default as SearchAPI } from './lib/search/SearchAPI'; +export { default as AutocompleteAPI } from './lib/autocomplete/AutocompleteAPI'; + +export * from './lib/album/AlbumAPI'; +export * from './lib/discovery/DiscoveryAPI'; +export * from './lib/image/ImageAPI'; +export * from './lib/article/ArticleAPI'; +export * from './lib/band/BandAPI'; +export * from './lib/fan/FanAPI'; +export * from './lib/show/ShowAPI'; +export * from './lib/tag/TagAPI'; +export * from './lib/track/TrackAPI'; +export * from './lib/album/AlbumAPI'; +export * from './lib/search/SearchAPI'; +export * from './lib/autocomplete/AutocompleteAPI'; + +export * from './lib/types/Album'; +export * from './lib/types/Article'; +export * from './lib/types/Artist'; +export * from './lib/types/Discovery'; +export * from './lib/types/Fan'; +export * from './lib/types/Image'; +export * from './lib/types/Label'; +export * from './lib/types/MediaKind'; +export * from './lib/types/Show'; +export * from './lib/types/Tag'; +export * from './lib/types/Track'; +export * from './lib/types/UserKind'; +export * from './lib/types/Search'; +export * from './lib/types/Autocomplete'; +export { CacheDataType } from './lib/utils/Cache'; + +export { default as Album } from './lib/types/Album'; +export { default as Article } from './lib/types/Article'; +export { default as Artist } from './lib/types/Artist'; +export { default as Fan } from './lib/types/Fan'; +export { default as Label } from './lib/types/Label'; +export { default as MediaKind } from './lib/types/MediaKind'; +export { default as Show } from './lib/types/Show'; +export { default as Tag } from './lib/types/Tag'; +export { default as Track } from './lib/types/Track'; +export { default as UserKind } from './lib/types/UserKind'; +export { default as NameValuePair } from './lib/utils/NameValuePair'; + +export class Cache { + static clear(type?: CacheDataType) { + CacheImpl.clear(type); + } + + static setTTL(type: CacheDataType, ttl: number) { + CacheImpl.setTTL(type, ttl); + } + + static setMaxPages(maxPages: number) { + CacheImpl.setMaxEntries(CacheDataType.Page, maxPages); + } +} + +export default { + album: AlbumAPI, + track: TrackAPI, + discovery: DiscoveryAPI, + image: ImageAPI, + band: BandAPI, + tag: TagAPI, + article: ArticleAPI, + show: ShowAPI, + fan: FanAPI, + search: SearchAPI, + autocomplete: AutocompleteAPI, + limiter: { + album: LimiterAlbumAPI, + track: LimiterTrackAPI, + discovery: LimiterDiscoveryAPI, + image: LimiterImageAPI, + band: LimiterBandAPI, + tag: LimiterTagAPI, + article: LimiterArticleAPI, + show: LimiterShowAPI, + fan: LimiterFanAPI, + search: LimiterSearchAPI, + autocomplete: LimiterAutocompleteAPI, + updateSettings: Limiter.updateSettings.bind(Limiter) + }, + cache: Cache +}; diff --git a/src/lib/album/AlbumAPI.ts b/src/lib/album/AlbumAPI.ts new file mode 100644 index 0000000..43d5ec2 --- /dev/null +++ b/src/lib/album/AlbumAPI.ts @@ -0,0 +1,33 @@ +import Album from '../types/Album'; +import { ImageFormat } from '../types/Image'; +import ImageAPI from '../image/ImageAPI'; +import { fetchPage } from '../utils/Fetch'; +import AlbumInfoParser from './AlbumInfoParser'; +import Limiter from '../utils/Limiter'; + +export interface AlbumAPIGetInfoParams { + albumUrl: string; + albumImageFormat?: string | number | ImageFormat; + artistImageFormat?: string | number | ImageFormat; + includeRawData?: boolean; +} + +export default class AlbumAPI { + static async getInfo(params: AlbumAPIGetInfoParams): Promise { + const imageConstants = await ImageAPI.getConstants(); + const opts = { + imageBaseUrl: imageConstants.baseUrl, + albumImageFormat: await ImageAPI.getFormat(params.albumImageFormat, 9), + artistImageFormat: await ImageAPI.getFormat(params.artistImageFormat, 21), + includeRawData: params.includeRawData !== undefined ? params.includeRawData : false + }; + const html = await fetchPage(params.albumUrl); + return AlbumInfoParser.parseInfo(html, opts); + } +} + +export class LimiterAlbumAPI extends AlbumAPI { + static async getInfo(params: AlbumAPIGetInfoParams): Promise { + return Limiter.schedule(() => super.getInfo(params)); + } +} diff --git a/src/lib/album/AlbumInfoParser.ts b/src/lib/album/AlbumInfoParser.ts new file mode 100644 index 0000000..d7a7e30 --- /dev/null +++ b/src/lib/album/AlbumInfoParser.ts @@ -0,0 +1,147 @@ +import { load as cheerioLoad } from 'cheerio'; +import { decode } from 'html-entities'; +import Album, { AlbumRelease } from '../types/Album'; +import { ImageFormat } from '../types/Image'; +import { ParseError, getAdditionalPropertyValue, normalizeUrl, parseLabelFromBackToLabelLink, parsePublisher, reformatImageUrl } from '../utils/Parse'; +import Track from '../types/Track'; + +interface AlbumInfoParseOptions { + imageBaseUrl: string; + albumImageFormat: ImageFormat | null; + artistImageFormat: ImageFormat | null; + includeRawData: boolean; +} + +export default class AlbumInfoParser { + + static parseInfo(html: string, opts: AlbumInfoParseOptions): Album { + const $ = cheerioLoad(html); + const rawBasic = $('script[type="application/ld+json"]').html(); + const rawExtra = decode($('script[data-tralbum]').attr('data-tralbum')); + + if (!rawBasic || !rawExtra) { + throw new ParseError('Failed to parse album info: data missing \'ld_json\' or \'tralbum\' fields.', html); + } + + let basic, extra; + try { + basic = JSON.parse(rawBasic); + } + catch (error: any) { + throw new ParseError('Failed to parse album info: JSON error in basic data.', rawBasic, error); + } + try { + extra = JSON.parse(rawExtra); + } + catch (error: any) { + throw new ParseError('Failed to parse album info: JSON error in extra data.', rawExtra, error); + } + + if (!basic || typeof basic !== 'object') { + throw new ParseError('Failed to parse album info: invalid basic data'); + } + if (!extra || typeof extra !== 'object') { + throw new ParseError('Failed to parse album info: invalid extra data'); + } + + const artist: Album['artist'] = { + name: basic.byArtist.name + }; + if (basic.byArtist['@id']) { + artist.url = basic.byArtist['@id']; + } + + const album: Album = { + type: 'album', + name: basic.name, + url: basic['@id'], + numTracks: basic.numTracks, + keywords: basic.keywords, + description: basic.description || '', + releaseDate: extra.album_release_date, + artist, + releases: [], + tracks: [] + }; + const imageUrl = reformatImageUrl(basic.image, opts.albumImageFormat); + if (imageUrl) { + album.imageUrl = imageUrl; + } + const label = parseLabelFromBackToLabelLink($); + if (label) { + album.label = { + name: label.name, + url: label.url + }; + } + const publisher = parsePublisher(basic, opts.artistImageFormat); + if (publisher) { + album.publisher = publisher; + } + + if (!artist.url && publisher?.url) { + artist.url = publisher.url; + } + + if (Array.isArray(basic.albumRelease)) { + const releases = basic.albumRelease.filter( + (release: any) => release.musicReleaseFormat).map((release: any) => { + const releaseItem: AlbumRelease = { + name: release.name, + format: release.musicReleaseFormat + }; + if (release.description) { + releaseItem.description = release.description; + } + const releaseUrl = normalizeUrl(release['@id'], album.url); + if (releaseUrl) { + releaseItem.url = releaseUrl; + } + if (release.image) { + if (Array.isArray(release.image) && release.image[0]) { + releaseItem.imageUrl = release.image[0]; + } + } + else { + const releaseImageArtId = getAdditionalPropertyValue(release, 'art_id'); + if (releaseImageArtId && opts.albumImageFormat) { + release.imageUrl = `${opts.imageBaseUrl}/img/a${releaseImageArtId}_${opts.albumImageFormat.id}.jpg`; + } + } + return releaseItem; + }) as AlbumRelease[]; + + if (releases.length > 0) { + album.releases = releases; + } + } + + if (Array.isArray(extra.trackinfo)) { + const tracks = extra.trackinfo.map((track: any) => { + const trackItem: Omit = { + name: track.title, + duration: track.duration, + streamUrl: track.file?.['mp3-128'] + }; + if (track.track_num !== undefined) { + trackItem.position = track.track_num; + } + const trackUrl = normalizeUrl(track.title_link, album.url); + if (trackUrl) { + trackItem.url = trackUrl; + } + return trackItem; + }) as Track[]; + + if (tracks.length > 0) { + album.tracks = tracks; + } + } + + if (opts.includeRawData) { + album.raw = { basic, extra }; + } + + return album; + } +} diff --git a/src/lib/article/ArticleAPI.ts b/src/lib/article/ArticleAPI.ts new file mode 100644 index 0000000..5310406 --- /dev/null +++ b/src/lib/article/ArticleAPI.ts @@ -0,0 +1,66 @@ +import ImageAPI from '../image/ImageAPI'; +import Article, { ArticleCategorySection, ArticleList } from '../types/Article'; +import { ImageFormat } from '../types/Image'; +import { URLS } from '../utils/Constants'; +import { fetchPage } from '../utils/Fetch'; +import Limiter from '../utils/Limiter'; +import { normalizeUrl } from '../utils/Parse'; +import ArticleCategoryParser from './ArticleCategoryParser'; +import ArticleListParser from './ArticleListParser'; +import ArticleParser from './ArticleParser'; + +export interface ArticleAPIGetArticleParams { + articleUrl: string; + albumImageFormat?: string | number | ImageFormat; + artistImageFormat?: string | number | ImageFormat; + includeRawData?: boolean; +} + +export interface ArticleAPIListParams { + categoryUrl?: string; + imageFormat?: string | number | ImageFormat; + page?: number; +} + +export default class ArticleAPI { + + static async getCategories(): Promise { + const html = await fetchPage(URLS.DAILY); + return ArticleCategoryParser.parseCategories(html); + } + + static async getArticle(params: ArticleAPIGetArticleParams): Promise
{ + const imageConstants = await ImageAPI.getConstants(); + const opts = { + imageBaseUrl: imageConstants.baseUrl, + albumImageFormat: await ImageAPI.getFormat(params.albumImageFormat, 9), + artistImageFormat: await ImageAPI.getFormat(params.artistImageFormat, 21), + includeRawData: !!params.includeRawData + }; + const html = await fetchPage(params.articleUrl); + return ArticleParser.parseArticle(html, opts); + } + + static async list(params?: ArticleAPIListParams): Promise { + let url = params?.categoryUrl ? params.categoryUrl : normalizeUrl('latest', URLS.DAILY); + if (params?.page) { + url += `?page=${params.page}`; + } + const opts = { + imageFormat: await ImageAPI.getFormat(params?.imageFormat) + }; + const html = await fetchPage(url); + return ArticleListParser.parseList(html, opts); + } +} + +export class LimiterArticleAPI extends ArticleAPI { + + static async getCategories(): Promise { + return Limiter.schedule(() => super.getCategories()); + } + + static async getArticle(params: ArticleAPIGetArticleParams): Promise
{ + return Limiter.schedule(() => super.getArticle(params)); + } +} diff --git a/src/lib/article/ArticleCategoryParser.ts b/src/lib/article/ArticleCategoryParser.ts new file mode 100644 index 0000000..7415dac --- /dev/null +++ b/src/lib/article/ArticleCategoryParser.ts @@ -0,0 +1,71 @@ +import { load as cheerioLoad } from 'cheerio'; +import { ArticleCategorySection } from '../types/Article'; +import { URLS } from '../utils/Constants'; +import { isAbsoluteUrl, normalizeUrl } from '../utils/Parse'; + +export default class ArticleCategoryParser { + + static parseCategories(html: string): ArticleCategorySection[] { + const $ = cheerioLoad(html); + const dailyUrl = URLS.DAILY; + + const _parseSection = (section: any) => { + const h = section.prev('h2'); + const title = h.length ? h.text() : ''; + const s: ArticleCategorySection = { + name: section.attr('class'), + title, + sections: [], + categories: [] + }; + section.children().each((i: number, c: any) => { + const tag = c.tagName; + c = $(c); + if (tag === 'section') { + const parsed = _parseSection($(c)); + if (parsed !== null && s.sections) { + s.sections.push(parsed); + } + } + else if (tag === 'div') { + c.find('a').each((i: number, a: any) => { + a = $(a); + let url = a.attr('href'); + if (!isAbsoluteUrl(url)) { + url = normalizeUrl(url, dailyUrl); + } + if (s.categories) { + s.categories.push({ + url, + name: a.text() + }); + } + }); + + } + }); + if (s.sections?.length === 0) { + delete s.sections; + } + if (s.categories?.length === 0) { + delete s.categories; + } + if (!s.sections && !s.categories) { + return null; + } + + return s; + }; + + const sections = $('#daily-view-all').children('section'); + const results: ArticleCategorySection[] = []; + sections.each((i, section) => { + const parsed = _parseSection($(section)); + if (parsed !== null) { + results.push(parsed); + } + }); + + return results; + } +} diff --git a/src/lib/article/ArticleListParser.ts b/src/lib/article/ArticleListParser.ts new file mode 100644 index 0000000..f54081c --- /dev/null +++ b/src/lib/article/ArticleListParser.ts @@ -0,0 +1,80 @@ +import { load as cheerioLoad } from 'cheerio'; +import { ArticleCategory, ArticleList, ArticleListItem } from '../types/Article'; +import { URLS } from '../utils/Constants'; +import { isAbsoluteUrl, normalizeUrl, reformatImageUrl, stripLineBreaks } from '../utils/Parse'; +import { ImageFormat } from '../types/Image'; + +interface ArticleListParseOptions { + imageFormat: ImageFormat | null; +} + +export default class ArticleListParser { + + static parseList(html: string, opts: ArticleListParseOptions): ArticleList { + const $ = cheerioLoad(html); + const dailyUrl = URLS.DAILY; + const result: ArticleList = { + articles: [], + total: 0, + start: 0, + end: 0 + }; + + $('articles-list').each((i, list) => { + $('.list-article', $(list)).each((i: number, article: any) => { + article = $(article); + const imgSrc = article.find('img').attr('src') || null; + // Category + const infoText = article.find('.article-info-text'); + const infoTextCategoryLink = infoText.find('a.franchise'); + const infoTextMiddot = infoText.find('.middot'); + const categoryName = infoTextCategoryLink.text(); + const category: ArticleCategory | null = categoryName ? { + name: categoryName + } : null; + const categoryUrl = infoTextCategoryLink.attr('href'); + if (category && categoryUrl) { + category.url = isAbsoluteUrl(categoryUrl) ? categoryUrl : normalizeUrl(categoryUrl, dailyUrl); + } + // Date + infoTextCategoryLink.remove(); + infoTextMiddot.remove(); + const date = stripLineBreaks(infoText.text()).trim(); + // Title and url + const titleLink = article.find('a.title'); + const title = titleLink.text(); + let url = titleLink.attr('href'); + if (!isAbsoluteUrl(url)) { + url = normalizeUrl(url, dailyUrl); + } + + if (titleLink) { + const parsed: ArticleListItem = { + url, + title, + date + }; + if (category) { + parsed.category = category; + } + const imageUrl = reformatImageUrl(imgSrc, opts.imageFormat); + if (imageUrl) { + parsed.imageUrl = imageUrl; + } + + result.articles.push(parsed); + } + }); + }); + + const resultsText = stripLineBreaks($('#num-results').text()).trim(); + const rtm = resultsText.match(/(\d+)(?:\s*to\s*)(\d+)(?:\s*of\s*)(\d+)/); + if (rtm?.length === 4) { + result.total = parseInt(rtm[3], 10); + result.start = parseInt(rtm[1], 10); + result.end = parseInt(rtm[2], 10); + } + + return result; + } +} diff --git a/src/lib/article/ArticleParser.ts b/src/lib/article/ArticleParser.ts new file mode 100644 index 0000000..297dddc --- /dev/null +++ b/src/lib/article/ArticleParser.ts @@ -0,0 +1,209 @@ +import { load as cheerioLoad } from 'cheerio'; +import { decode } from 'html-entities'; +import Article, { ArticleMediaItem, ArticleSection } from '../types/Article'; +import { URLS } from '../utils/Constants'; +import { ParseError, brToNewLine, isAbsoluteUrl, normalizeUrl, stripTags } from '../utils/Parse'; +import { ImageFormat } from '../types/Image'; +import Album from '../types/Album'; +import { EOL } from 'os'; + +interface ArticleParseOptions { + imageBaseUrl: string; + albumImageFormat: ImageFormat | null; + artistImageFormat: ImageFormat | null; + includeRawData: boolean +} + +export default class ArticleParser { + + static parseArticle(html: string, opts: ArticleParseOptions): Article { + const $ = cheerioLoad(html); + const ldJson = $('script[type="application/ld+json"]').html(); + if (!ldJson) { + throw new ParseError('Failed to parse article: missing JSON data.', html); + } + let basic, players; + try { + basic = JSON.parse(ldJson); + } + catch (error: any) { + throw new ParseError('Failed to parse article: JSON error in basic info.', html, error); + } + try { + players = JSON.parse(decode($('#p-daily-article').attr('data-player-infos'))); + } + catch (error: any) { + players = null; + } + + const article: Article = { + title: basic.headline, + description: basic.description, + url: basic['@id'], + imageUrl: basic.image, + date: basic.datePublished, + category: { + name: basic.articleSection + }, + author: { + name: basic.author.name, + url: basic.author['@id'] + }, + mediaItems: [], + sections: [] + }; + + // Get genre + const genreLink = $('.genre a'); + if (genreLink.length > 0) { + article.genre = { + name: genreLink.text(), + url: genreLink.attr('href') + }; + + const genreReadMoreLink = $('.moreingenre a')?.attr('href'); + if (genreReadMoreLink) { + article.genre.readMoreUrl = isAbsoluteUrl(genreReadMoreLink) ? genreReadMoreLink : normalizeUrl(genreReadMoreLink, URLS.DAILY); + } + } + + // Get category url + const categoryLink = $('article-type a')?.attr('href'); + if (categoryLink) { + article.category.url = isAbsoluteUrl(categoryLink) ? categoryLink : normalizeUrl(categoryLink, URLS.DAILY); + } + + // Get media items (albums and tracks featured in article) + if (Array.isArray(players)) { + players.forEach((player) => { + let mediaItemType: 'album' | 'track' | null; + switch (player.parent_tralbum_type) { + case 'a': + mediaItemType = 'album'; + break; + case 't': + mediaItemType = 'track'; + break; + default: + mediaItemType = null; + } + if (mediaItemType) { + const mediaItem: ArticleMediaItem = { + type: mediaItemType, + name: player.title, + url: player.tralbum_url, + imageUrl: '', + featuredTrackPosition: player.featured_track_number, + artist: { + name: player.band_name, + url: player.band_url, + imageUrl: '', + location: player.band_location + }, + tracks: [], + mediaItemRef: player.player_id + }; + if (player.parent_tralbum_type === 'a') { + mediaItem.type = 'album'; + } + else if (player.parent_tralbum_type === 't') { + mediaItem.type = 'track'; + } + if (player.art_id && opts.albumImageFormat?.id) { + mediaItem.imageUrl = `${opts.imageBaseUrl}/img/a${player.art_id}_${opts.albumImageFormat.id}.jpg`; + } + if (player.band_image_id && mediaItem.artist && opts.artistImageFormat?.id) { + mediaItem.artist.imageUrl = `${opts.imageBaseUrl}/img/${player.band_image_id}_${opts.artistImageFormat.id}.jpg`; + } + if (mediaItemType === 'album' && Array.isArray(player.tracklist)) { + (mediaItem as Album).tracks = player.tracklist.map((trackInfo: any) => ({ + position: trackInfo.track_number, + name: trackInfo.track_title, + duration: trackInfo.audio_track_duration, + streamUrl: trackInfo.audio_url?.['mp3-128'] + })); + } + + article.mediaItems.push(mediaItem); + } + }); + } + + // Function that returns a section corresponding to a media item + const _getSectionByPlayer = (player: any) => { + const section: ArticleSection = { + html: '', + text: '' + }; + + // Get heading + const heading = player.prevUntil('bamplayer-art', 'h3, h2').first(); + if (heading.length > 0) { + section.heading = { + html: heading.html(), + text: stripTags(brToNewLine(heading.html())).trim() + }; + } + + // Get html and text + const paragraphs = player.nextUntil('bamplayer-art, h3, h5, article-end', 'p'); + paragraphs.each((i: number, p: any) => { + p = $(p); + section.html += (section.html !== '' ? EOL : '') + p.html(); + section.text += (section.text !== '' ? EOL + EOL : '') + p.text(); + }); + + // Get mediaItemRef + const playerIdMatch = player.attr('data-bind').match(/playerMap\["(.+?)"]/); + if (playerIdMatch?.[1]) { + section.mediaItemRef = playerIdMatch[1]; + } + + return section; + }; + + // Function that returns the introductory paragraph(s) of the article + const _getIntroSection = (articleBody: any) => { + const firstPlayer = articleBody.find('bamplayer-art').first(); + const paragraphs = firstPlayer.length > 0 ? firstPlayer.prevAll('p') : articleBody.find('p'); + if (paragraphs.length > 0) { + const section = { + html: '', + text: '' + }; + paragraphs.each((i: number, p: any) => { + p = $(p); + section.html += (section.html !== '' ? EOL : '') + p.html(); + section.text += (section.text !== '' ? EOL + EOL : '') + p.text(); + }); + return section; + } + + return null; + + }; + + // Sections + const articleBody = $('#p-daily-article article'); + const sections = []; + const introSection = _getIntroSection(articleBody); + if (introSection) { + sections.push(introSection); + } + const bcplayers = articleBody.find('bamplayer-art'); + bcplayers.each((i, player) => { + sections.push(_getSectionByPlayer($(player))); + }); + article.sections = sections; + + if (opts.includeRawData) { + article.raw = { + basic, + mediaItems: players, + body: articleBody.html() || '' + }; + } + + return article; + } +} diff --git a/src/lib/autocomplete/AutocompleteAPI.ts b/src/lib/autocomplete/AutocompleteAPI.ts new file mode 100644 index 0000000..ae42794 --- /dev/null +++ b/src/lib/autocomplete/AutocompleteAPI.ts @@ -0,0 +1,61 @@ +import { URLS } from '../utils/Constants'; +import { FetchMethod, fetchPage } from '../utils/Fetch'; +import { AutoCompleteTag, AutocompleteLocation } from '../types/Autocomplete'; +import AutocompleteResultsParser from './AutocompleteResultsParser'; +import Limiter from '../utils/Limiter'; + +export enum AutocompleteItemType { + Tag = 'Tag', + Location = 'Location' +} + +export interface AutocompleteAPIGetSuggestionsParams { + query: string; + itemType: AutocompleteItemType; + limit?: number; +} + +export default class AutocompleteAPI { + + static getSuggestions(params: AutocompleteAPIGetSuggestionsParams & { itemType: AutocompleteItemType.Location }): Promise; + static getSuggestions(params: AutocompleteAPIGetSuggestionsParams & { itemType: AutocompleteItemType.Tag }): Promise; + static getSuggestions(params: AutocompleteAPIGetSuggestionsParams): Promise; + static getSuggestions(params: AutocompleteAPIGetSuggestionsParams): Promise { + if (params.itemType === AutocompleteItemType.Tag) { + return this.#getAutocompleteTags(params); + } + + return this.#getAutocompleteLocations(params); + } + + static async #getAutocompleteTags(params: AutocompleteAPIGetSuggestionsParams) { + const payload = { + search_term: params.query, + count: params.limit || 5 + }; + + const json = await fetchPage(URLS.AUTOCOMPLETE.TAG, true, FetchMethod.POST, payload); + return AutocompleteResultsParser.parseTags(json); + } + + static async #getAutocompleteLocations(params: AutocompleteAPIGetSuggestionsParams) { + const payload = { + q: params.query, + n: params.limit || 5, + geocoder_fallback: true + }; + + const json = await fetchPage(URLS.AUTOCOMPLETE.LOCATION, true, FetchMethod.POST, payload); + return AutocompleteResultsParser.parseLocations(json); + } +} + +export class LimiterAutocompleteAPI extends AutocompleteAPI { + + static getSuggestions(params: AutocompleteAPIGetSuggestionsParams & { itemType: AutocompleteItemType.Location; }): Promise; + static getSuggestions(params: AutocompleteAPIGetSuggestionsParams & { itemType: AutocompleteItemType.Tag; }): Promise; + static getSuggestions(params: AutocompleteAPIGetSuggestionsParams): Promise; + static getSuggestions(params: AutocompleteAPIGetSuggestionsParams): Promise { + return Limiter.schedule(() => super.getSuggestions(params)); + } +} diff --git a/src/lib/autocomplete/AutocompleteResultsParser.ts b/src/lib/autocomplete/AutocompleteResultsParser.ts new file mode 100644 index 0000000..14feb75 --- /dev/null +++ b/src/lib/autocomplete/AutocompleteResultsParser.ts @@ -0,0 +1,31 @@ +import { AutoCompleteTag, AutocompleteLocation } from '../types/Autocomplete'; +import { ParseError } from '../utils/Parse'; + +export default class AutocompleteResultsParser { + + static parseTags(json: any): AutoCompleteTag[] { + if (Array.isArray(json?.matching_tags)) { + const arr = json.matching_tags as Array; + return arr.map((tagData: any) => ({ + type: 'tag', + count: tagData.count, + value: tagData.tag_norm_name, + name: tagData.tag_name + })); + } + throw new ParseError('Failed to parse autocomplete tags: JSON invalid or missing \'matching_tags\'.', json); + } + + static parseLocations(json: any): AutocompleteLocation[] { + if (Array.isArray(json?.results)) { + const arr = json.results as Array; + return arr.map((locationData: any) => ({ + type: 'location', + value: parseInt(locationData.id, 10), + name: locationData.name, + fullName: locationData.fullname + })); + } + throw new ParseError('Failed to parse autocomplete locations: JSON invalid or missing \'results\'.', json); + } +} diff --git a/src/lib/band/BandAPI.ts b/src/lib/band/BandAPI.ts new file mode 100644 index 0000000..03da318 --- /dev/null +++ b/src/lib/band/BandAPI.ts @@ -0,0 +1,137 @@ +import ImageAPI from '../image/ImageAPI'; +import Album from '../types/Album'; +import Artist from '../types/Artist'; +import { ImageFormat } from '../types/Image'; +import Label, { LabelArtist } from '../types/Label'; +import Track from '../types/Track'; +import { fetchPage } from '../utils/Fetch'; +import Limiter from '../utils/Limiter'; +import { normalizeUrl } from '../utils/Parse'; +import BandInfoParser from './BandInfoParser'; +import DiscographyParser from './DiscographyParser'; +import LabelArtistsParser from './LabelArtistsParser'; + +export interface BandAPIGetDiscographyParams { + bandUrl: string; + imageFormat?: string | number | ImageFormat; +} + +export interface BandAPIGetInfoParams { + bandUrl: string; + imageFormat?: string | number | ImageFormat; + labelId?: number; +} + +export interface BandAPIGetLabelArtistsParams { + labelUrl: string; + imageFormat?: string | number | ImageFormat; +} + +export default class BandAPI { + + static async getDiscography(params: BandAPIGetDiscographyParams): Promise> { + const imageConstants = await ImageAPI.getConstants(); + const opts = { + imageBaseUrl: imageConstants.baseUrl, + bandUrl: params.bandUrl, + imageFormat: await ImageAPI.getFormat(params.imageFormat, 9) + }; + const html = await fetchPage(normalizeUrl('music', params.bandUrl)); + return DiscographyParser.parseDiscography(html, opts); + } + + static async getInfo(params: BandAPIGetInfoParams): Promise { + const opts = { + bandUrl: params.bandUrl, + imageFormat: await ImageAPI.getFormat(params.imageFormat, 21) + }; + + const url = this.#getUrl(params.bandUrl); + const html = await fetchPage(url); + const result = BandInfoParser.parseInfo(html, opts); + // Return if result is complete + if (this.#isInfoComplete(result)) { + return result; + } + + // Info lacking name or label (for artist) - try getting them from music page + const musicUrl = this.#getUrl(params.bandUrl, 'music'); + const musicHtml = await fetchPage(musicUrl); + const info = BandInfoParser.parseInfo(musicHtml, opts); + this.#fillInfo(result, info); + // Return if result is complete + if (this.#isInfoComplete(result)) { + return result; + } + + // Info is still lacking name or label (for artist) - last try with fetching + // From discog's first album or track + const discogItems = await this.getDiscography(params); + if (discogItems[0]) { + const url = discogItems[0]?.url; + if (url) { + const html = await fetchPage(url); + const info = BandInfoParser.parseInfo(html, opts); + this.#fillInfo(result, info); + } + } + + if (result.url === null) { + result.url = params.bandUrl; + } + + return result; + } + + static async getLabelArtists(params: BandAPIGetLabelArtistsParams): Promise { + const opts = { + labelUrl: params.labelUrl, + imageFormat: await ImageAPI.getFormat(params.imageFormat) + }; + + const html = await fetchPage(normalizeUrl('artists', params.labelUrl)); + return LabelArtistsParser.parseLabelArtists(html, opts); + } + + static #getUrl(artistOrLabelUrl: string, path?: string, labelId?: string): string { + let url = path ? normalizeUrl(path, artistOrLabelUrl) : artistOrLabelUrl; + if (labelId) { + url += `/?label=${encodeURIComponent(labelId)}`; + } + return url; + } + + static #isInfoComplete(data: Artist | Label) { + return data.name && data.url && + (data.type === 'label' || data.label); + } + + static #fillInfo(target: T, src: T): T { + if (target.name === null) { + target.name = src.name; + } + if (target.url === null) { + target.url = src.url; + } + if (target.type === 'artist' && src.type === 'artist' && !target.label && src.label) { + target.label = src.label; + } + + return target; + } +} + +export class LimiterBandAPI extends BandAPI { + + static async getDiscography(params: BandAPIGetDiscographyParams): Promise<(Album | Track)[]> { + return Limiter.schedule(() => super.getDiscography(params)); + } + + static async getInfo(params: BandAPIGetInfoParams): Promise { + return Limiter.schedule(() => super.getInfo(params)); + } + + static async getLabelArtists(params: BandAPIGetLabelArtistsParams): Promise { + return Limiter.schedule(() => super.getLabelArtists(params)); + } +} diff --git a/src/lib/band/BandInfoParser.ts b/src/lib/band/BandInfoParser.ts new file mode 100644 index 0000000..0472a14 --- /dev/null +++ b/src/lib/band/BandInfoParser.ts @@ -0,0 +1,77 @@ +import { load as cheerioLoad } from 'cheerio'; +import { decode } from 'html-entities'; +import Artist from '../types/Artist'; +import { ImageFormat } from '../types/Image'; +import Label from '../types/Label'; +import { ParseError, brToNewLine, parseLabelFromBackToLabelLink, reformatImageUrl, stripLineBreaks, stripTags } from '../utils/Parse'; + +interface BandInfoParseOptions { + bandUrl: string; + imageFormat: ImageFormat | null; +} + +export default class BandInfoParser { + static parseInfo(html: string, opts: BandInfoParseOptions): Artist | Label { + const $ = cheerioLoad(html); + let bandData; + try { + bandData = JSON.parse(decode($('script[data-band]').attr('data-band'))); + } + catch (error: any) { + throw new ParseError('Failed to parse artist / label info: JSON error in band data.', html, error); + } + + const bioText = $('#bio-text'); + let description; + if (bioText.length) { + const bioTextMore = bioText.find('.peekaboo-text'); + if (bioTextMore.length) { + bioTextMore.find('.lightweightBreak').remove(); + bioText.find('.peekaboo-text, .peekaboo-link').remove(); + description = (`${bioText.html()?.trim()} ${bioTextMore.html()}`).trim(); + } + else { + description = bioText.html()?.trim(); + } + if (description) { + description = stripLineBreaks(description); + description = brToNewLine(description); + description = stripTags(description); + description = decode(description); + } + } + + const isLabel = bandData.is_label; + const result: Artist | Label = { + type: isLabel ? 'label' : 'artist', + name: bandData.name, + description: description + }; + + if (bandData.url) { + result.url = bandData.url; + } + + const location = $('#band-name-location').find('.location').text(); + if (location) { + result.location = location; + } + + const imageUrl = reformatImageUrl($('img.band-photo').attr('src'), opts.imageFormat); + if (imageUrl) { + result.imageUrl = imageUrl; + } + + if (!isLabel) { + const label = parseLabelFromBackToLabelLink($); + if (label) { + (result as Artist).label = label; + } + } + else { + (result as Label).labelId = bandData.id; + } + + return result; + } +} \ No newline at end of file diff --git a/src/lib/band/DiscographyParser.ts b/src/lib/band/DiscographyParser.ts new file mode 100644 index 0000000..ac88dfe --- /dev/null +++ b/src/lib/band/DiscographyParser.ts @@ -0,0 +1,155 @@ +import { load as cheerioLoad } from 'cheerio'; +import { ImageFormat } from '../types/Image'; +import { normalizeUrl, reformatImageUrl, splitUrl } from '../utils/Parse'; +import Track from '../types/Track'; +import Album from '../types/Album'; +import TrackInfoParser from '../track/TrackInfoParser'; +import AlbumInfoParser from '../album/AlbumInfoParser'; + +interface DiscographyParseOptions { + imageBaseUrl: string; + bandUrl: string; + imageFormat: ImageFormat | null; +} + +export default class DiscographyParser { + static parseDiscography(html: string, opts: DiscographyParseOptions): Array { + const $ = cheerioLoad(html); + + // One-album / one-track artists don't have a discography page. + // The page for the album or track will be loaded instead. + // Check if this is the case and handle accordingly. + const currentAlbumOrTrack = $('script[type="application/ld+json"]'); + let isOneTrack = false, + isOneAlbum = false; + if (currentAlbumOrTrack.length) { + let currentAlbumOrTrackData; + const currentAlbumOrTrackHtml = currentAlbumOrTrack.html(); + if (currentAlbumOrTrackHtml) { + try { + currentAlbumOrTrackData = JSON.parse(currentAlbumOrTrackHtml); + } + catch (error: any) { + currentAlbumOrTrackData = null; + } + } + if (currentAlbumOrTrackData && typeof currentAlbumOrTrackData === 'object') { + // Check if there is a 'discography' element and, if there is, whether + // It is hidden or has only one track / album child + const discographyEl = $('#discography'); + if (discographyEl.length === 0 || discographyEl.css('display') === 'none' || discographyEl.find('li').length === 1) { + const currentAlbumOrTrackUrl = splitUrl(currentAlbumOrTrackData['@id']); + isOneTrack = !!currentAlbumOrTrackUrl.path.startsWith('/track/'); + isOneAlbum = !!currentAlbumOrTrackUrl.path.startsWith('/album/'); + } + } + } + if (isOneTrack || isOneAlbum) { + const newOpts = { + imageBaseUrl: opts.imageBaseUrl, + albumImageFormat: opts.imageFormat, + artistImageFormat: null, + includeRawData: false + }; + const info = isOneTrack ? TrackInfoParser.parseInfo(html, newOpts) : AlbumInfoParser.parseInfo(html, newOpts); + if (info.artist !== undefined) { + return [ { + ...info, + artist: { + name: info.artist.name + } + } ]; + } + return [ info ]; + } + + const allLinks = $('a'); + const items: Record> = {}; + const defaultArtistName = $('#band-name-location').find('.title').text(); + allLinks.each((index, link) => { + const linkEl = $(link); + const href = linkEl.attr('href'); + if (typeof href !== 'string' || href === '') { + return true; + } + let host, pathname; + // Regex taken from: + // https://github.com/masterT/bandcamp-scraper/blob/master/lib/htmlParser.js + if ((/^\/(track|album)\/(.+)$/).exec(href)) { // Relative url starting with '/track' or '/album' + host = opts.bandUrl; + pathname = href; + } + else { // Full url (label discography) + try { + const _url = splitUrl(href); + if (_url.path && (/^\/(track|album)\/(.+)$/).exec(_url.path)) { + host = _url.base; + pathname = _url.path; + } + } + catch (e) { + return true; + } + } + if (host !== undefined && pathname !== undefined) { + const url = normalizeUrl(pathname, host); + if (items[url] === undefined) { + items[url] = { + type: pathname.startsWith('/track/') ? 'track' : 'album' + }; + } + // Link element wraps around img and title + const img = linkEl.find('img'); + if (img.length) { + const imgSrc = img.attr('data-original') || img.attr('src'); + const imageUrl = reformatImageUrl(imgSrc, opts.imageFormat); + if (imageUrl) { + items[url].imageUrl = imageUrl; + } + } + const title = linkEl.find('.title'); + if (title.length) { + // For labels, title element contains artist name (when it doesn't, then artist = label). + // For artists, title element may also contain an artist name which overrides the default + const artistNameEl = title.find('.artist-override'); + if (artistNameEl.length) { + const artistName = artistNameEl.text().trim(); + artistNameEl.remove(); + items[url].artist = { + name: artistName + }; + } + else { + items[url].artist = { + name: defaultArtistName + }; + } + items[url].name = title.text().trim(); + } + + if (img.length > 0 && title.length > 0) { + items[url].name = linkEl.text().trim(); + } + } + }); + const results = []; + for (const [ url, props ] of Object.entries(items)) { + if (props.type && props.name) { + const item: Album | Track = { + url, + type: props.type, + name: props.name, + artist: props.artist || { + name: defaultArtistName + } + }; + if (props.imageUrl) { + item.imageUrl = props.imageUrl; + } + results.push(item); + } + } + + return results; + } +} \ No newline at end of file diff --git a/src/lib/band/LabelArtistsParser.ts b/src/lib/band/LabelArtistsParser.ts new file mode 100644 index 0000000..fa038b7 --- /dev/null +++ b/src/lib/band/LabelArtistsParser.ts @@ -0,0 +1,33 @@ +import { load as cheerioLoad } from 'cheerio'; +import { ImageFormat } from '../types/Image'; +import { reformatImageUrl, splitUrl } from '../utils/Parse'; +import { LabelArtist } from '../types/Label'; + +interface LabelArtistsParseOptions { + labelUrl: string; + imageFormat: ImageFormat | null; +} + +export default class LabelArtistsParser { + static parseLabelArtists(html: string, opts: LabelArtistsParseOptions): LabelArtist[] { + const $ = cheerioLoad(html); + const artistsList = $('li.featured-item, li.artists-grid-item'); + const results: LabelArtist[] = []; + artistsList.each((i: number, artistListItem: any) => { + artistListItem = $(artistListItem); + const img = artistListItem.find('img'); + const imgSrc = img.attr('data-original') || img.attr('src'); + const imageUrl = reformatImageUrl(imgSrc, opts.imageFormat); + const artist: LabelArtist = { + name: artistListItem.find('.featured-grid-name, .artists-grid-name').text(), + url: splitUrl(artistListItem.find('a').attr('href')).base, + location: artistListItem.find('.artists-grid-location').text() + }; + if (imageUrl) { + artist.imageUrl = imageUrl; + } + results.push(artist); + }); + return results; + } +} diff --git a/src/lib/discovery/DiscoverOptionsParser.ts b/src/lib/discovery/DiscoverOptionsParser.ts new file mode 100644 index 0000000..5c1e76c --- /dev/null +++ b/src/lib/discovery/DiscoverOptionsParser.ts @@ -0,0 +1,60 @@ +import { load as cheerioLoad } from 'cheerio'; +import { DiscoverOptions } from '../types/Discovery'; +import { ParseError } from '../utils/Parse'; + +export default class DiscoverOptionsParser { + static parseOptions(html: string): DiscoverOptions { + const $ = cheerioLoad(html); + const blob = $('#pagedata[data-blob]').attr('data-blob'); + if (!blob) { + throw new ParseError('Failed to parse discover options: blob not found in data.', html); + } + let parsed; + try { + parsed = JSON.parse(blob); + } + catch (error: any) { + throw new ParseError('Failed to parse discover options: JSON error.', blob, error); + } + const options = parsed?.discover_2015?.options; + if (options && typeof options === 'object') { + const result: DiscoverOptions = { + genres: [], + subgenres: {}, + sortBys: [], + artistRecommendationTypes: [], + locations: [], + formats: [], + times: [] + }; + if (Array.isArray(options.r)) { + result.artistRecommendationTypes = options.r.map((r: any) => ({ name: r.name, value: r.value })); + } + if (Array.isArray(options.l)) { + result.locations = options.l.map((l: any) => ({ name: l.name, value: l.value })); + } + if (Array.isArray(options.w)) { + result.times = options.w.map((w: any) => ({ name: w.name, value: w.value, title: w.title })); + } + if (Array.isArray(options.f)) { + result.formats = options.f.map((f: any) => ({ name: f.name, value: f.value })); + } + if (Array.isArray(options.s)) { + result.sortBys = options.s.map((s: any) => ({ name: s.name, value: s.value })); + } + if (typeof options.t === 'object') { + for (const [ genre, subgenres ] of Object.entries(options.t)) { + if (Array.isArray(subgenres)) { + result.subgenres[genre] = subgenres.map((sg: any) => ({ name: sg.name, value: sg.value })); + } + } + } + if (Array.isArray(options.g)) { + result.genres = options.g.map((g: any) => ({ name: g.name, value: g.value })); + } + return result; + } + + throw new ParseError('Failed to parse discover options: blob is missing or has invalid \'discover_2015.options\' field.', parsed); + } +} diff --git a/src/lib/discovery/DiscoverResultParser.ts b/src/lib/discovery/DiscoverResultParser.ts new file mode 100644 index 0000000..f9c4ed9 --- /dev/null +++ b/src/lib/discovery/DiscoverResultParser.ts @@ -0,0 +1,61 @@ +import Album from '../types/Album'; +import Artist from '../types/Artist'; +import { DiscoverParams, DiscoverResult } from '../types/Discovery'; +import { ImageFormat } from '../types/Image'; +import { ParseError } from '../utils/Parse'; + +interface DiscoverResultParseOptions { + imageBaseUrl: string; + albumImageFormat: ImageFormat | null; + artistImageFormat: ImageFormat | null; +} + +export default class DiscoverResultParser { + + static parseDiscoverResult(json: any, opts: DiscoverResultParseOptions, resultParams: DiscoverParams): DiscoverResult { + if (typeof json === 'object' && Array.isArray(json.items)) { + const items = json.items.filter( + (item: any) => item.type === 'a').map((item: any) => { + const artist: Artist = { + type: 'artist', + name: item.secondary_text + }; + const album: Album = { + type: 'album', + name: item.primary_text, + genre: item.genre_text, + artist, + location: item.location_text + }; + if (item.url_hints) { + artist.url = `https://${item.url_hints.subdomain}.bandcamp.com`; + } + if (artist.url) { + album.url = `${artist.url}/album/${item.url_hints.slug}`; + } + if (item.art_id && opts.albumImageFormat) { + album.imageUrl = `${opts.imageBaseUrl}/img/a${item.art_id}_${opts.albumImageFormat.id}.jpg`; + } + if (item.featured_track) { + album.featuredTrack = { + name: item.featured_track.title, + duration: item.featured_track.duration, + streamUrl: item.featured_track.file?.['mp3-128'] + }; + } + if (item.bio_image && opts.artistImageFormat) { + artist.imageUrl = `${opts.imageBaseUrl}/img/${item.bio_image.image_id}_${opts.artistImageFormat.id}.jpg`; + } + return album; + }) as Album[]; + + return { + items, + total: json.total_count, + params: resultParams + }; + } + + throw new ParseError('Failed to parse discover results: data is missing or has invalid \'items\' field.', json); + } +} diff --git a/src/lib/discovery/DiscoveryAPI.ts b/src/lib/discovery/DiscoveryAPI.ts new file mode 100644 index 0000000..3c8f1ba --- /dev/null +++ b/src/lib/discovery/DiscoveryAPI.ts @@ -0,0 +1,142 @@ +import ImageAPI from '../image/ImageAPI'; +import { DiscoverOptions, DiscoverParams, DiscoverResult } from '../types/Discovery'; +import { Cache, CacheDataType } from '../utils/Cache'; +import { URLS } from '../utils/Constants'; +import { FetchMethod, fetchPage } from '../utils/Fetch'; +import Limiter from '../utils/Limiter'; +import NameValuePair from '../utils/NameValuePair'; +import DiscoverOptionsParser from './DiscoverOptionsParser'; +import DiscoverResultParser from './DiscoverResultParser'; + +interface DiscoverRequestPayload { + s: string; // SortyBy + p: number; // Page + g?: string; // Genre + t?: string; // Subgenre + gn?: string; // Location + f?: string; // Format + r?: string; // Artist recommedation type + w?: number; // Time +} + +export default class DiscoveryAPI { + + static async getAvailableOptions(): Promise { + return Cache.getOrSet(CacheDataType.Constants, 'discoverOptions', async () => { + const html = await fetchPage(URLS.SITE_URL); + return DiscoverOptionsParser.parseOptions(html); + }); + } + + static async sanitizeDiscoverParams(params?: DiscoverParams): Promise { + const options = await this.getAvailableOptions(); + + const _getOptionValue = (optArr: NameValuePair[], value?: T, defaultIndex = 0) => { + if (value !== undefined && optArr) { + const opt = optArr.find((o) => o.value == value); + if (opt) { + return opt.value; + } + } + if (optArr) { + return optArr[defaultIndex].value; + } + + return undefined; + + }; + + const sanitized: DiscoverParams = { + genre: _getOptionValue(options.genres, params?.genre), + sortBy: _getOptionValue(options.sortBys, params?.sortBy), + page: params?.page || 0 + }; + + if (sanitized.sortBy !== 'rec' && sanitized.genre) { + // Following only available when sortBy is not 'rec' (artist-recommend) + const subgenreOptions = options.subgenres[sanitized.genre]; + if (subgenreOptions) { // `false` if genre is 'all' + sanitized.subgenre = _getOptionValue(subgenreOptions, params?.subgenre); + } + // 'Time' option only available when there is effectively no subgenre (e.g. genre is 'all' or subgenre is 'all-metal') + const timeAllowed = sanitized.subgenre === undefined || sanitized.subgenre == subgenreOptions[0].value; + if (timeAllowed) { + sanitized.time = _getOptionValue(options.times, params?.time, 1); + } + sanitized.location = _getOptionValue(options.locations, params?.location); + sanitized.format = _getOptionValue(options.formats, params?.format); + } + else { + sanitized.artistRecommendationType = _getOptionValue(options.artistRecommendationTypes, params?.artistRecommendationType); + } + + return sanitized; + } + + static async discover(params?: DiscoverParams): Promise { + const imageConstants = await ImageAPI.getConstants(); + const opts = { + imageBaseUrl: imageConstants.baseUrl, + albumImageFormat: await ImageAPI.getFormat(params?.albumImageFormat, 9), + artistImageFormat: await ImageAPI.getFormat(params?.artistImageFormat, 21) + }; + + const sanitizedParams = await this.sanitizeDiscoverParams(params); + const resultParams = { ...sanitizedParams }; + // Passing an 'all' type subgenre (e.g. 'all-metal') in the discover url + // Actually returns far fewer / zero results than without. + // The Bandcamp site also does not seem to include it in its discover requests... + if (sanitizedParams.time !== undefined) { + // If 'time' exists in sanitized params, then we have an 'all' type subgenre + // - refer to sanitizeDiscoverParams() + delete sanitizedParams.subgenre; + } + + const payload = this.#getDiscoverRequestPayload(sanitizedParams); + const json = await fetchPage(URLS.DISCOVER_URL, true, FetchMethod.GET, payload); + return DiscoverResultParser.parseDiscoverResult(json, opts, resultParams); + } + + static #getDiscoverRequestPayload(params: DiscoverParams): DiscoverRequestPayload { + const result: DiscoverRequestPayload = { + s: params.sortBy || 'top', + p: params.page || 0 + }; + if (params.genre) { + result.g = params.genre; + + if (params.subgenre) { + result.t = params.subgenre; + } + } + if (params.location !== undefined) { + result.gn = params.location; + } + if (params.format) { + result.f = params.format; + } + if (result.s === 'rec' && params.artistRecommendationType) { + result.r = params.artistRecommendationType; + } + if (params.time !== undefined) { + result.w = params.time; + } + + return result; + } +} + +export class LimiterDiscoveryAPI extends DiscoveryAPI { + + static async getAvailableOptions(): Promise { + return Limiter.schedule(() => super.getAvailableOptions()); + } + + static async sanitizeDiscoverParams(params: DiscoverParams): Promise { + return Limiter.schedule(() => super.sanitizeDiscoverParams(params)); + } + + static async discover(params: DiscoverParams): Promise { + return Limiter.schedule(() => super.discover(params)); + } +} diff --git a/src/lib/fan/FanAPI.ts b/src/lib/fan/FanAPI.ts new file mode 100644 index 0000000..9b71010 --- /dev/null +++ b/src/lib/fan/FanAPI.ts @@ -0,0 +1,151 @@ +import ImageAPI from '../image/ImageAPI'; +import Fan, { FanItemsContinuation } from '../types/Fan'; +import { ImageFormat } from '../types/Image'; +import { URLS } from '../utils/Constants'; +import { FetchError, FetchMethod, fetchPage } from '../utils/Fetch'; +import { FanContinuationItemsResult, FanItemParseOptions, FanPageItemsResult } from './FanItemsBaseParser'; +import FanCollectionParser from './FanCollectionParser'; +import FanFollowingParser from './FanFollowingParser'; +import FanInfoParser from './FanInfoParser'; +import FanWishlistParser from './FanWishlistParser'; +import Album from '../types/Album'; +import Track from '../types/Track'; +import UserKind from '../types/UserKind'; +import Tag from '../types/Tag'; +import Limiter from '../utils/Limiter'; + +export { FanPageItemsResult, FanContinuationItemsResult }; + +export interface FanAPIGetInfoParams { + username: string; + imageFormat: string | number | ImageFormat; +} + +export interface FanAPIGetItemsParams { + target: string | FanItemsContinuation; + imageFormat?: string | number | ImageFormat; +} + +/** + * @internal + */ +export interface FanAPIGetItemsFullParams extends FanAPIGetItemsParams { + defaultImageFormat: number; + continuationUrl?: string; + parsePageFn: (html: string, opts: FanItemParseOptions) => FanPageItemsResult; + parseContinuationFn: (json: any, continuation: FanItemsContinuation, opts: FanItemParseOptions) => FanContinuationItemsResult; +} + +export default class FanAPI { + + static async getInfo(params: FanAPIGetInfoParams): Promise { + const imageConstants = await ImageAPI.getConstants(); + const fanPageUrl = this.#getFanPageUrl(params.username); + const opts = { + imageBaseUrl: imageConstants.baseUrl, + imageFormat: await ImageAPI.getFormat(params.imageFormat, 20) + }; + const html = await fetchPage(fanPageUrl); + return FanInfoParser.parseInfo(html, opts); + } + + static async getCollection(params: FanAPIGetItemsParams) { + return await this.#getItems({ + ...params, + defaultImageFormat: 9, + continuationUrl: URLS.FAN_CONTINUATION.COLLECTION, + parsePageFn: FanCollectionParser.parseCollectionFromPage.bind(FanCollectionParser), + parseContinuationFn: FanCollectionParser.parseCollectionFromContinuation.bind(FanCollectionParser) + }); + } + + static async getWishlist(params: FanAPIGetItemsParams) { + return await this.#getItems({ + ...params, + defaultImageFormat: 9, + continuationUrl: URLS.FAN_CONTINUATION.WISHLIST, + parsePageFn: FanWishlistParser.parseWishlistFromPage.bind(FanWishlistParser), + parseContinuationFn: FanWishlistParser.parseWishlistFromContinuation.bind(FanWishlistParser) + }); + } + + static async getFollowingArtistsAndLabels(params: FanAPIGetItemsParams) { + return await this.#getItems({ + ...params, + defaultImageFormat: 21, + continuationUrl: URLS.FAN_CONTINUATION.FOLLOWING_BANDS, + parsePageFn: FanFollowingParser.parseFollowingBandsFromPage.bind(FanFollowingParser), + parseContinuationFn: FanFollowingParser.parseFollowingBandsFromContinuation.bind(FanFollowingParser) + }); + } + + static async getFollowingGenres(params: FanAPIGetItemsParams) { + return await this.#getItems({ + ...params, + defaultImageFormat: 3, + continuationUrl: URLS.FAN_CONTINUATION.FOLLOWING_GENRES, + parsePageFn: FanFollowingParser.parseFollowingGenresFromPage.bind(FanFollowingParser), + parseContinuationFn: FanFollowingParser.parseFollowingGenresFromContinuation.bind(FanFollowingParser) + }); + } + + static async #getItems(params: FanAPIGetItemsFullParams): Promise | FanContinuationItemsResult> { + const { target, imageFormat, defaultImageFormat, continuationUrl } = params; + const imageConstants = await ImageAPI.getConstants(); + const opts = { + imageBaseUrl: imageConstants.baseUrl, + imageFormat: await ImageAPI.getFormat(imageFormat, defaultImageFormat) + }; + + if (!this.#isContinuation(target)) { + const fanPageUrl = this.#getFanPageUrl(target as string); + const html = await fetchPage(fanPageUrl); + return params.parsePageFn(html, opts); + } + + // Continuation + if (!continuationUrl) { + throw new FetchError('Unable to fetch fan contents: target is continuation token but continuation URL is missing.'); + } + + const continuation = target as FanItemsContinuation; + const payload = { + fan_id: continuation.fanId, + older_than_token: continuation.token, + count: 20 + }; + const json = await fetchPage(continuationUrl, true, FetchMethod.POST, payload); + return params.parseContinuationFn(json, continuation, opts); + } + + static #getFanPageUrl(username: string) { + return `${URLS.SITE_URL}/${username}`; + } + + static #isContinuation(target: any) { + return typeof target === 'object' && target.fanId && target.token; + } +} + +export class LimiterFanAPI extends FanAPI { + + static async getInfo(params: FanAPIGetInfoParams): Promise { + return Limiter.schedule(() => super.getInfo(params)); + } + + static async getCollection(params: FanAPIGetItemsParams): Promise> | FanContinuationItemsResult>> { + return Limiter.schedule(() => super.getCollection(params)); + } + + static async getWishlist(params: FanAPIGetItemsParams): Promise> | FanContinuationItemsResult>> { + return Limiter.schedule(() => super.getWishlist(params)); + } + + static async getFollowingArtistsAndLabels(params: FanAPIGetItemsParams): Promise | FanContinuationItemsResult> { + return Limiter.schedule(() => super.getFollowingArtistsAndLabels(params)); + } + + static async getFollowingGenres(params: FanAPIGetItemsParams): Promise | FanContinuationItemsResult> { + return Limiter.schedule(() => super.getFollowingGenres(params)); + } +} diff --git a/src/lib/fan/FanCollectionParser.ts b/src/lib/fan/FanCollectionParser.ts new file mode 100644 index 0000000..49d2c28 --- /dev/null +++ b/src/lib/fan/FanCollectionParser.ts @@ -0,0 +1,26 @@ +import { FanItemsContinuation } from '../types/Fan'; +import FanItemsBaseParser, { FanItemParseOptions } from './FanItemsBaseParser'; +import FanWishlistParser from './FanWishlistParser'; + +export default class FanCollectionParser extends FanItemsBaseParser { + + static parseCollectionFromPage(html: string, opts: FanItemParseOptions) { + return this.parsePageItems(html, { + ...opts, + dataKey: 'collection', + parseItemFn: this.parseCollectionItem + }); + } + + static parseCollectionItem(data: any, opts: FanItemParseOptions, tracklists: any) { + return FanWishlistParser.parseWishlistItem(data, opts, tracklists); + } + + static parseCollectionFromContinuation(json: any, continuation: FanItemsContinuation, opts: FanItemParseOptions) { + return this.parseContinuationItems(json, continuation, { + ...opts, + dataKey: 'items', + parseItemFn: this.parseCollectionItem + }); + } +} diff --git a/src/lib/fan/FanFollowingParser.ts b/src/lib/fan/FanFollowingParser.ts new file mode 100644 index 0000000..4a5fea5 --- /dev/null +++ b/src/lib/fan/FanFollowingParser.ts @@ -0,0 +1,76 @@ +import { FanItemsContinuation } from '../types/Fan'; +import Tag from '../types/Tag'; +import UserKind from '../types/UserKind'; +import FanItemsBaseParser, { FanItemParseOptions } from './FanItemsBaseParser'; + +export default class FanFollowingParser extends FanItemsBaseParser { + + static parseFollowingBandsFromPage(html: string, opts: FanItemParseOptions) { + return this.parsePageItems(html, { + ...opts, + dataKey: 'following_bands', + parseItemFn: this.parseFollowingBand + }); + } + + static parseFollowingBandsFromContinuation(json: any, continuation: FanItemsContinuation, opts: FanItemParseOptions) { + return this.parseContinuationItems(json, continuation, { + ...opts, + dataKey: 'followeers', + parseItemFn: this.parseFollowingBand + }); + } + + static parseFollowingBand(data: any, opts: FanItemParseOptions) { + if (!data) { + return null; + } + + const band: UserKind = { + name: data.name || null, + location: data.location || '' + }; + if (data.url_hints && data.url_hints.subdomain) { + band.url = `https://${data.url_hints.subdomain}.bandcamp.com`; + } + if (data.image_id && opts.imageFormat?.id) { + band.imageUrl = `${opts.imageBaseUrl}/img/${data.image_id}_${opts.imageFormat.id}.jpg`; + } + + return band; + } + + static parseFollowingGenresFromPage(html: string, opts: FanItemParseOptions) { + return this.parsePageItems(html, { + ...opts, + dataKey: 'following_genres', + parseItemFn: this.parseFollowingGenre + }); + } + + static parseFollowingGenresFromContinuation(json: any, continuation: FanItemsContinuation, opts: FanItemParseOptions) { + return this.parseContinuationItems(json, continuation, { + ...opts, + dataKey: 'followeers', + parseItemFn: this.parseFollowingGenre + }); + } + + static parseFollowingGenre(data: any, opts: FanItemParseOptions) { + if (!data) { + return null; + } + + const genre: Tag = { + type: 'tag', + name: data.display_name, + value: data.token, + url: data.tag_page_url + }; + if (Array.isArray(data.art_ids) && opts.imageFormat?.id) { + genre.imageUrls = data.art_ids.map((artId: number) => `${opts.imageBaseUrl}/img/a${artId}_${opts.imageFormat?.id}.jpg`); + } + + return genre; + } +} diff --git a/src/lib/fan/FanInfoParser.ts b/src/lib/fan/FanInfoParser.ts new file mode 100644 index 0000000..fc5fa1a --- /dev/null +++ b/src/lib/fan/FanInfoParser.ts @@ -0,0 +1,52 @@ +import { load as cheerioLoad } from 'cheerio'; +import { decode } from 'html-entities'; +import Fan from '../types/Fan'; +import { ImageFormat } from '../types/Image'; +import { ParseError } from '../utils/Parse'; + +interface FanInfoParseOptions { + imageBaseUrl: string; + imageFormat: ImageFormat | null; +} + +export default class FanInfoParser { + + static parseInfo(html: string, opts: FanInfoParseOptions): Fan { + const $ = cheerioLoad(html); + const blob = decode($('#pagedata[data-blob]').attr('data-blob')); + let parsed; + try { + parsed = JSON.parse(blob); + } + catch (error: any) { + throw new ParseError('Failed to parse fan info: JSON error in data-blob.', html, error); + } + + const fanData = parsed.fan_data || {}; + const fanId = fanData.fan_id; + if (!fanId || !fanData.name || !fanData.username) { + throw new ParseError('Failed to parse fan info: invalid data.', html); + } + + const result: Fan = { + 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, + collectionItemCount: parsed.collection_data?.item_count || 0, + wishlistItemCount: parsed.wishlist_data?.item_count || 0 + }; + + if (fanData.photo && fanData.photo.image_id && opts.imageFormat?.id) { + result.imageUrl = `${opts.imageBaseUrl}/img/${fanData.photo.image_id}_${opts.imageFormat.id}.jpg`; + } + + return result; + } +} diff --git a/src/lib/fan/FanItemsBaseParser.ts b/src/lib/fan/FanItemsBaseParser.ts new file mode 100644 index 0000000..13b6a2c --- /dev/null +++ b/src/lib/fan/FanItemsBaseParser.ts @@ -0,0 +1,98 @@ +import { load as cheerioLoad } from 'cheerio'; +import { decode } from 'html-entities'; +import { FanItemsContinuation } from '../types/Fan'; +import { ImageFormat } from '../types/Image'; + +type PageDataKey = 'collection' | 'wishlist' | 'following_genres' | 'following_bands'; +type ContinuationDataKey = 'items' | 'followeers'; + +interface FanItemsParseParams extends FanItemParseOptions { + dataKey: PageDataKey | ContinuationDataKey; + parseItemFn: (data: any, opts: FanItemParseOptions, tracklists: any) => T; +} + +export interface FanPageItemsResult { + items: T[]; + total: number; + continuation: FanItemsContinuation | null; +} + +export interface FanContinuationItemsResult { + items: T[]; + continuation: FanItemsContinuation | null; +} + +export interface FanItemParseOptions { + imageBaseUrl: string; + imageFormat: ImageFormat | null; +} + +export default abstract class FanItemsBaseParser { + + protected static parsePageItems(html: string, params: FanItemsParseParams): FanPageItemsResult> { + const _getSequenceOrPending = (o: any) => { + 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 $ = cheerioLoad(html); + const blob = decode($('#pagedata[data-blob]').attr('data-blob')); + const parsed = JSON.parse(blob); + const result: FanPageItemsResult> = { + items: [], + total: 0, + continuation: null + }; + + const itemListData = parsed[`${params.dataKey}_data`]; + const itemCache = parsed.item_cache?.[params.dataKey]; + if (itemListData && itemCache) { + const tracklists = parsed.tracklists?.[params.dataKey]; + const sequence = _getSequenceOrPending(itemListData); + const parseFn = params.parseItemFn; + + sequence.forEach((itemKey: string) => { + const parsedItem = parseFn(itemCache[itemKey], params, 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.continuation = { + fanId, + token: itemListData.last_token + }; + } + } + + return result; + } + + protected static parseContinuationItems(json: any, continuation: FanItemsContinuation, + params: FanItemsParseParams): FanContinuationItemsResult> { + + const items = json[params.dataKey] || []; + const tracklists = json.tracklists || null; + const parseFn = params.parseItemFn; + const result: FanContinuationItemsResult> = { + items: [], + continuation: null + }; + items.forEach((data: any) => { + const parsedItem = parseFn(data, params, tracklists); + if (parsedItem) { + result.items.push(parsedItem); + } + }); + if (json.more_available && json.last_token) { + result.continuation = { + fanId: continuation.fanId, + token: json.last_token + }; + } + return result; + } +} diff --git a/src/lib/fan/FanWishlistParser.ts b/src/lib/fan/FanWishlistParser.ts new file mode 100644 index 0000000..a4eeed8 --- /dev/null +++ b/src/lib/fan/FanWishlistParser.ts @@ -0,0 +1,92 @@ +import Album from '../types/Album'; +import { FanItemsContinuation } from '../types/Fan'; +import Track from '../types/Track'; +import FanItemsBaseParser, { FanItemParseOptions } from './FanItemsBaseParser'; + +export default class FanWishlistParser extends FanItemsBaseParser { + static parseWishlistFromPage(html: string, opts: FanItemParseOptions) { + return this.parsePageItems(html, { + ...opts, + dataKey: 'wishlist', + parseItemFn: this.parseWishlistItem + }); + } + + static parseWishlistFromContinuation(json: any, continuation: FanItemsContinuation, opts: FanItemParseOptions) { + return this.parseContinuationItems(json, continuation, { + ...opts, + dataKey: 'items', + parseItemFn: this.parseWishlistItem + }); + } + + static parseWishlistItem(data: any, opts: FanItemParseOptions, tracklists: any): Album | Track | null { + if (!data) { + return null; + } + + const _findInTrackLists = (tracklists: any, id: number) => { + if (!tracklists || typeof tracklists !== 'object') { + return null; + } + for (const tracks of Object.values(tracklists)) { + if (Array.isArray(tracks)) { + const track = tracks.find((t: any) => t.id === id); + if (track) { + return track; + } + } + } + }; + + let mediaItemType: 'album' | 'track' | null; + switch (data.tralbum_type) { + case 'a': + mediaItemType = 'album'; + break; + case 't': + mediaItemType = 'track'; + break; + default: + mediaItemType = null; + } + if (!mediaItemType) { + return null; + } + const mediaItem: Album | Track = { + type: mediaItemType, + name: data.item_title, + url: data.item_url, + imageUrl: '', + artist: { + name: data.band_name + } + }; + + if (data.item_art_id && opts.imageFormat?.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) { + mediaItem.artist.url = `https://${data.url_hints.subdomain}.bandcamp.com`; + } + const featuredTrackData = data.featured_track !== undefined ? _findInTrackLists(tracklists, data.featured_track) : null; + if (featuredTrackData) { + const duration = featuredTrackData.duration; + const streamUrl = featuredTrackData.file?.['mp3-128']; + if (mediaItemType === 'album') { + (mediaItem as Album).featuredTrack = { + position: featuredTrackData.track_number, + name: featuredTrackData.title, + artist: featuredTrackData.artist, + duration, + streamUrl + }; + } + else { + (mediaItem as Track).duration = duration; + (mediaItem as Track).streamUrl = streamUrl; + } + } + return mediaItem; + } +} diff --git a/src/lib/image/ImageAPI.ts b/src/lib/image/ImageAPI.ts new file mode 100644 index 0000000..4e0e724 --- /dev/null +++ b/src/lib/image/ImageAPI.ts @@ -0,0 +1,69 @@ +import { ImageConstants, ImageFormat } from '../types/Image'; +import { Cache, CacheDataType } from '../utils/Cache'; +import { URLS } from '../utils/Constants'; +import { fetchPage } from '../utils/Fetch'; +import Limiter from '../utils/Limiter'; +import ImageParser from './ImageParser'; + +export enum ImageFormatFilter { + /** Album image formats */ + Album = 'album', + /** Artist / Profile image formats */ + Bio = 'bio' +} + +export default class ImageAPI { + + /** + * @internal + */ + static async getConstants(): Promise { + return Cache.getOrSet(CacheDataType.Constants, 'imageConstants', async () => { + const html = await fetchPage(URLS.SITE_URL); + return ImageParser.parseImageConstants(html); + }); + } + + static async getFormat(target?: string | number | ImageFormat, fallbackId?: number): Promise { + if (target && typeof target === 'object' && target.id !== undefined && target.name) { + return target; + } + let format; + if (target !== undefined) { + const imageConstants = await this.getConstants(); + format = imageConstants.formats.find( + (format) => (typeof target === 'string' && format.name === target) || + (Number.isInteger(target) && format.id === target)) || null; + } + if (format) { + return format; + } + if (fallbackId !== undefined) { + return this.getFormat(fallbackId); + } + return null; + } + + static async getFormats(filter?: ImageFormatFilter): Promise { + const constants = await this.getConstants(); + if (filter === ImageFormatFilter.Album) { + return constants.formats.filter( (c) => c.name.startsWith('art_') ); + } + else if (filter === ImageFormatFilter.Bio) { + return constants.formats.filter( (c) => c.name.startsWith('bio_') ); + } + + return constants.formats; + } +} + +export class LimiterImageAPI extends ImageAPI { + + static async getFormats(filter?: ImageFormatFilter | undefined): Promise { + return Limiter.schedule(() => super.getFormats(filter)); + } + + static async getFormat(target?: string | number | ImageFormat | undefined, fallbackId?: number | undefined): Promise { + return Limiter.schedule(() => super.getFormat(target, fallbackId)); + } +} diff --git a/src/lib/image/ImageParser.ts b/src/lib/image/ImageParser.ts new file mode 100644 index 0000000..fc8a701 --- /dev/null +++ b/src/lib/image/ImageParser.ts @@ -0,0 +1,34 @@ +import { load as cheerioLoad } from 'cheerio'; +import { decode } from 'html-entities'; +import { ImageConstants } from '../types/Image'; +import { ParseError } from '../utils/Parse'; + +export default class ImageParser { + + static parseImageConstants(html: string): ImageConstants { + const $ = cheerioLoad(html); + const vars = decode($('script[data-vars]').attr('data-vars')); + let parsed; + try { + parsed = JSON.parse(vars); + } + catch (error: any) { + throw new ParseError('Failed to parse image constants: JSON error.', vars, error); + } + if (parsed?.client_template_globals) { + const formats = parsed.client_template_globals.image_formats as Array; + return { + baseUrl: parsed.client_template_globals.image_siteroot_https, + formats: formats?.map((format: any) => ({ + id: format.id, + name: format.name, + width: format.width, + height: format.height, + fileFormat: format.file_format + })) + }; + } + + throw new ParseError('Failed to parse image constants: data is missing \'client_template_globals\' field.', parsed); + } +} diff --git a/src/lib/search/SearchAPI.ts b/src/lib/search/SearchAPI.ts new file mode 100644 index 0000000..c7b0e42 --- /dev/null +++ b/src/lib/search/SearchAPI.ts @@ -0,0 +1,106 @@ +import { URL } from 'url'; +import ImageAPI from '../image/ImageAPI'; +import { ImageFormat } from '../types/Image'; +import { URLS } from '../utils/Constants'; +import { fetchPage } from '../utils/Fetch'; +import SearchResultsParser from './SearchResultsParser'; +import { SearchResultAlbum, SearchResultAny, SearchResultArtist, SearchResultFan, SearchResultLabel, SearchResultTrack, SearchResults } from '../types/Search'; +import Limiter from '../utils/Limiter'; + +export enum SearchItemType { + All = 'All', + ArtistsAndLabels = 'ArtistsAndLabels', + Albums = 'Albums', + Tracks = 'Tracks', + Fans = 'Fans' +} + +export interface SearchAPISearchParams { + query: string; + page?: number; + albumImageFormat?: string | number | ImageFormat; + artistImageFormat?: string | number | ImageFormat; +} + +export default class SearchAPI { + + static async all(params: SearchAPISearchParams) { + return this.#search({ ...params, itemType: SearchItemType.All }); + } + + static async artistsAndLabels(params: SearchAPISearchParams) { + return this.#search({ ...params, itemType: SearchItemType.ArtistsAndLabels }); + } + + static async albums(params: SearchAPISearchParams) { + return this.#search({ ...params, itemType: SearchItemType.Albums }); + } + + static async tracks(params: SearchAPISearchParams) { + return this.#search({ ...params, itemType: SearchItemType.Tracks }); + } + + static async fans(params: SearchAPISearchParams) { + return this.#search({ ...params, itemType: SearchItemType.Fans }); + } + + static async #search(params: SearchAPISearchParams & { itemType: SearchItemType.ArtistsAndLabels }): Promise>; + static async #search(params: SearchAPISearchParams & { itemType: SearchItemType.Albums }): Promise>; + static async #search(params: SearchAPISearchParams & { itemType: SearchItemType.Tracks }): Promise>; + static async #search(params: SearchAPISearchParams & { itemType: SearchItemType.Fans }): Promise>; + static async #search(params: SearchAPISearchParams & { itemType: SearchItemType.All }): Promise>; + static async #search(params: SearchAPISearchParams & { itemType: SearchItemType }): Promise { + const opts = { + itemType: params.itemType || SearchItemType.All, + albumImageFormat: await ImageAPI.getFormat(params.albumImageFormat, 9), + artistImageFormat: await ImageAPI.getFormat(params.artistImageFormat, 21) + }; + const html = await fetchPage(this.#getSearchUrl(params)); + return SearchResultsParser.parseResults(html, opts); + } + + static #getSearchUrl(params: SearchAPISearchParams & { itemType: SearchItemType }) { + const urlObj = new URL(URLS.SEARCH); + urlObj.searchParams.set('q', params.query); + urlObj.searchParams.set('page', (params.page || 1).toString()); + switch (params.itemType) { + case SearchItemType.ArtistsAndLabels: + urlObj.searchParams.set('item_type', 'b'); + break; + case SearchItemType.Albums: + urlObj.searchParams.set('item_type', 'a'); + break; + case SearchItemType.Tracks: + urlObj.searchParams.set('item_type', 't'); + break; + case SearchItemType.Fans: + urlObj.searchParams.set('item_type', 'f'); + break; + default: + } + return urlObj.toString(); + } +} + +export class LimiterSearchAPI extends SearchAPI { + + static async all(params: SearchAPISearchParams): Promise> { + return Limiter.schedule(() => super.all(params)); + } + + static async artistsAndLabels(params: SearchAPISearchParams): Promise> { + return Limiter.schedule(() => super.artistsAndLabels(params)); + } + + static async albums(params: SearchAPISearchParams): Promise> { + return Limiter.schedule(() => super.albums(params)); + } + + static async tracks(params: SearchAPISearchParams): Promise> { + return Limiter.schedule(() => super.tracks(params)); + } + + static async fans(params: SearchAPISearchParams): Promise> { + return Limiter.schedule(() => super.fans(params)); + } +} diff --git a/src/lib/search/SearchResultsParser.ts b/src/lib/search/SearchResultsParser.ts new file mode 100644 index 0000000..e4001b5 --- /dev/null +++ b/src/lib/search/SearchResultsParser.ts @@ -0,0 +1,176 @@ +import { load as cheerioLoad } from 'cheerio'; +import { ImageFormat } from '../types/Image'; +import { reformatImageUrl, stripLineBreaks, stripMultipleWhitespaces, substrAfter, substrBefore } from '../utils/Parse'; +import { SearchResultAlbum, SearchResultAny, SearchResultArtist, SearchResultFan, SearchResultItem, SearchResultLabel, SearchResultTrack, SearchResults } from '../types/Search'; +import { SearchItemType } from './SearchAPI'; + +const VALID_RESULT_TYPES = [ 'artist', 'label', 'album', 'track', 'fan' ]; + +interface SearchResultsParseOptions { + itemType: SearchItemType; + albumImageFormat: ImageFormat | null; + artistImageFormat: ImageFormat | null; +} + +export default class SearchResultsParser { + + static parseResults(html: string, opts: SearchResultsParseOptions): SearchResults { + const $ = cheerioLoad(html); + const resultsList = $('li.searchresult'); + const items: Array = []; + resultsList.each((i, resultListItem: any) => { + resultListItem = $(resultListItem); + const resultInfo = resultListItem.find('.result-info'); + + // Common info + const resultType = resultInfo.children('.itemtype').text().trim().toLowerCase(); + const imgSrc = $('.art img', resultListItem).attr('src'); + const heading = $('.heading a', resultInfo); + const name = heading.text().trim(); + const url = resultInfo.find('.itemurl').text().trim(); + const imageUrl = reformatImageUrl(imgSrc, resultType === 'album' || resultType === 'track' ? opts.albumImageFormat : opts.artistImageFormat); + + if (!this.#matchSearchItemTypeWithResult(opts.itemType, resultType) || !name || !url) { + return true; + } + + // Other info + let location, genre, tags, artist, numTracks, duration, releaseDate, album; + resultInfo.find('.subhead, .genre, .tags, .released, .length').each((i: number, info: any) => { + info = $(info); + if (info.hasClass('subhead')) { + if (resultType === 'artist' || resultType === 'label') { + location = info.text().trim(); + } + else if (resultType === 'album' || resultType === 'track') { + const infoText = info.text(); + artist = substrAfter(infoText, 'by ')?.trim(); + + if (resultType === 'track') { + album = substrBefore(infoText, ' by'); + if (album) { + album = substrAfter(album, 'from ')?.trim(); + } + } + } + return true; + } + if (info.hasClass('genre')) { + genre = substrAfter(info.text(), 'genre: ')?.trim(); + return true; + } + if (info.hasClass('tags')) { + tags = substrAfter(info.text(), 'tags:'); + if (tags) { + tags = stripLineBreaks(stripMultipleWhitespaces(tags)).trim(); + } + return true; + } + if (info.hasClass('released')) { + releaseDate = substrAfter(info.text(), 'released ')?.trim(); + return true; + } + if (info.hasClass('length')) { + const lengthParts = info.text().split(','); + const tracksText = lengthParts[0]; + const minutesText = lengthParts[1]; + const numTracksStr = tracksText ? substrBefore(tracksText, 'tracks') : null; + if (numTracksStr) { + numTracks = parseInt(numTracksStr, 10); + } + const durationStr = minutesText ? substrBefore(minutesText, 'minutes') : null; + if (durationStr) { + duration = parseInt(durationStr, 10) * 60; + } + } + }); + + const base: Omit = { + name: heading.text().trim(), + url: resultInfo.find('.itemurl').text().trim() || '' + }; + if (imageUrl) { + base.imageUrl = imageUrl; + } + + if (resultType === 'artist') { + const artist: SearchResultArtist = { + type: 'artist', + ...base + }; + if (location) artist.location = location; + if (genre) artist.genre = genre; + if (tags) artist.tags = tags; + items.push(artist); + } + else if (resultType === 'label') { + const label: SearchResultLabel = { + type: 'label', + ...base + }; + if (location) label.location = location; + items.push(label); + } + else if (resultType === 'album') { + const album: SearchResultAlbum = { + type: 'album', + ...base + }; + if (artist) album.artist = artist; + if (numTracks) album.numTracks = numTracks; + if (duration) album.duration = duration; + if (releaseDate) album.releaseDate = releaseDate; + if (tags) album.tags = tags; + items.push(album); + } + else if (resultType === 'track') { + const track: SearchResultTrack = { + type: 'track', + ...base + }; + if (artist) track.artist = artist; + if (album) track.album = album; + if (releaseDate) track.releaseDate = releaseDate; + if (tags) track.tags = tags; + items.push(track); + } + else if (resultType === 'fan') { + const fan: SearchResultFan = { + type: 'fan', + ...base + }; + if (genre) fan.genre = genre; + items.push(fan); + } + }); + + let totalPages = parseInt($('.pagelist').find('.pagenum').last().text(), 10); + if (isNaN(totalPages)) { + totalPages = 1; + } + + return { + items, + totalPages + }; + } + + static #matchSearchItemTypeWithResult(searchType: SearchItemType, resultType: string) { + if (searchType === SearchItemType.All && VALID_RESULT_TYPES.includes(resultType)) { + return true; + } + + switch (searchType) { + case SearchItemType.ArtistsAndLabels: + return resultType === 'artist' || resultType === 'label'; + case SearchItemType.Albums: + return resultType === 'album'; + case SearchItemType.Tracks: + return resultType === 'track'; + case SearchItemType.Fans: + return resultType === 'fan'; + default: + return false; + } + } +} diff --git a/src/lib/show/ShowAPI.ts b/src/lib/show/ShowAPI.ts new file mode 100644 index 0000000..bcf2aa2 --- /dev/null +++ b/src/lib/show/ShowAPI.ts @@ -0,0 +1,56 @@ +import ImageAPI from '../image/ImageAPI'; +import { ImageFormat } from '../types/Image'; +import Show from '../types/Show'; +import { URLS } from '../utils/Constants'; +import { fetchPage } from '../utils/Fetch'; +import Limiter from '../utils/Limiter'; +import ShowListParser from './ShowListParser'; +import ShowParser from './ShowParser'; + +export interface ShowAPIGetShowParams { + showUrl: string, + albumImageFormat?: string | number | ImageFormat; + artistImageFormat?: string | number | ImageFormat; + showImageFormat?: string | number | ImageFormat; +} + +export interface ShowAPIListParams { + imageFormat?: string | number | ImageFormat; +} + +export default class ShowAPI { + + static async getShow(params: ShowAPIGetShowParams): Promise { + const imageConstants = await ImageAPI.getConstants(); + const opts = { + showUrl: params.showUrl, + imageBaseUrl: imageConstants.baseUrl, + albumImageFormat: await ImageAPI.getFormat(params.albumImageFormat, 9), + artistImageFormat: await ImageAPI.getFormat(params.artistImageFormat, 21), + showImageFormat: await ImageAPI.getFormat(params.showImageFormat, 25) + }; + const html = await fetchPage(params.showUrl); + return ShowParser.parseShow(html, opts); + } + + static async list(params?: ShowAPIListParams) { + const imageConstants = await ImageAPI.getConstants(); + const opts = { + imageBaseUrl: imageConstants.baseUrl, + imageFormat: await ImageAPI.getFormat(params?.imageFormat, 25) + }; + const json = await fetchPage(URLS.SHOWS, true); + return ShowListParser.parseList(json, opts); + } +} + +export class LimiterShowAPI extends ShowAPI { + + static async getShow(params: ShowAPIGetShowParams): Promise { + return Limiter.schedule(() => super.getShow(params)); + } + + static async list(params?: ShowAPIListParams | undefined): Promise { + return Limiter.schedule(() => super.list(params)); + } +} diff --git a/src/lib/show/ShowListParser.ts b/src/lib/show/ShowListParser.ts new file mode 100644 index 0000000..d89df16 --- /dev/null +++ b/src/lib/show/ShowListParser.ts @@ -0,0 +1,38 @@ +import { ImageFormat } from '../types/Image'; +import Show from '../types/Show'; +import { URLS } from '../utils/Constants'; + +interface ShowListParseOptions { + imageBaseUrl: string; + imageFormat: ImageFormat | null; +} + +export default class ShowListParser { + + static parseList(json: any, opts: ShowListParseOptions): Show[] { + const shows: Show[] = []; + if (typeof json === 'object' && Array.isArray(json.results)) { + json.results.forEach((show: any) => { + const parsed: Show = { + type: 'show', + name: show.title, + url: this.#getShowUrl(show.id), + publishedDate: show.published_date, + description: show.desc, + imageCaption: show.image_caption, + subtitle: show.subtitle, + screenImageUrl: `${opts.imageBaseUrl}/img/${show.v2_image_id}_0` + }; + if (show.v2_image_id && opts.imageFormat?.id) { + parsed.imageUrl = `${opts.imageBaseUrl}/img/${show.v2_image_id}_${opts.imageFormat.id}.jpg`; + } + shows.push(parsed); + }); + } + return shows; + } + + static #getShowUrl(showId: string): string { + return `${URLS.SITE_URL}/?show=${showId}`; + } +} diff --git a/src/lib/show/ShowParser.ts b/src/lib/show/ShowParser.ts new file mode 100644 index 0000000..b4216e6 --- /dev/null +++ b/src/lib/show/ShowParser.ts @@ -0,0 +1,103 @@ +import { load as cheerioLoad } from 'cheerio'; +import { decode } from 'html-entities'; +import { ImageFormat } from '../types/Image'; +import Show from '../types/Show'; +import { ParseError, splitUrl } from '../utils/Parse'; +import { URLS } from '../utils/Constants'; +import Track from '../types/Track'; + +interface ShowParseOptions { + showUrl: string, + imageBaseUrl: string, + albumImageFormat?: ImageFormat | null; + artistImageFormat?: ImageFormat | null; + showImageFormat?: ImageFormat | null; +} + +export default class ShowParser { + static parseShow(html: string, opts: ShowParseOptions): Show { + const $ = cheerioLoad(html); + const blob = decode($('#pagedata[data-blob]').attr('data-blob')); + let parsed; + try { + parsed = JSON.parse(blob); + } + catch (error: any) { + throw new ParseError('Failed to parse show: JSON error in data-blob.', html, error); + } + + if (typeof parsed === 'object' && parsed.bcw_data) { + const showId = this.#getShowIdFromUrl(opts.showUrl); + let showInfo; + if (showId !== null) { + showInfo = parsed.bcw_data?.[showId]; + } + if (showInfo) { + const show: Show = { + type: 'show', + name: showInfo.title, + url: this.#getShowUrl(showInfo.show_id), + publishedDate: showInfo.published_date, + description: showInfo.desc, + shortDescription: showInfo.short_desc, + imageCaption: showInfo.image_caption, + subtitle: showInfo.subtitle, + duration: showInfo.audio_duration, + screenImageUrl: `${opts.imageBaseUrl}/img/${showInfo.show_v2_image_id}_0` + }; + const mp3StreamUrl = showInfo.audio_stream?.['mp3-128']; + const opusStreamUrl = showInfo.audio_stream?.['opus-lo']; + if (mp3StreamUrl || opusStreamUrl) { + show.streamUrl = {}; + if (mp3StreamUrl) { + show.streamUrl['mp3-128'] = mp3StreamUrl; + } + if (opusStreamUrl) { + show.streamUrl['opus-lo'] = opusStreamUrl; + } + } + if (opts.showImageFormat?.id) { + show.imageUrl = `${opts.imageBaseUrl}/img/${showInfo.show_v2_image_id}_${opts.showImageFormat.id}.jpg`; + } + show.tracks = showInfo.tracks.map((track: any) => { + const trackItem: Omit = { + name: track.title, + url: track.track_url, + seekPosition: track.timecode, + artist: { + name: track.artist, + url: `https://${track.url_hints.subdomain}.bandcamp.com`, + location: track.location_text + } + }; + if (opts.albumImageFormat?.id) { + trackItem.imageUrl = `${opts.imageBaseUrl}/img/a${track.track_art_id}_${opts.albumImageFormat.id}.jpg`; + } + if (opts.artistImageFormat?.id && trackItem.artist) { + trackItem.artist.imageUrl = `${opts.imageBaseUrl}/img/${track.bio_image_id}_${opts.artistImageFormat.id}.jpg`; + } + if (track.album_title) { + trackItem.album = { + name: track.album_title, + url: track.album_url + }; + } + return trackItem; + }); + + return show; + } + } + + throw new ParseError('Failed to parse show: missing bcw_data.', html); + } + + static #getShowIdFromUrl(showUrl: string) { + const params = splitUrl(showUrl).searchParams; + return params.get('show'); + } + + static #getShowUrl(showId: string): string { + return `${URLS.SITE_URL}/?show=${showId}`; + } +} \ No newline at end of file diff --git a/src/lib/tag/AlbumHighlightsByTagParser.ts b/src/lib/tag/AlbumHighlightsByTagParser.ts new file mode 100644 index 0000000..542bfaa --- /dev/null +++ b/src/lib/tag/AlbumHighlightsByTagParser.ts @@ -0,0 +1,68 @@ +import { load as cheerioLoad } from 'cheerio'; +import { decode } from 'html-entities'; +import { ImageFormat } from '../types/Image'; +import { ParseError } from '../utils/Parse'; +import { AlbumHighlightsByTag } from '../types/Tag'; +import Album from '../types/Album'; + +interface AlbumHighlightsByTagParseOptions { + imageBaseUrl: string; + imageFormat: ImageFormat | null; +} + +export default class AlbumHighlightsByTagParser { + + static parseHighlights(html: string, opts: AlbumHighlightsByTagParseOptions): AlbumHighlightsByTag[] { + const $ = cheerioLoad(html); + const blob = decode($('#pagedata[data-blob]').attr('data-blob')); + let parsed; + try { + parsed = JSON.parse(blob); + } + catch (error: any) { + throw new ParseError('Failed to parse album highlights by tag: JSON error in data-blob', html, error); + } + const collections: AlbumHighlightsByTag[] = []; + if (Array.isArray(parsed?.hub?.tabs?.[0]?.collections)) { + + parsed.hub.tabs[0].collections.forEach((collection: any) => { + if (collection?.name && collection?.render?.title) { + const collectionRes: AlbumHighlightsByTag = { + name: collection.name, + title: collection.render.title, + items: [] + }; + collection.items.forEach((item: any) => { + if (item?.item_type === 'a') { + const album: Album = { + type: 'album', + name: item.title, + url: item.tralbum_url, + imageUrl: '', + genre: item.genre, + artist: { + name: item.artist, + url: item.band_url + } + }; + if (item.art_id && opts.imageFormat?.id) { + album.imageUrl = `${opts.imageBaseUrl}/img/a${item.art_id}_${opts.imageFormat.id}.jpg`; + } + if (item.featured_track_title) { + album.featuredTrack = { + name: item.featured_track_title, + streamUrl: item.audio_url?.['mp3-128'] + }; + } + collectionRes.items.push(album); + } + }); + if (collectionRes.items.length > 0) { + collections.push(collectionRes); + } + } + }); + } + return collections; + } +} diff --git a/src/lib/tag/ReleasesByTagParser.ts b/src/lib/tag/ReleasesByTagParser.ts new file mode 100644 index 0000000..6ce4878 --- /dev/null +++ b/src/lib/tag/ReleasesByTagParser.ts @@ -0,0 +1,189 @@ +import { load as cheerioLoad } from 'cheerio'; +import { decode } from 'html-entities'; +import Album from '../types/Album'; +import { ImageFormat } from '../types/Image'; +import { ReleasesByTag } from '../types/Tag'; +import Track from '../types/Track'; +import { ParseError, stripLineBreaks } from '../utils/Parse'; +import { Interpreter } from 'eval5'; + +interface ReleasesByTagParseOptions { + imageBaseUrl: string; + imageFormat: ImageFormat | null; +} + +export default class ReleasesByTagParser { + + static parseHubJSPath(html: string) { + const jsMatch = (/src="((?:.+?)hub-(?:.+?).js)"/g).exec(html); + return jsMatch?.[1] || null; + } + + static parseHubJSFilterValueNames(js: string): ReleasesByTag.FilterValueNames { + const filterValueNames: ReleasesByTag.FilterValueNames = {}; + const tObj = (/"hubs\/digdeeper\/filter_value":(.+?)}\),/gs).exec(js); + if (tObj?.[1]) { + const interpreter = new Interpreter({}); + const t = interpreter.evaluate(tObj[1]); + if (Array.isArray(t?.[0]?.blocks)) { + const _getValFromBlockAttachment = (attachment: any) => { + if (attachment?.type === 'translate' && attachment?.nodelist?.[0] !== undefined) { + return stripLineBreaks(attachment.nodelist[0]).trim(); + } + else if (typeof attachment === 'string') { + return stripLineBreaks(attachment).trim(); + } + + return ''; + + }; + t[0].blocks.forEach((filterBlock: any) => { + const filterName = interpreter.evaluate(filterBlock.expression?.split('==')[1]); + if (filterName && Array.isArray(filterBlock.attachment)) { + filterBlock + .attachment.find((a: any) => Array.isArray(a?.blocks)) + .blocks.filter((block: any) => block?.expression) + .forEach((valueBlock: any) => { + const value = interpreter.evaluate(valueBlock.expression?.split('==')[1]); + if (value != null && Array.isArray(valueBlock?.attachment)) { + const valueName = valueBlock.attachment.reduce((a: string, c: any) => { + const cVal = stripLineBreaks(_getValFromBlockAttachment(c)).trim(); + if (cVal !== '') { + return a !== '' ? `${a} ${cVal}` : cVal; + } + + return a; + + }, ''); + //Console.log('value name: ' + valueName); + if (valueName) { + if (!filterValueNames[filterName]) { + filterValueNames[filterName] = {}; + } + filterValueNames[filterName][value] = valueName; + } + } + }); + } + }); + } + } + return filterValueNames; + } + + static parseFilters(html: string, filterValueNames: ReleasesByTag.FilterValueNames): ReleasesByTag.Filter[] { + const $ = cheerioLoad(html); + const blob = decode($('#pagedata[data-blob]').attr('data-blob')); + const parsed = JSON.parse(blob); + const filters: ReleasesByTag.Filter[] = []; + if (typeof parsed === 'object' && parsed.hub && Array.isArray(parsed.hub.tabs)) { + const tab = parsed.hub.tabs[1]; // All releases + + const _setOrAdd = (f: any, t: any, prop: string) => { + const target = f.options.find((f: any) => f.value === t.value); + if (target) { + target[prop] = true; + } + else if (t.value && t.name) { + const tAdd = { + name: t.name, + value: t.value + } as any; + tAdd[prop] = true; + f.options.push(tAdd); + } + }; + + if (tab && tab.dig_deeper && typeof tab.dig_deeper.filters === 'object') { + const filterKeys = Object.keys(tab.dig_deeper.filters); + filterKeys.forEach((filterName) => { + const filter: ReleasesByTag.Filter = { + name: filterName, + options: [] + }; + const filterData = tab.dig_deeper.filters[filterName]; + if (Array.isArray(filterData?.options)) { + filterData.options.forEach((filterOption: any) => { + const valueName = filterValueNames?.[filterName]?.[filterOption?.value] || filterOption?.name || filterOption?.value; + if (valueName) { + filter.options.push({ + name: valueName, + value: filterOption.value + }); + } + }); + } + if (typeof filterData?.selected === 'object' && !Array.isArray(filterData.selected)) { + _setOrAdd(filter, filterData.selected, 'selected'); + } + else if (Array.isArray(filterData?.selected)) { + filterData.selected.forEach((s: any) => { + _setOrAdd(filter, s, 'selected'); + }); + } + if (filterData?.default) { + _setOrAdd(filter, filterData.default, 'default'); + } + + filters.push(filter); + }); + } + } + return filters; + } + + + static parseReleases(json: any, opts: ReleasesByTagParseOptions): ReleasesByTag { + if (typeof json === 'object' && Array.isArray(json.items)) { + const parsedItems: Array = []; + json.items.forEach((item: any) => { + let mediaItemType: 'album' | 'track' | null; + switch (item.item_type) { + case 'a': + mediaItemType = 'album'; + break; + case 't': + mediaItemType = 'track'; + break; + default: + mediaItemType = null; + } + if (mediaItemType) { + const mediaItem: Album | Track = { + type: mediaItemType, + name: item.title, + url: item.tralbum_url, + imageUrl: '', + genre: item.genre, + artist: { + name: item.artist, + url: item.band_url + } + }; + if (item.art_id && opts.imageFormat?.id) { + mediaItem.imageUrl = `${opts.imageBaseUrl}/img/a${item.art_id}_${opts.imageFormat.id}.jpg`; + } + if (mediaItemType === 'album' && item.featured_track_title) { + const album = mediaItem as Album; + album.featuredTrack = { + name: item.featured_track_title, + position: item.featured_track_number, + streamUrl: item.audio_url?.['mp3-128'] + }; + } + else if (mediaItemType === 'track') { + (mediaItem as Track).streamUrl = item.audio_url?.['mp3-128']; + } + parsedItems.push(mediaItem); + } + }); + return { + items: parsedItems, + hasMore: json.more_available, + filters: JSON.parse(json.filters) + }; + } + + throw new ParseError('Failed to parse releases by tag: invalid JSON data', json); + } +} diff --git a/src/lib/tag/TagAPI.ts b/src/lib/tag/TagAPI.ts new file mode 100644 index 0000000..e02d855 --- /dev/null +++ b/src/lib/tag/TagAPI.ts @@ -0,0 +1,162 @@ +import ImageAPI from '../image/ImageAPI'; +import { ImageFormat } from '../types/Image'; +import Tag, { AlbumHighlightsByTag, ReleasesByTag, TagList } from '../types/Tag'; +import { URLS } from '../utils/Constants'; +import { FetchMethod, fetchPage } from '../utils/Fetch'; +import Limiter from '../utils/Limiter'; +import { ParseError, normalizeUrl, splitUrl } from '../utils/Parse'; +import AlbumHighlightsByTagParser from './AlbumHighlightsByTagParser'; +import ReleasesByTagParser from './ReleasesByTagParser'; +import TagInfoParser from './TagInfoParser'; +import TagListParser from './TagListParser'; + +export interface TagAPIGetAlbumHighlightsParams { + tagUrl: string; + imageFormat?: string | number | ImageFormat; +} + +export interface TagAPIGetReleasesParams { + tagUrl: string; + imageFormat?: string | number | ImageFormat; + useHardcodedDefaultFilters?: boolean; + filters?: Record>; + page?: number; +} + +export default class TagAPI { + + static async list(): Promise { + const html = await fetchPage(normalizeUrl('tags')); + return TagListParser.parseTags(html); + } + + static async getInfo(tagUrl: string): Promise { + const html = await fetchPage(tagUrl); + return TagInfoParser.parseInfo(html, tagUrl); + } + + static async getAlbumHighlights(params: TagAPIGetAlbumHighlightsParams): Promise { + const imageConstants = await ImageAPI.getConstants(); + const opts = { + imageBaseUrl: imageConstants.baseUrl, + imageFormat: await ImageAPI.getFormat(params.imageFormat, 9) + }; + + const html = await fetchPage(params.tagUrl); + return AlbumHighlightsByTagParser.parseHighlights(html, opts); + } + + static async getReleasesAvailableFilters(tagUrl: string): Promise { + const filterValueNames = await this.#getReleaseFilterValueNames(tagUrl); + const html = await fetchPage(tagUrl); + return ReleasesByTagParser.parseFilters(html, filterValueNames); + } + + static async getReleases(params: TagAPIGetReleasesParams): Promise { + const imageConstants = await ImageAPI.getConstants(); + const opts = { + imageBaseUrl: imageConstants.baseUrl, + imageFormat: await ImageAPI.getFormat(params.imageFormat, 9) + }; + + const _getDefaultFilters = async (tagUrl: string) => { + if (params.useHardcodedDefaultFilters) { + let tagUrlPath = splitUrl(tagUrl).path; + if (tagUrlPath.endsWith('/')) { + tagUrlPath = tagUrlPath.substring(0, tagUrlPath.length - 1); + } + const tagValue = tagUrlPath.split('/').pop(); + + return Promise.resolve({ + tags: [ tagValue ], + location: 0, + format: 'all', + sort: 'pop' + }); + } + + const filterOptions = await this.getReleasesAvailableFilters(tagUrl); + const defaultFilters: TagAPIGetReleasesParams['filters'] = {}; + filterOptions.forEach((filter) => { + const selectedOption = filter.options.find((o) => o.selected); + const defaultOption = filter.options.find((o) => o.default); + if (selectedOption) { + if (filter.name === 'tags') { + defaultFilters[filter.name] = [ selectedOption.value ]; + } + else { + defaultFilters[filter.name] = selectedOption.value; + } + } + else if (defaultOption) { + defaultFilters[filter.name] = defaultOption.value; + } + }); + + return defaultFilters; + + }; + + const defaultFilters = await _getDefaultFilters(params.tagUrl); + const tagsFilter = Array.isArray(defaultFilters.tags) ? [ ...defaultFilters.tags ] : []; + if (params?.filters?.tags && Array.isArray(params.filters.tags)) { + params.filters.tags.forEach((tag) => { + if (!tagsFilter.includes(tag)) { + tagsFilter.push(tag); + } + }); + } + const paramFilters = params.filters ? { + ...defaultFilters, + ...params.filters, + tags: tagsFilter + } : defaultFilters; + + const postData = { + filters: paramFilters, + page: params.page || 1 + }; + + const json = await fetchPage(URLS.DIG_DEEPER, true, FetchMethod.POST, postData); + return ReleasesByTagParser.parseReleases(json, opts); + } + + static async #getReleaseFilterValueNames(tagUrl: string) { + const url = `${tagUrl}?tab=all_releases`; + const html = await fetchPage(url); + const path = ReleasesByTagParser.parseHubJSPath(html); + if (!path) { + throw new ParseError(`Failed to obtain Hub JS path from ${url}`, html); + } + const js = await fetchPage(path); + try { + return ReleasesByTagParser.parseHubJSFilterValueNames(js); + } + catch (error: any) { + throw new ParseError('Failed to obtain filter names / values from Hub JS', js, error); + } + } +} + +export class LimiterTagAPI extends TagAPI { + + static async list(): Promise { + return Limiter.schedule(() => super.list()); + } + + static async getInfo(tagUrl: string): Promise { + return Limiter.schedule(() => super.getInfo(tagUrl)); + } + + static async getAlbumHighlights(params: TagAPIGetAlbumHighlightsParams): Promise { + return Limiter.schedule(() => super.getAlbumHighlights(params)); + } + + static async getReleasesAvailableFilters(tagUrl: string): Promise { + return Limiter.schedule(() => super.getReleasesAvailableFilters(tagUrl)); + } + + static async getReleases(params: TagAPIGetReleasesParams): Promise { + return Limiter.schedule(() => super.getReleases(params)); + } +} diff --git a/src/lib/tag/TagInfoParser.ts b/src/lib/tag/TagInfoParser.ts new file mode 100644 index 0000000..5b0a584 --- /dev/null +++ b/src/lib/tag/TagInfoParser.ts @@ -0,0 +1,40 @@ +import { load as cheerioLoad } from 'cheerio'; +import { decode } from 'html-entities'; +import Tag from '../types/Tag'; +import { ParseError, normalizeUrl } from '../utils/Parse'; + +export default class TagInfoParser { + + static parseInfo(html: string, tagUrl: string): Tag { + const $ = cheerioLoad(html); + const blob = decode($('#pagedata[data-blob]').attr('data-blob')); + let parsed; + try { + parsed = JSON.parse(blob); + } + catch (error: any) { + throw new ParseError('Failed to parse tag info: JSON error in data-blob.', html, error); + } + if (typeof parsed?.hub === 'object') { + const tag: Tag = { + type: 'tag', + name: parsed.hub.name, + url: tagUrl, + value: parsed.hub.norm_name, + related: [] + }; + if (Array.isArray(parsed.hub.related_tags)) { + tag.related = parsed.hub.related_tags.map((related: any) => ({ + type: 'tag', + name: related.name, + url: normalizeUrl(related.url), + value: related.norm_name, + isLocation: related.isloc + })); + } + return tag; + } + + throw new ParseError('Failed to parse tag info: hub data missing or invalid.', html); + } +} diff --git a/src/lib/tag/TagListParser.ts b/src/lib/tag/TagListParser.ts new file mode 100644 index 0000000..72e1190 --- /dev/null +++ b/src/lib/tag/TagListParser.ts @@ -0,0 +1,38 @@ +import { load as cheerioLoad } from 'cheerio'; +import Tag, { TagList } from '../types/Tag'; +import { normalizeUrl } from '../utils/Parse'; + +export default class TagListParser { + static parseTags(html: string): TagList { + const $ = cheerioLoad(html); + + const _findTag = (tagUrl: string, tagName: string, tags: Omit[]) => { + return tags.find((t) => t.url === tagUrl && t.name === tagName); + }; + + const _parseCloud = (id: string) => { + const cloud = $(`#${id}`); + const tagsInCloud: Omit[] = []; + cloud.find('a.tag').each((index, link) => { + const linkEl = $(link); + const name = linkEl.text().trim(); + const href = linkEl.attr('href'); + if (href) { + const url = normalizeUrl(href); + if (name && href !== '/tag/' && !_findTag(url, name, tagsInCloud)) { // Skip blank or repeating tags + tagsInCloud.push({ + name, + url + }); + } + } + }); + return tagsInCloud; + }; + + return { + tags: _parseCloud('tags_cloud'), + locations: _parseCloud('locations_cloud') + }; + } +} diff --git a/src/lib/track/TrackAPI.ts b/src/lib/track/TrackAPI.ts new file mode 100644 index 0000000..4de6d88 --- /dev/null +++ b/src/lib/track/TrackAPI.ts @@ -0,0 +1,34 @@ +import ImageAPI from '../image/ImageAPI'; +import { ImageFormat } from '../types/Image'; +import Track from '../types/Track'; +import { fetchPage } from '../utils/Fetch'; +import Limiter from '../utils/Limiter'; +import TrackInfoParser from './TrackInfoParser'; + +export interface TrackAPIGetInfoParams { + trackUrl: string; + albumImageFormat?: string | number | ImageFormat; + artistImageFormat?: string | number | ImageFormat; + includeRawData?: boolean; +} + +export default class TrackAPI { + static async getInfo(params: TrackAPIGetInfoParams): Promise { + const imageConstants = await ImageAPI.getConstants(); + const opts = { + trackUrl: params.trackUrl, + imageBaseUrl: imageConstants.baseUrl, + albumImageFormat: await ImageAPI.getFormat(params.albumImageFormat, 9), + artistImageFormat: await ImageAPI.getFormat(params.artistImageFormat, 21), + includeRawData: params.includeRawData !== undefined ? params.includeRawData : false + }; + const html = await fetchPage(params.trackUrl); + return TrackInfoParser.parseInfo(html, opts); + } +} + +export class LimiterTrackAPI extends TrackAPI { + static async getInfo(params: TrackAPIGetInfoParams): Promise { + return Limiter.schedule(() => super.getInfo(params)); + } +} diff --git a/src/lib/track/TrackInfoParser.ts b/src/lib/track/TrackInfoParser.ts new file mode 100644 index 0000000..ccfe3af --- /dev/null +++ b/src/lib/track/TrackInfoParser.ts @@ -0,0 +1,165 @@ +import { load as cheerioLoad } from 'cheerio'; +import { decode } from 'html-entities'; +import { ImageFormat } from '../types/Image'; +import Track from '../types/Track'; +import { ParseError, getAdditionalPropertyValue, parseLabelFromBackToLabelLink, parsePublisher, reformatImageUrl, splitUrl } from '../utils/Parse'; +import AlbumInfoParser from '../album/AlbumInfoParser'; + +interface TrackInfoParseOptions { + trackUrl?: string; + imageBaseUrl: string; + albumImageFormat: ImageFormat | null; + artistImageFormat: ImageFormat | null; + includeRawData: boolean; +} + +export default class TrackInfoParser { + + static parseInfo(html: string, opts: TrackInfoParseOptions): Track { + // Some tracks don't have a dedicated '/track' url, + // But take this form instead: {albumUrl}#t{x}, where 'x' is the + // Track position. These tracks are not displayed as links nor playable. + // Since the album page is actually loaded, we can return the track info + // From the album data with parseAlbumInfo(). + if (opts.trackUrl) { + const { path: trackUrlPath, hash: trackUrlHash } = splitUrl(opts.trackUrl); + if (trackUrlPath && trackUrlHash) { + const matchTrackPosInUrl = (/^\/(album)\/(.+)#t(\d+)/).exec(trackUrlPath + trackUrlHash); + if (matchTrackPosInUrl && matchTrackPosInUrl[3]) { + return this.#parseTrackInfoFromAlbum(html, opts, Number(matchTrackPosInUrl[3])); + } + } + } + + const $ = cheerioLoad(html); + const rawBasic = $('script[type="application/ld+json"]').html(); + const rawExtra = decode($('script[data-tralbum]').attr('data-tralbum')); + + if (!rawBasic || !rawExtra) { + throw new ParseError('Failed to parse track info: data missing \'ld_json\' or \'tralbum\' fields.', html); + } + + let basic, extra; + try { + basic = JSON.parse(rawBasic); + } + catch (error: any) { + throw new ParseError('Failed to parse track info: JSON error in basic data.', rawBasic, error); + } + try { + extra = JSON.parse(rawExtra); + } + catch (error: any) { + throw new ParseError('Failed to parse track info: JSON error in extra data.', rawExtra, error); + } + + if (!basic || typeof basic !== 'object') { + throw new ParseError('Failed to parse track info: invalid basic data'); + } + if (!extra || typeof extra !== 'object') { + throw new ParseError('Failed to parse track info: invalid extra data'); + } + + const track: Track = { + type: 'track', + name: basic.name, + url: basic['@id'] + }; + + const imageUrl = reformatImageUrl(basic.image, opts.albumImageFormat); + if (imageUrl) { + track.imageUrl = imageUrl; + } + + if (extra.current?.release_date) { + track.releaseDate = extra.current.release_date; + } + + const duration = getAdditionalPropertyValue(basic, 'duration_secs'); + if (duration !== undefined) { + track.duration = duration; + } + + const streamUrl = extra.trackinfo?.[0]?.file?.['mp3-128']; + if (streamUrl) { + track.streamUrl = streamUrl; + } + + let byArtist; + if (basic.inAlbum?.byArtist) { + byArtist = basic.inAlbum.byArtist; + } + else { + byArtist = basic.byArtist; + } + if (byArtist) { + track.artist = { + name: byArtist.name + }; + if (byArtist['@id']) { + track.artist.url = byArtist['@id']; + } + } + + const publisher = parsePublisher(basic, opts.artistImageFormat); + if (publisher) { + track.publisher = publisher; + } + + if (track.artist && !track.artist.url && publisher?.url) { + track.artist.url = publisher.url; + } + + const label = parseLabelFromBackToLabelLink($); + if (label) { + track.label = { + name: label.name, + url: label.url + }; + } + + if (basic.inAlbum?.['@id']) { + track.album = { + name: basic.inAlbum.name, + url: basic.inAlbum['@id'], + releaseDate: extra.album_release_date + }; + track.releaseDate = extra.album_release_date; + } + + if (opts.includeRawData) { + track.raw = { basic, extra }; + } + + return track; + } + + static #parseTrackInfoFromAlbum(html: string, opts: TrackInfoParseOptions, trackPosition: number): Track { + const album = AlbumInfoParser.parseInfo(html, opts); + const trackData = album.tracks?.[trackPosition - 1]; + if (trackData) { + const track: Track = { + type: 'track', + name: trackData.name, + url: trackData.url, + imageUrl: album.imageUrl, + releaseDate: album.releaseDate, + duration: trackData.duration, + artist: album.artist, + publisher: album.publisher, + label: album.label, + album: { + name: album.name, + url: album.url, + releaseDate: album.releaseDate + } + }; + if (trackData.streamUrl) { + track.streamUrl = trackData.streamUrl; + } + return track; + } + + throw new ParseError(`Track not found in album (position: ${trackPosition}`, html); + } +} diff --git a/src/lib/types/Album.ts b/src/lib/types/Album.ts new file mode 100644 index 0000000..6a18306 --- /dev/null +++ b/src/lib/types/Album.ts @@ -0,0 +1,24 @@ +import Track from './Track'; +import MediaKind from './MediaKind'; + +interface Album extends MediaKind { + type: 'album'; + numTracks?: number; + keywords?: string[]; + description?: string; + genre?: string; + location?: string; + featuredTrack?: Omit; + releases?: AlbumRelease[]; + tracks?: Omit[]; +} + +export interface AlbumRelease { + name: string; + format: string; + url?: string; + imageUrl?: string; + description?: string; +} + +export default Album; diff --git a/src/lib/types/Article.ts b/src/lib/types/Article.ts new file mode 100644 index 0000000..093f829 --- /dev/null +++ b/src/lib/types/Article.ts @@ -0,0 +1,71 @@ +import Album from './Album'; +import Track from './Track'; + +interface Article { + title: string; + description: string; + url: string; + imageUrl: string; + date: string; + category: ArticleCategory; + genre?: { + name: string, + url?: string, + readMoreUrl?: string + }; + author: { + name: string, + url: string + }; + mediaItems: Array; + sections: ArticleSection[]; + raw?: { + basic: any, + mediaItems: any, + body: string + } +} + +export interface ArticleCategory { + name: string; + url?: string; +} + +export interface ArticleCategorySection { + name: string; + title?: string; + sections?: ArticleCategorySection[]; + categories?: ArticleCategory[]; +} + +export interface ArticleSection { + heading?: { + html: string, + text: string + }; + html: string; + text: string; + mediaItemRef?: string; +} + +export type ArticleMediaItem = (Album | Track) & { + mediaItemRef?: string, + featuredTrackPosition: number +}; + +export interface ArticleList { + articles: ArticleListItem[]; + total: number; + start: number; + end: number; +} + +export interface ArticleListItem { + url: string; + title: string; + date: string; + category?: ArticleCategory; + imageUrl?: string; +} + +export default Article; diff --git a/src/lib/types/Artist.ts b/src/lib/types/Artist.ts new file mode 100644 index 0000000..e7db458 --- /dev/null +++ b/src/lib/types/Artist.ts @@ -0,0 +1,10 @@ +import Label from './Label'; +import UserKind from './UserKind'; + +interface Artist extends UserKind { + type: 'artist'; + label?: Label; + genre?: string; +} + +export default Artist; diff --git a/src/lib/types/Autocomplete.ts b/src/lib/types/Autocomplete.ts new file mode 100644 index 0000000..c0709a3 --- /dev/null +++ b/src/lib/types/Autocomplete.ts @@ -0,0 +1,16 @@ +export interface AutocompleteItem { + type: 'tag' | 'location'; + name: string; +} + +export interface AutoCompleteTag extends AutocompleteItem { + type: 'tag'; + count: number; + value: 'string'; +} + +export interface AutocompleteLocation extends AutocompleteItem { + type: 'location'; + fullName: string; + value: number; +} diff --git a/src/lib/types/Discovery.ts b/src/lib/types/Discovery.ts new file mode 100644 index 0000000..a63c8ca --- /dev/null +++ b/src/lib/types/Discovery.ts @@ -0,0 +1,60 @@ +import NameValuePair from '../utils/NameValuePair'; +import Album from './Album'; +import { ImageFormat } from './Image'; + +/** + * Options and list of values for each option that can be used to formulate {@link DiscoverParams}. + * + * @see DiscoveryAPI.getAvailableOptions + */ +export interface DiscoverOptions { + genres: NameValuePair[]; + sortBys: NameValuePair[]; + times: (NameValuePair & { title: string })[]; + subgenres: Record[]>; + locations: NameValuePair[]; + formats: NameValuePair[]; + artistRecommendationTypes: NameValuePair[]; +} + +/** + * Params used in discovery requests. + * + * @see DiscoveryAPI.discover + */ +export interface DiscoverParams { + genre?: string; + sortBy?: string; + page?: number; + time?: number; + subgenre?: string; + location?: string; + format?: string; + artistRecommendationType?: string; + /** + * Value indicating the image format to adopt when constructing image URLs of discovered albums. + */ + albumImageFormat?: string | number | ImageFormat; + /** + * Value indicating the image format to adopt when constructing image URLs of album artists. + */ + artistImageFormat?: string | number | ImageFormat; +} + +/** + * Results returned by {@link DiscoveryAPI.discover}. + */ +export interface DiscoverResult { + /** + * List of discovered albums. + */ + items: Album[]; + /** + * Total number of albums discovered. + */ + total: number; + /** + * Sanitized params used in the discovery request. + */ + params: DiscoverParams; +} diff --git a/src/lib/types/Fan.ts b/src/lib/types/Fan.ts new file mode 100644 index 0000000..e963b96 --- /dev/null +++ b/src/lib/types/Fan.ts @@ -0,0 +1,18 @@ +import UserKind from './UserKind'; + +interface Fan extends UserKind { + type: 'fan'; + username: string; + websiteUrl: string; + followingGenresCount: number; + followingArtistsAndLabelsCount: number; + wishlistItemCount: number; + collectionItemCount: number; +} + +export interface FanItemsContinuation { + fanId: number; + token: string; +} + +export default Fan; diff --git a/src/lib/types/Image.ts b/src/lib/types/Image.ts new file mode 100644 index 0000000..74282f3 --- /dev/null +++ b/src/lib/types/Image.ts @@ -0,0 +1,12 @@ +export interface ImageFormat { + id: number; + name: string; + width: number; + height: number; + fileFormat: string; +} + +export interface ImageConstants { + baseUrl: string; + formats: ImageFormat[]; +} diff --git a/src/lib/types/Label.ts b/src/lib/types/Label.ts new file mode 100644 index 0000000..d912c06 --- /dev/null +++ b/src/lib/types/Label.ts @@ -0,0 +1,11 @@ +import Artist from './Artist'; +import UserKind from './UserKind'; + +interface Label extends UserKind { + type: 'label'; + labelId?: number; +} + +export type LabelArtist = Omit; + +export default Label; diff --git a/src/lib/types/MediaKind.ts b/src/lib/types/MediaKind.ts new file mode 100644 index 0000000..e3a185a --- /dev/null +++ b/src/lib/types/MediaKind.ts @@ -0,0 +1,19 @@ +import Artist from './Artist'; +import Label from './Label'; +import UserKind from './UserKind'; + +interface MediaKind { + name: string; + url?: string; + imageUrl?: string; + releaseDate?: string; + artist?: Omit; + label?: Omit; + publisher?: UserKind; + raw?: { + basic: string, + extra: string + }; +} + +export default MediaKind; diff --git a/src/lib/types/Search.ts b/src/lib/types/Search.ts new file mode 100644 index 0000000..094d741 --- /dev/null +++ b/src/lib/types/Search.ts @@ -0,0 +1,47 @@ +export interface SearchResults { + items: T[]; + totalPages: number; +} + +export interface SearchResultItem { + type: 'artist' | 'label' | 'album' | 'track' | 'fan'; + name: string; + url: string; + imageUrl?: string; +} + +export interface SearchResultArtist extends SearchResultItem { + type: 'artist'; + location?: string; + genre?: string; + tags?: string; +} + +export interface SearchResultLabel extends SearchResultItem { + type: 'label'; + location?: string; +} + +export interface SearchResultAlbum extends SearchResultItem { + type: 'album'; + artist?: string; + numTracks?: number; + duration?: number; + releaseDate?: string; + tags?: string; +} + +export interface SearchResultTrack extends SearchResultItem { + type: 'track'; + artist?: string; + album?: string; + releaseDate?: string; + tags?: string; +} + +export interface SearchResultFan extends SearchResultItem { + type: 'fan'; + genre?: string; +} + +export type SearchResultAny = SearchResultArtist | SearchResultLabel | SearchResultAlbum | SearchResultTrack | SearchResultFan; diff --git a/src/lib/types/Show.ts b/src/lib/types/Show.ts new file mode 100644 index 0000000..a3a3be3 --- /dev/null +++ b/src/lib/types/Show.ts @@ -0,0 +1,22 @@ +import Track from './Track'; + +interface Show { + type: 'show'; + name: string; + url: string; + publishedDate: string; + description: string; + shortDescription?: string; + duration?: number; + streamUrl?: { + 'mp3-128'?: string; + 'opus-lo'?: string; + }; + tracks?: Omit[]; + imageCaption: string; + subtitle: string; + imageUrl?: string; + screenImageUrl: string; +} + +export default Show; diff --git a/src/lib/types/Tag.ts b/src/lib/types/Tag.ts new file mode 100644 index 0000000..95bb11e --- /dev/null +++ b/src/lib/types/Tag.ts @@ -0,0 +1,50 @@ +import NameValuePair from '../utils/NameValuePair'; +import Album from './Album'; +import Track from './Track'; + +interface Tag { + type: 'tag' | string; + name: string; + url: string; + value?: string; + related?: Tag[]; + isLocation?: boolean; + imageUrls?: string[]; +} + +export interface TagList { + tags: Omit[]; + locations: Omit[]; +} + +export interface AlbumHighlightsByTag { + name: string; + title: string; + items: Album[]; +} + +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace ReleasesByTag { + + export interface FilterValueNames { + [k: string]: Record; + } + + export interface FilterOption extends NameValuePair { + default?: boolean; + selected?: boolean; + } + + export interface Filter { + name: string; + options: FilterOption[]; + } +} + +export interface ReleasesByTag { + items: Array; + hasMore: boolean; + filters: Record>; +} + +export default Tag; diff --git a/src/lib/types/Track.ts b/src/lib/types/Track.ts new file mode 100644 index 0000000..8f00d54 --- /dev/null +++ b/src/lib/types/Track.ts @@ -0,0 +1,13 @@ +import Album from './Album'; +import MediaKind from './MediaKind'; + +interface Track extends MediaKind { + type: 'track'; + duration?: number; + seekPosition?: number; + streamUrl?: string; + album?: Omit; + position?: number; +} + +export default Track; diff --git a/src/lib/types/UserKind.ts b/src/lib/types/UserKind.ts new file mode 100644 index 0000000..54aabe4 --- /dev/null +++ b/src/lib/types/UserKind.ts @@ -0,0 +1,9 @@ +interface UserKind { + name: string; + description?: string; + url?: string; + imageUrl?: string; + location?: string; +} + +export default UserKind; diff --git a/src/lib/utils/Cache.ts b/src/lib/utils/Cache.ts new file mode 100644 index 0000000..3022357 --- /dev/null +++ b/src/lib/utils/Cache.ts @@ -0,0 +1,99 @@ +import NodeCache from 'node-cache'; + +export enum CacheDataType { + Page = 'Page', + Constants = 'Constants' +} + +class CacheImpl { + #ttl: Record; + #maxEntries: Record; + #cache: NodeCache; + + constructor(ttl: Record, maxEntries: Record) { + this.#ttl = ttl; + this.#maxEntries = maxEntries; + this.#cache = new NodeCache({ + checkperiod: 600 + }); + } + + setTTL(type: CacheDataType, ttl: number) { + if (this.#ttl[type] !== ttl) { + this.getKeys(type).forEach((key) => { + this.#cache.ttl(key, ttl); + }); + this.#ttl[type] = ttl; + } + } + + setMaxEntries(type: CacheDataType, maxEntries: number) { + this.reduceEntries(type, maxEntries); + this.#maxEntries[type] = maxEntries; + } + + getMaxEntries(type: CacheDataType) { + return this.#maxEntries[type] !== undefined ? this.#maxEntries[type] : -1; + } + + get(type: CacheDataType, key: string): T | undefined { + return this.#cache.get(this.#getCacheKey(type, key)); + } + + put(type: CacheDataType, key: string, value: T) { + const maxEntries = this.getMaxEntries(type); + if (maxEntries === 0) { + return false; + } + else if (maxEntries > 0) { + this.reduceEntries(type, maxEntries - 1); + } + return this.#cache.set(this.#getCacheKey(type, key), value, this.#ttl[type]); + } + + #getCacheKey(type: CacheDataType, key: string): string { + return `${type}.${key}`; + } + + reduceEntries(type: CacheDataType, reduceTo?: number) { + if (reduceTo === undefined) { + reduceTo = this.getMaxEntries(type); + } + const keys = this.getKeys(type); + if (keys.length > reduceTo) { + for (let i = 0; i < keys.length - reduceTo; i++) { + this.#cache.del(keys[i]); + } + } + } + + getKeys(type: CacheDataType): string[] { + return this.#cache.keys().filter((key) => key.startsWith(`${type}.`)); + } + + clear(type?: CacheDataType) { + if (!type) { + this.#cache.flushAll(); + } + else { + this.getKeys(type).forEach((key) => { + this.#cache.del(key); + }); + } + } + + async getOrSet(type: CacheDataType, key: string, promiseCallback: () => Promise): Promise { + const cachedValue = this.get(type, key); + if (cachedValue !== undefined) { + return cachedValue; + } + const value = await promiseCallback(); + this.put(type, key, value); + return value; + } +} + +export const Cache = new CacheImpl({ + [CacheDataType.Constants]: 3600, + [CacheDataType.Page]: 300 +}, { page: 10 }); diff --git a/src/lib/utils/Constants.ts b/src/lib/utils/Constants.ts new file mode 100644 index 0000000..e328b35 --- /dev/null +++ b/src/lib/utils/Constants.ts @@ -0,0 +1,21 @@ +const SITE_URL = 'https://bandcamp.com'; +const API_URL = `${SITE_URL}/api`; + +export const URLS = { + SITE_URL, + DISCOVER_URL: `${API_URL}/discover/3/get_web`, + DIG_DEEPER: `${API_URL}/hub/2/dig_deeper`, + DAILY: 'https://daily.bandcamp.com', + SHOWS: `${API_URL}/bcweekly/3/list`, + FAN_CONTINUATION: { + COLLECTION: `${API_URL}/fancollection/1/collection_items`, + WISHLIST: `${API_URL}/fancollection/1/wishlist_items`, + FOLLOWING_BANDS: `${API_URL}/fancollection/1/following_bands`, + FOLLOWING_GENRES: `${API_URL}/fancollection/1/following_genres` + }, + SEARCH: `${SITE_URL}/search`, + AUTOCOMPLETE: { + TAG: `${API_URL}/fansignup/1/search_tag`, + LOCATION: `${API_URL}/location/1/geoname_search` + } +}; diff --git a/src/lib/utils/Fetch.ts b/src/lib/utils/Fetch.ts new file mode 100644 index 0000000..d2bd49f --- /dev/null +++ b/src/lib/utils/Fetch.ts @@ -0,0 +1,81 @@ +import { URL } from 'url'; +import fetch from 'node-fetch'; +import { Cache, CacheDataType } from './Cache'; + +export enum FetchMethod { + GET = 'GET', + POST = 'POST' +} + +export function fetchPage(url: string, jsonResponse: true, method?: FetchMethod, payload?: Record): Promise; +export function fetchPage(url: string, jsonResponse?: boolean, method?: FetchMethod, payload?: Record): Promise; +export function fetchPage(url: string, jsonResponse?: boolean, method?: FetchMethod, payload?: Record) { + if (jsonResponse === undefined) { + jsonResponse = false; + } + return Cache.getOrSet(CacheDataType.Page, getCacheKey(url, jsonResponse, payload), async () => { + if (method === undefined) { + method = FetchMethod.GET; + } + let response; + if (method === FetchMethod.GET) { + const urlObj = new URL(url); + if (payload) { + for (const [ key, value ] of Object.entries(payload)) { + urlObj.searchParams.set(key, value); + } + } + try { + response = await fetch(urlObj.toString()); + } + catch (error) { + throw new FetchError(error); + } + } + else { + const init = { + method: 'POST', + body: payload ? JSON.stringify(payload) : undefined, + headers: { 'Content-Type': 'application/x-www-form-urlencoded' } + }; + try { + response = await fetch(url, init); + } + catch (error) { + throw new FetchError(error); + } + } + if (response.status === 429) { + throw new FetchError({ + message: '429 Too Many Requests', + code: 429 + }); + } + if (jsonResponse) { + return response.json(); + } + return response.text(); + }); +} + +function getCacheKey(url: string, jsonResponse: boolean, payload?: Record): string { + return url + (jsonResponse ? ':json' : ':html') + + (payload ? `:${JSON.stringify(payload)}` : ''); +} + +export class FetchError extends Error { + code?: number; + + constructor(payload: any) { + super(); + if (payload?.message) { + this.message = payload.message; + } + if (payload?.code) { + this.code = payload.code; + } + if (payload?.stack) { + this.stack = payload.stack; + } + } +} diff --git a/src/lib/utils/Limiter.ts b/src/lib/utils/Limiter.ts new file mode 100644 index 0000000..cb90693 --- /dev/null +++ b/src/lib/utils/Limiter.ts @@ -0,0 +1,16 @@ +import Bottleneck from 'bottleneck'; + +const _limiter = new Bottleneck({ + maxConcurrent: 5, + minTime: 200 +}); + +export default class Limiter { + static updateSettings(options?: Bottleneck.ConstructorOptions) { + _limiter.updateSettings(options); + } + + static schedule(fn: () => PromiseLike): Promise { + return _limiter.schedule(fn); + } +} diff --git a/src/lib/utils/NameValuePair.ts b/src/lib/utils/NameValuePair.ts new file mode 100644 index 0000000..941add7 --- /dev/null +++ b/src/lib/utils/NameValuePair.ts @@ -0,0 +1,6 @@ +interface NameValuePair { + name: string; + value: T; +} + +export default NameValuePair; diff --git a/src/lib/utils/Parse.ts b/src/lib/utils/Parse.ts new file mode 100644 index 0000000..ed842f2 --- /dev/null +++ b/src/lib/utils/Parse.ts @@ -0,0 +1,168 @@ +import { load as cheerioLoad } from 'cheerio'; +import Label from '../types/Label'; +import { URLS } from './Constants'; +import UserKind from '../types/UserKind'; +import { ImageFormat } from '../types/Image'; + +export interface BackToLabel { + text: string | null; + url: string; +} + +export interface UrlParts { + base: string; + path: string; + query: string; + hash: string; + searchParams: URLSearchParams; +} + +type CheerioSelector = ReturnType; + +/** + * @internal + * + * @param $ + * @returns + */ +export function parseLabelFromBackToLabelLink($: CheerioSelector): Label | null { + const labelLink = $('.back-to-label-link'); + if (labelLink.length) { + const linkText = labelLink.find('.back-link-text').html(); + const labelName = linkText && + (substrAfter(linkText, '
') || + substrAfter(linkText, '
') || + substrBefore(linkText, ' に戻る') || + substrBefore(linkText, ' のアイテムをもっと聴く')); + const linkHref = labelLink.attr('href'); + const labelHref = linkHref && splitUrl(linkHref).base; + + return labelName && labelHref ? { + type: 'label', + name: labelName, + url: labelHref + } : null; + } + + return null; +} + +export function parsePublisher(json: any, imageFormat: ImageFormat | null): UserKind | null { + if (json.publisher) { + const imageUrl = reformatImageUrl(json.publisher.image, imageFormat); + const publisher: UserKind = { + name: json.publisher.name, + url: json.publisher['@id'], + description: json.publisher.description + }; + if (imageUrl) { + publisher.imageUrl = imageUrl; + } + return publisher; + } + return null; +} + +/** + * @internal + * + * @param url + * @param baseUrl + * @returns + */ +export function normalizeUrl(url: string, baseUrl?: string): string; +export function normalizeUrl(url?: string, baseUrl?: string): string | null { + if (!url) { + return null; + } + if (isAbsoluteUrl(url)) { + return url; + } + if (!baseUrl) { + baseUrl = URLS.SITE_URL; + } + return new URL(url, baseUrl).toString(); +} + +export function getAdditionalPropertyValue(o: Record, propName: string): T | undefined { + if (Array.isArray(o.additionalProperty)) { + const p = o.additionalProperty.find((prop) => prop.name === propName); + if (p?.value !== undefined) { + return p.value; + } + } + return undefined; +} + +export function isAbsoluteUrl(url: string) { + const isAbsolute = new RegExp('^([a-z]+://|//)', 'i'); + return isAbsolute.test(url); +} + +export function substrAfter(str: string, after: string): string | null { + const afterIndex = str.indexOf(after); + return afterIndex >= 0 ? str.substring(afterIndex + after.length) : null; +} + +export function substrBefore(str: string, before: string): string | null { + const beforeIndex = str.indexOf(before); + return beforeIndex >= 0 ? str.substring(0, beforeIndex) : null; +} + +export function splitUrl(url: string): UrlParts { + const urlObj = new URL(url); + return { + base: `${urlObj.protocol}//${urlObj.host}`, + path: urlObj.pathname, + query: urlObj.search, + hash: urlObj.hash, + searchParams: urlObj.searchParams + }; +} + +export function stripTags(str: string): string { + // https://css-tricks.com/snippets/javascript/strip-html-tags-in-javascript/ + return str.replace(/(<([^>]+)>)/gi, ''); +} + +export function stripLineBreaks(str: string): string { + return str.replace(/(\r\n|\n|\r)/gm, ' '); +} + +export function stripMultipleWhitespaces(str: string) { + return str.replace(/\s+/g, ' '); +} + +export function brToNewLine(str: string): string { + // https://stackoverflow.com/questions/5959415/jquery-javascript-regex-replace-br-with-n + return str.replace(//gi, '\n'); +} + +/** +* @internal +* Takes an image URL and returns one that corresponds to `imageFormat`. +* @param imageUrl +* @param imageFormat +* @returns +*/ +export function reformatImageUrl(imageUrl?: string | null, imageFormat?: ImageFormat | null): string | null { + if (imageUrl) { + // Regex from: https://github.com/masterT/bandcamp-scraper/blob/master/lib/htmlParser.js + return imageFormat ? imageUrl.replace(/_\d{1,3}\./, `_${imageFormat.id}.`) : imageUrl; + } + + return null; +} + +export class ParseError extends Error { + parseTarget?: string; + cause?: Error; + + constructor(message: string, parseTarget?: string, cause?: any) { + super(message); + this.parseTarget = parseTarget; + if (cause) { + this.cause = cause instanceof Error ? cause : Error(cause.toString()); + } + } +} diff --git a/tsconfig-base.json b/tsconfig-base.json new file mode 100644 index 0000000..1a0d7de --- /dev/null +++ b/tsconfig-base.json @@ -0,0 +1,18 @@ +{ + "include": ["src/**/*.ts"], + "compilerOptions": { + "target": "es2020", + "declaration": true, + "declarationMap": true, + "esModuleInterop": true, + "moduleResolution": "node", + "sourceMap": true, + "inlineSources": true, + "strict": true, + "strictPropertyInitialization": false, + "typeRoots": [ + "src/typings", + "node_modules/@types" + ] + } +} diff --git a/tsconfig-esm.json b/tsconfig-esm.json new file mode 100644 index 0000000..c366602 --- /dev/null +++ b/tsconfig-esm.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig-base.json", + "exclude": ["src/index-cjs.ts"], + "compilerOptions": { + "module": "es2020", + "outDir": "dist/mjs" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..7aae811 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig-base.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "dist/cjs" + } +} diff --git a/typedoc.json b/typedoc.json new file mode 100644 index 0000000..6c5059b --- /dev/null +++ b/typedoc.json @@ -0,0 +1,9 @@ +{ + "plugin": ["typedoc-plugin-markdown", "typedoc-plugin-rename-defaults"], + "excludePrivate": true, + "excludeExternals": true, + "excludeInternal": true, + "entryPoints": ["src/index.ts"], + "readme": "none", + "out": "docs/api" +}