diff --git a/dist/index.js b/dist/index.js index a895f8d..2631ca6 100644 --- a/dist/index.js +++ b/dist/index.js @@ -14,7 +14,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; @@ -66,6 +66,7 @@ var download_1 = require("./download"); var is_url_1 = __importDefault(require("./is-url")); var protocols_1 = require("./protocols"); var formats_1 = require("./formats"); +var search_1 = require("./search"); /** @internal */ var download = function (url, clientID) { return __awaiter(void 0, void 0, void 0, function () { var info; @@ -211,6 +212,26 @@ var SCDL = /** @class */ (function () { }); }); }; + /** + * Searches for tracks/playlists for the given query + * @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) { + 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]; + return [4 /*yield*/, this._assignClientID(clientID)]; + case 1: return [2 /*return*/, _a.apply(void 0, _b.concat([_c.sent()]))]; + } + }); + }); + }; /** * Returns whether or not the given URL is a valid Soundcloud URL * @param url - URL of the Soundcloud track diff --git a/dist/search.js b/dist/search.js new file mode 100644 index 0000000..4306ef0 --- /dev/null +++ b/dist/search.js @@ -0,0 +1,58 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +exports.__esModule = true; +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 () { + 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 1: + data = (_a.sent()).data; + return [2 /*return*/, data]; + } + }); +}); }; diff --git a/src/index.ts b/src/index.ts index 3e560db..2e1a090 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,6 +8,7 @@ import isValidURL from './is-url' import STREAMING_PROTOCOLS, { _PROTOCOLS } from './protocols' import FORMATS, { _FORMATS } from './formats' +import { search } from './search' /** @internal */ const download = async (url: string, clientID: string) => { @@ -92,6 +93,16 @@ export class SCDL { return getSetInfo(url, await this._assignClientID(clientID), full) } + /** + * Searches for tracks/playlists for the given query + * @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)) + } + /** * Returns whether or not the given URL is a valid Soundcloud URL * @param url - URL of the Soundcloud track diff --git a/src/search.ts b/src/search.ts new file mode 100644 index 0000000..5ae6bd5 --- /dev/null +++ b/src/search.ts @@ -0,0 +1,19 @@ +/* eslint-disable camelcase */ +import axios from 'axios' +import { TrackInfo } from './info' + +/** @internal */ +const baseURL = 'https://api-v2.soundcloud.com/search' + +export type SearchResponse = { + collection: TrackInfo[] + total_results: number, + next_href: string, + query_urn: string +} + +/** @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 +} diff --git a/tests/search.test.js b/tests/search.test.js new file mode 100644 index 0000000..891f207 --- /dev/null +++ b/tests/search.test.js @@ -0,0 +1,19 @@ +/** + * @jest-environment node + */ + +import scdl from '../' + +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 keys = ['collection', 'total_results', 'query_urn'].forEach(key => expect(searchResponse[key]).toBeDefined()) + done() + } catch (err) { + console.error(err) + done(err) + } + }) +})