diff --git a/dist/index.js b/dist/index.js index 2631ca6..e3d25fe 100644 --- a/dist/index.js +++ b/dist/index.js @@ -170,22 +170,21 @@ var SCDL = /** @class */ (function () { }); }; /** - * Returns info about a given track. - * @param id - The track ID + * Returns info about the given track(s) specified by ID. + * @param ids - The ID(s) of the tracks * @param clientID - A Soundcloud Client ID, will find one if not provided * @returns Info about the track */ - SCDL.prototype.getTrackInfoByID = function (id, clientID) { + SCDL.prototype.getTrackInfoByID = function (ids, clientID) { return __awaiter(this, void 0, void 0, function () { - var _a, _b; - return __generator(this, function (_c) { - switch (_c.label) { + var _a; + return __generator(this, function (_b) { + switch (_b.label) { case 0: _a = info_1.getTrackInfoByID; - _b = [id]; return [4 /*yield*/, this._assignClientID(clientID)]; - case 1: return [4 /*yield*/, _a.apply(void 0, _b.concat([_c.sent()]))]; - case 2: return [2 /*return*/, _c.sent()]; + case 1: return [4 /*yield*/, _a.apply(void 0, [_b.sent(), ids])]; + case 2: return [2 /*return*/, _b.sent()]; } }); }); @@ -197,8 +196,7 @@ var SCDL = /** @class */ (function () { * @param clientID - A Soundcloud Client ID, will find one if not provided * @returns Info about the set */ - SCDL.prototype.getSetInfo = function (url, full, clientID) { - if (full === void 0) { full = false; } + SCDL.prototype.getSetInfo = function (url, clientID) { return __awaiter(this, void 0, void 0, function () { var _a, _b; return __generator(this, function (_c) { @@ -207,25 +205,26 @@ var SCDL = /** @class */ (function () { _a = info_1.getSetInfo; _b = [url]; return [4 /*yield*/, this._assignClientID(clientID)]; - case 1: return [2 /*return*/, _a.apply(void 0, _b.concat([_c.sent(), full]))]; + case 1: return [2 /*return*/, _a.apply(void 0, _b.concat([_c.sent()]))]; } }); }); }; /** * Searches for tracks/playlists for the given query + * @param type - The type of resource, one of: 'tracks', 'people', 'albums', 'sets', 'all' * @param query - The keywords for the search * @param clientID - A Soundcloud Client ID, will find one if not provided * @returns SearchResponse */ - SCDL.prototype.search = function (query, clientID) { + SCDL.prototype.search = function (type, query, clientID) { return __awaiter(this, void 0, void 0, function () { var _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: _a = search_1.search; - _b = [query]; + _b = [type, query]; return [4 /*yield*/, this._assignClientID(clientID)]; case 1: return [2 /*return*/, _a.apply(void 0, _b.concat([_c.sent()]))]; } diff --git a/dist/info.js b/dist/info.js index 909a7d6..c87bd97 100644 --- a/dist/info.js +++ b/dist/info.js @@ -44,13 +44,13 @@ exports.getTrackInfoByID = exports.getSetInfo = exports.getInfoBase = void 0; var axios_1 = __importDefault(require("axios")); var util_1 = require("./util"); /** @internal */ -var getTrackInfoBase = function (trackID, clientID, axiosRef) { return __awaiter(void 0, void 0, void 0, function () { +var getTrackInfoBase = function (clientID, axiosRef, ids) { return __awaiter(void 0, void 0, void 0, function () { var data, err_1; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); - return [4 /*yield*/, axiosRef.get("https://api-v2.soundcloud.com/tracks/" + trackID + "?client_id=" + clientID)]; + return [4 /*yield*/, axiosRef.get("https://api-v2.soundcloud.com/tracks?ids=" + ids.join(',') + "&client_id=" + clientID)]; case 1: data = (_a.sent()).data; return [2 /*return*/, data]; @@ -83,40 +83,20 @@ exports.getInfoBase = function (url, clientID, axiosRef) { return __awaiter(void }); }); }; /** @internal */ -var getSetInfoBase = function (url, clientID, full, axiosRef) { return __awaiter(void 0, void 0, void 0, function () { - var setInfo, incompleteTracks, completeTracks, _i, incompleteTracks_1, track, info, err_3; +var getSetInfoBase = function (url, clientID, axiosRef) { return __awaiter(void 0, void 0, void 0, function () { + var setInfo, incompleteTracks, completeTracks, ids, info; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, exports.getInfoBase(url, clientID, axiosRef)]; case 1: setInfo = _a.sent(); - if (!full) - return [2 /*return*/, setInfo]; incompleteTracks = setInfo.tracks.filter(function (track) { return !track.title; }); completeTracks = setInfo.tracks.filter(function (track) { return track.title; }); - _i = 0, incompleteTracks_1 = incompleteTracks; - _a.label = 2; + ids = incompleteTracks.map(function (t) { return t.id; }); + return [4 /*yield*/, exports.getTrackInfoByID(clientID, ids)]; case 2: - if (!(_i < incompleteTracks_1.length)) return [3 /*break*/, 7]; - track = incompleteTracks_1[_i]; - _a.label = 3; - case 3: - _a.trys.push([3, 5, , 6]); - return [4 /*yield*/, getTrackInfoBase(track.id, clientID, axiosRef)]; - case 4: info = _a.sent(); - completeTracks.push(info); - return [3 /*break*/, 6]; - case 5: - err_3 = _a.sent(); - console.log(err_3); - completeTracks.push(track); - return [3 /*break*/, 6]; - case 6: - _i++; - return [3 /*break*/, 2]; - case 7: - setInfo.tracks = completeTracks; + setInfo.tracks = completeTracks.concat(info); return [2 /*return*/, setInfo]; } }); @@ -136,27 +116,24 @@ var getInfo = function (url, clientID) { return __awaiter(void 0, void 0, void 0 }); }); }; /** @internal */ -exports.getSetInfo = function (url, clientID, full) { - if (full === void 0) { full = false; } - return __awaiter(void 0, void 0, void 0, function () { - var data; - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, getSetInfoBase(url, clientID, full, axios_1["default"])]; - case 1: - data = _a.sent(); - if (!data.tracks) - throw new Error('The given URL does not link to a Soundcloud set'); - return [2 /*return*/, data]; - } - }); +exports.getSetInfo = function (url, clientID) { return __awaiter(void 0, void 0, void 0, function () { + var data; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, getSetInfoBase(url, clientID, axios_1["default"])]; + case 1: + data = _a.sent(); + if (!data.tracks) + throw new Error('The given URL does not link to a Soundcloud set'); + return [2 /*return*/, data]; + } }); -}; +}); }; /** @intenral */ -exports.getTrackInfoByID = function (id, clientID) { return __awaiter(void 0, void 0, void 0, function () { +exports.getTrackInfoByID = function (clientID, ids) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { - case 0: return [4 /*yield*/, getTrackInfoBase(id, clientID, axios_1["default"])]; + case 0: return [4 /*yield*/, getTrackInfoBase(clientID, axios_1["default"], ids)]; case 1: return [2 /*return*/, _a.sent()]; } }); diff --git a/dist/search.js b/dist/search.js index 4306ef0..324c424 100644 --- a/dist/search.js +++ b/dist/search.js @@ -39,20 +39,36 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; exports.__esModule = true; -exports.search = void 0; +exports.related = exports.search = void 0; /* eslint-disable camelcase */ var axios_1 = __importDefault(require("axios")); /** @internal */ var baseURL = 'https://api-v2.soundcloud.com/search'; /** @internal */ -exports.search = function (query, clientID) { return __awaiter(void 0, void 0, void 0, function () { +exports.search = function (type, query, clientID) { return __awaiter(void 0, void 0, void 0, function () { var data; return __generator(this, function (_a) { switch (_a.label) { - case 0: return [4 /*yield*/, axios_1["default"].get(baseURL + "?client_id=" + clientID + "&q=" + query)]; + case 0: return [4 /*yield*/, axios_1["default"].get("" + baseURL + (type === 'all' ? '' : "/" + type + "/") + "?client_id=" + clientID + "&q=" + query)]; case 1: data = (_a.sent()).data; return [2 /*return*/, data]; } }); }); }; +/** @internal */ +exports.related = function (type, id, limit, offset, clientID) { + if (limit === void 0) { limit = 10; } + if (offset === void 0) { offset = 0; } + return __awaiter(void 0, void 0, void 0, function () { + var data; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, axios_1["default"].get("https://api-v2.soundcloud.com/" + type + "/" + id + "/related?client_id=" + clientID + "&offset=" + offset + "&limit=" + limit)]; + case 1: + data = (_a.sent()).data; + return [2 /*return*/, data]; + } + }); + }); +}; diff --git a/src/index.ts b/src/index.ts index 2e1a090..2cd0a71 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,7 +8,7 @@ import isValidURL from './is-url' import STREAMING_PROTOCOLS, { _PROTOCOLS } from './protocols' import FORMATS, { _FORMATS } from './formats' -import { search } from './search' +import { search, SoundcloudResource } from './search' /** @internal */ const download = async (url: string, clientID: string) => { @@ -73,13 +73,13 @@ export class SCDL { } /** - * Returns info about a given track. - * @param id - The track ID + * Returns info about the given track(s) specified by ID. + * @param ids - The ID(s) of the tracks * @param clientID - A Soundcloud Client ID, will find one if not provided * @returns Info about the track */ - async getTrackInfoByID (id: number, clientID?: string) { - return await getTrackInfoByID(id, await this._assignClientID(clientID)) + async getTrackInfoByID (ids: number[], clientID?: string) { + return await getTrackInfoByID(await this._assignClientID(clientID), ids) } /** @@ -89,18 +89,19 @@ export class SCDL { * @param clientID - A Soundcloud Client ID, will find one if not provided * @returns Info about the set */ - async getSetInfo (url: string, full = false, clientID?: string) { - return getSetInfo(url, await this._assignClientID(clientID), full) + async getSetInfo (url: string, clientID?: string) { + return getSetInfo(url, await this._assignClientID(clientID)) } /** * Searches for tracks/playlists for the given query + * @param type - The type of resource, one of: 'tracks', 'people', 'albums', 'sets', 'all' * @param query - The keywords for the search * @param clientID - A Soundcloud Client ID, will find one if not provided * @returns SearchResponse */ - async search (query: string, clientID?: string) { - return search(query, await this._assignClientID(clientID)) + async search (type: SoundcloudResource | 'all', query: string, clientID?: string) { + return search(type, query, await this._assignClientID(clientID)) } /** diff --git a/src/info.ts b/src/info.ts index 7b853a7..7b3b8df 100644 --- a/src/info.ts +++ b/src/info.ts @@ -116,9 +116,9 @@ export interface Transcoding { } /** @internal */ -const getTrackInfoBase = async (trackID: number, clientID: string, axiosRef: AxiosInstance): Promise => { +const getTrackInfoBase = async (clientID: string, axiosRef: AxiosInstance, ids: number[]): Promise => { try { - const { data } = await axiosRef.get(`https://api-v2.soundcloud.com/tracks/${trackID}?client_id=${clientID}`) + const { data } = await axiosRef.get(`https://api-v2.soundcloud.com/tracks?ids=${ids.join(',')}&client_id=${clientID}`) return data as TrackInfo } catch (err) { @@ -141,24 +141,16 @@ export const getInfoBase = async (url: string, cl } /** @internal */ -const getSetInfoBase = async (url: string, clientID: string, full: boolean, axiosRef: AxiosInstance): Promise => { +const getSetInfoBase = async (url: string, clientID: string, axiosRef: AxiosInstance): Promise => { const setInfo = await getInfoBase(url, clientID, axiosRef) - if (!full) return setInfo const incompleteTracks = setInfo.tracks.filter(track => !track.title) const completeTracks = setInfo.tracks.filter(track => track.title) - for (const track of incompleteTracks) { - try { - const info = await getTrackInfoBase(track.id, clientID, axiosRef) - completeTracks.push(info) - } catch (err) { - console.log(err) - completeTracks.push(track) - } - } + const ids = incompleteTracks.map(t => t.id) + const info = await getTrackInfoByID(clientID, ids) - setInfo.tracks = completeTracks + setInfo.tracks = completeTracks.concat(info) return setInfo } @@ -170,14 +162,14 @@ const getInfo = async (url: string, clientID: string): Promise => { } /** @internal */ -export const getSetInfo = async (url: string, clientID: string, full = false): Promise => { - const data = await getSetInfoBase(url, clientID, full, axios) +export const getSetInfo = async (url: string, clientID: string): Promise => { + const data = await getSetInfoBase(url, clientID, axios) if (!data.tracks) throw new Error('The given URL does not link to a Soundcloud set') return data } /** @intenral */ -export const getTrackInfoByID = async (id: number, clientID: string) => { - return await getTrackInfoBase(id, clientID, axios) +export const getTrackInfoByID = async (clientID: string, ids: number[]) => { + return await getTrackInfoBase(clientID, axios, ids) } export default getInfo diff --git a/src/search.ts b/src/search.ts index 5ae6bd5..f4bf441 100644 --- a/src/search.ts +++ b/src/search.ts @@ -1,19 +1,29 @@ /* eslint-disable camelcase */ import axios from 'axios' -import { TrackInfo } from './info' +import { TrackInfo, User, SetInfo } from './info' /** @internal */ const baseURL = 'https://api-v2.soundcloud.com/search' -export type SearchResponse = { - collection: TrackInfo[] +export type SearchResponse = { + collection: T[] total_results: number, next_href: string, query_urn: string } +export type SearchResponseAll = SearchResponse + +export type SoundcloudResource = 'tracks' | 'people' | 'albums' | 'playlists' + +/** @internal */ +export const search = async (type: SoundcloudResource | 'all', query: string, clientID?: string): Promise => { + const { data } = await axios.get(`${baseURL}${type === 'all' ? '' : `/${type}/`}?client_id=${clientID}&q=${query}`) + return data as SearchResponseAll +} + /** @internal */ -export const search = async (query: string, clientID?: string): Promise => { - const { data } = await axios.get(`${baseURL}?client_id=${clientID}&q=${query}`) - return data as SearchResponse +export const related = async (type: SoundcloudResource, id: number, limit = 10, offset = 0, clientID: string): Promise> => { + const { data } = await axios.get(`https://api-v2.soundcloud.com/${type}/${id}/related?client_id=${clientID}&offset=${offset}&limit=${limit}`) + return data as SearchResponse } diff --git a/tests/getSetInfo.test.js b/tests/getSetInfo.test.js index c7f50ad..bbdd912 100644 --- a/tests/getSetInfo.test.js +++ b/tests/getSetInfo.test.js @@ -5,14 +5,15 @@ import scdl from '../' describe('getSetInfo()', () => { - describe('called with full = false', () => { + describe('returns valid SetInfo', () => { let info beforeAll(async () => { try { info = await scdl.getSetInfo('https://soundcloud.com/user-845046062/sets/playlist') + console.log(info.tracks.length) } catch (err) { - console.error(err) + console.log(err) process.exit(1) } }) @@ -24,28 +25,8 @@ describe('getSetInfo()', () => { expect(info.tracks).toBeDefined() expect(typeof info.tracks).toBe('object') }) - }) - - describe('called with full = true', () => { - let info - - beforeAll(async () => { - jest.setTimeout(20 * 1000) - try { - info = await scdl.getSetInfo('https://soundcloud.com/user-845046062/sets/playlist', true) - } catch (err) { - console.error(err) - process.exit(1) - } - }) it('returns SetInfo with every track containing full information', () => { - expect(info).toBeDefined() - expect(info.title).toBeDefined() - expect(typeof info.title).toBe('string') - expect(info.tracks).toBeDefined() - expect(typeof info.tracks).toBe('object') - for (const track of info.tracks) { expect(track.title).toBeDefined() } diff --git a/tests/getTrackInfoByID.test.js b/tests/getTrackInfoByID.test.js index 70790a1..f7ada07 100644 --- a/tests/getTrackInfoByID.test.js +++ b/tests/getTrackInfoByID.test.js @@ -6,9 +6,9 @@ import scdl from '../' describe('getTrackInfoByID()', () => { it('returns track info when given a valid url', async done => { try { - const info = await scdl.getTrackInfoByID(145997673) - expect(info.title).toBeDefined() - expect(info.title).toEqual('Logic Ft. Big Sean - Alright (Prod. By Tae Beast)') + const info = await scdl.getTrackInfoByID([145997673, 291270539]) + expect(info[0].title).toBeDefined() + expect(info[0].title).toEqual('Logic Ft. Big Sean - Alright (Prod. By Tae Beast)') done() } catch (err) { console.error(err) diff --git a/tests/search.test.js b/tests/search.test.js index 891f207..3f7d115 100644 --- a/tests/search.test.js +++ b/tests/search.test.js @@ -8,7 +8,7 @@ describe('search()', () => { it('returns a valid search object', async done => { try { const query = 'borderline tame impala' - const searchResponse = await scdl.search('outta ma mind') + const searchResponse = await scdl.search('all', query) const keys = ['collection', 'total_results', 'query_urn'].forEach(key => expect(searchResponse[key]).toBeDefined()) done() } catch (err) {