radio-browser-api
Version:
Wrapper for free and open-source radio browser api: https://api.radio-browser.info/.
1 lines • 28.1 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../../src/constants.ts","../../src/radioBrowser.ts"],"sourcesContent":["/**\n */\nexport const StationSearchOrder = {\n name: 'name',\n url: 'url',\n homepage: 'homepage',\n favicon: 'favicon',\n tags: 'tags',\n country: 'country',\n state: 'state',\n language: 'language',\n votes: 'votes',\n codec: 'codec',\n bitrate: 'bitrate',\n lastCheckOK: 'lastCheckOK',\n lastCheckTime: 'lastCheckTime',\n clickTimeStamp: 'clickTimeStamp',\n clickCount: 'clickCount',\n clickTrend: 'clickTrend',\n random: 'random'\n} as const\n\n/**\n */\nexport const StationSearchType = {\n byUuid: 'byUuid',\n byName: 'byName',\n byNameExact: 'byNameExact',\n byCodec: 'byCodec',\n byCodexExact: 'byCodecExact',\n byCountry: 'byCountry',\n byCountryExact: 'byCountryExact',\n byCountryCodeExact: 'byCountryCodeExact',\n byState: 'byState',\n byStateExact: 'byStateExact',\n byLanguage: 'byLanguage',\n byLanguageExact: 'byLanguageExact',\n byTag: 'byTag',\n byTagExact: 'byTagExact'\n} as const\n\n/**\n */\nexport type StationResponse = {\n changeuuid: string\n stationuuid: string\n name: string\n url: string\n url_resolved: string\n homepage: string\n favicon: string\n tags: string\n country: string\n countrycode: string\n state: string\n language: string\n votes: number\n lastchangetime: string\n codec: string\n bitrate: number\n hls: number\n lastcheckok: number\n lastchecktime: string\n lastlocalchecktime: string\n lastcheckoktime: string\n clicktimestamp: string\n clickcount: number\n clicktrend: number\n geo_lat?: number | null\n geo_long?: number | null\n}\n\n/**\n */\nexport type Station = {\n changeId: string\n id: string\n name: string\n url: string\n urlResolved: string\n homepage: string\n favicon: string\n tags: string[]\n country: string\n countryCode: string\n state: string\n language: string[]\n votes: number\n lastChangeTime: Date\n codec: string\n bitrate: number\n hls: boolean\n lastCheckOk: boolean\n lastCheckTime: Date\n lastCheckOkTime: Date\n lastLocalCheckTime: Date\n clickTimestamp: Date\n clickCount: number\n clickTrend: number\n geoLat?: number | null\n geoLong?: number | null\n}\n\n/**\n */\nexport type StationQuery = {\n offset?: number\n limit?: number\n reverse?: boolean\n order?: keyof typeof StationSearchOrder\n hideBroken?: boolean\n removeDuplicates?: boolean\n}\n\n/**\n */\nexport type AdvancedStationQuery = {\n name?: string\n nameExact?: boolean\n country?: string\n countryExact?: boolean\n countryCode?: string\n state?: string\n stateExact?: boolean\n language?: string\n languageExact?: boolean\n tag?: string\n tagExact?: boolean\n tagList?: string[]\n codec?: string\n bitrateMin?: string\n bitrateMax?: string\n hasGeoInfo?: boolean\n} & StationQuery\n\n/**\n */\nexport type Query = {\n order?: 'name' | 'stationcount'\n reverse?: boolean\n hideBroken?: boolean\n} & Record<string, any>\n\n// valid for country codes also\n/**\n */\nexport type CountryResult = {\n name: string\n stationcount: number\n}\n\n/**\n */\nexport type TagResult = CountryResult\n/**\n */\nexport type CountryStateResult = CountryResult & {\n country: string\n}\n","import {\n AdvancedStationQuery,\n CountryResult,\n CountryStateResult,\n Query,\n Station,\n StationQuery,\n StationResponse,\n StationSearchType,\n TagResult\n} from './constants'\n\n/**\n * Query the radio browser api.\n */\nexport class RadioBrowserApi {\n static version = __VERSION__\n\n protected baseUrl: string | undefined\n\n protected fetchConfig: RequestInit = {\n method: 'GET',\n redirect: 'follow'\n }\n\n /**\n * Creates an instance of radio browser api.\n * @param appName - App name to be used as user agent header to indentify the calls to the API\n * @param hideBroken - Hide broken stations for all future API calls\n */\n constructor(protected appName: string, protected hideBroken = true) {\n if (!appName) {\n throw new Error('appName is required')\n }\n this.fetchConfig.headers = { 'user-agent': this.appName }\n }\n\n /**\n * Resolves API base url this will be the default for all class instances.\n * @param config - Fetch configuration\n * @returns Array of objects with the ip and name of the api server\n */\n async resolveBaseUrl(\n config: RequestInit = {}\n ): Promise<{ ip: string; name: string }[]> {\n let result: { ip: string; name: string }[]\n const response = await fetch(\n 'https://all.api.radio-browser.info/json/servers',\n config\n )\n if (response.ok) {\n result = await response.json()\n\n return result\n } else {\n throw response\n }\n }\n\n /**\n * Sets base url for all api calls\n * @param url - Url to the api server\n */\n setBaseUrl(url: string): void {\n this.baseUrl = url\n }\n\n /**\n * Get current base url\n * @returns Base url\n */\n getBaseUrl(): string | undefined {\n return this.baseUrl\n }\n\n /**\n * Gets available countries\n * @param search - Search for country\n * @param query - Query params\n * @param fetchConfig - Fetch configuration\n * @returns Array of country results with the name of the station and station count\n */\n async getCountries(\n search?: string,\n query?: Query,\n fetchConfig?: RequestInit\n ): Promise<CountryResult[]> {\n return this.runRequest(\n this.buildRequest('countries', search, query),\n fetchConfig\n )\n }\n\n /**\n * Gets countries by country code\n * @param search - Country code\n * @param query - Query\n * @param fetchConfig - Fetch configuration\n * @returns Array of country results with the name of the station and station count\n */\n async getCountryCodes(\n search?: string,\n query?: Query,\n fetchConfig?: RequestInit\n ): Promise<CountryResult[]> {\n search = search ? `${search.toUpperCase()}` : ''\n\n return this.runRequest(\n this.buildRequest('countrycodes', search, query),\n fetchConfig\n )\n }\n\n /**\n * Gets available codes\n * @param query - Query\n * @param fetchConfig - Fetch configuration\n * @returns List of available codes\n */\n async getCodecs(\n query?: Query,\n fetchConfig?: RequestInit\n ): Promise<CountryResult[]> {\n return this.runRequest(this.buildRequest('codecs', '', query), fetchConfig)\n }\n\n /**\n * Gets country states. States **should** be regions inside a country.\n * @param country - Limit state to particular country\n * @param query - Query\n * @param fetchConfig - Fetch configuration\n * @returns Array of country states\n */\n async getCountryStates(\n country?: string,\n query?: Query,\n fetchConfig?: RequestInit\n ): Promise<CountryStateResult[]> {\n return this.runRequest(\n this.buildRequest('states', country, query),\n fetchConfig\n )\n }\n\n /**\n * Gets all available languages\n * @param language - Limit results to particular language\n * @param query - Query\n * @param fetchConfig - Fetch configuration\n * @returns Array of language results\n */\n async getLanguages(\n language?: string,\n query?: Query,\n fetchConfig?: RequestInit\n ): Promise<CountryResult[]> {\n return this.runRequest(\n this.buildRequest('languages', language, query),\n fetchConfig\n )\n }\n\n /**\n * Gets all available tags\n * @param tag - Limit results to particular tag\n * @param query - Query\n * @param fetchConfig - Fetch configuration\n * @returns List of tag results\n */\n async getTags(\n tag?: string,\n query?: Query,\n fetchConfig?: RequestInit\n ): Promise<TagResult[]> {\n tag = tag ? tag.toLowerCase() : '' // empty string returns all tags\n\n return this.runRequest(this.buildRequest('tags', tag, query), fetchConfig)\n }\n\n /**\n * Gets stations by various available parameters\n * @param searchType - Parameter for the search\n * @param search - Search value for the parameter\n * @param query - Query\n * @param fetchConfig - Fetch configuration\n * @param removeDuplicates - remove duplicate stations\n * @returns Array of station results\n */\n async getStationsBy(\n searchType: keyof typeof StationSearchType,\n search?: string,\n query?: StationQuery,\n fetchConfig?: RequestInit,\n removeDuplicates = false\n ): Promise<Station[]> {\n if (!StationSearchType[searchType]) {\n throw new Error(`search type does not exist: ${searchType}`)\n }\n\n search = search ? search.toLowerCase() : ''\n\n // http://fr1.api.radio-browser.info/{format}/stations/byuuid/{searchterm}\n const stations = await this.runRequest<StationResponse[]>(\n this.buildRequest(`stations/${searchType.toLowerCase()}`, search, query),\n fetchConfig\n )\n\n return this.normalizeStations(stations, removeDuplicates)\n }\n\n /**\n * Normalizes stations from the API response\n * @param stations - Array of station responses\n * @param removeDuplicates - remove duplicate stations\n * @returns Array of normalized stations\n */\n protected normalizeStations(\n stations: StationResponse[],\n removeDuplicates = false\n ): Station[] {\n const result = []\n const duplicates: { [key: string]: boolean } = {}\n\n for (const response of stations) {\n if (removeDuplicates) {\n const nameAndUrl = `${response.name.toLowerCase().trim()}${response.url\n .toLowerCase()\n .trim()}`\n\n // guard against results having the same stations under different id's\n if (duplicates[nameAndUrl]) continue\n\n duplicates[nameAndUrl] = true\n }\n\n const station: Station = {\n changeId: response.changeuuid,\n id: response.stationuuid,\n name: response.name,\n url: response.url,\n urlResolved: response.url_resolved,\n homepage: response.homepage,\n favicon: response.favicon,\n country: response.country,\n countryCode: response.countrycode,\n state: response.state,\n votes: response.votes,\n codec: response.codec,\n bitrate: response.bitrate,\n clickCount: response.clickcount,\n clickTrend: response.clicktrend,\n hls: Boolean(response.hls),\n lastCheckOk: Boolean(response.lastcheckok),\n lastChangeTime: new Date(response.lastchangetime),\n lastCheckOkTime: new Date(response.lastcheckoktime),\n clickTimestamp: new Date(response.clicktimestamp),\n lastLocalCheckTime: new Date(response.lastlocalchecktime),\n language: response.language.split(','),\n lastCheckTime: new Date(response.lastchecktime),\n geoLat: response.geo_lat,\n geoLong: response.geo_long,\n tags: Array.from(new Set(response.tags.split(','))).filter(\n (tag) => tag.length > 0 && tag.length < 10\n ) // drop duplicates and tags over 10 characters\n }\n\n result.push(station)\n }\n\n return result\n }\n\n /**\n * Gets all available stations. Please note that if results\n * are not limited somehow, they can be huge (size in MB)\n * @param query - Query\n * @param fetchConfig - Fetch configuration\n * @param removeDuplicates - remove duplicate stations\n * @returns Array of all available stations\n */\n async getAllStations(\n query?: Omit<StationQuery, 'hidebroken'>,\n fetchConfig?: RequestInit,\n removeDuplicates = false\n ): Promise<Station[]> {\n const stations = await this.runRequest<StationResponse[]>(\n this.buildRequest('stations', '', query),\n fetchConfig\n )\n\n return this.normalizeStations(stations, removeDuplicates)\n }\n\n /**\n * Searches stations by particular params\n * @param query - Query\n * @param fetchConfig - Fetch configuration\n * @param removeDuplicates - remove duplicate stations\n * @returns Array of station results\n */\n async searchStations(\n query: AdvancedStationQuery,\n fetchConfig?: RequestInit,\n removeDuplicates = false\n ): Promise<Station[]> {\n const stations = await this.runRequest<StationResponse[]>(\n this.buildRequest('stations/search', undefined, query),\n fetchConfig\n )\n\n return this.normalizeStations(stations, removeDuplicates)\n }\n\n /**\n * Gets stations by clicks. Stations with the highest number of clicks are most popular\n * @param limit - Limit the number of returned stations\n * @param fetchConfig - Fetch configuration\n * @returns Array of stations\n */\n async getStationsByClicks(\n limit?: number,\n fetchConfig?: RequestInit\n ): Promise<Station[]> {\n return this.resolveGetStations('topclick', limit, fetchConfig)\n }\n\n /**\n * Gets stations by votes. Returns most voted stations\n * @param limit - Limit the number of returned stations\n * @param fetchConfig - Fetch configuration\n * @returns Array of stations\n */\n async getStationsByVotes(\n limit?: number,\n fetchConfig?: RequestInit\n ): Promise<Station[]> {\n return this.resolveGetStations('topvote', limit, fetchConfig)\n }\n\n /**\n * Gets stations by recent clicks. They are basically most recently listened stations.\n * @param limit - Limit the number of returned stations\n * @param fetchConfig - Fetch configuration\n * @returns Array of stations\n */\n async getStationsByRecentClicks(\n limit?: number,\n fetchConfig?: RequestInit\n ): Promise<Station[]> {\n return this.resolveGetStations('lastclick', limit, fetchConfig)\n }\n\n /**\n * Sends click for the station. This method should be used when user starts to listen to the station.\n * @param id - Station id\n * @param fetchConfig - Fetch configuration\n * @returns Station click object\n */\n async sendStationClick(\n id: string,\n fetchConfig?: RequestInit\n ): Promise<{\n ok: boolean\n message: string\n stationuuid: string\n name: string\n url: string\n }> {\n return this.runRequest(\n this.buildRequest('url', id, undefined, false),\n fetchConfig\n )\n }\n\n /**\n * Votes for station. This method should be used when user adds the station to favourites etc..\n * @param id - Station id\n * @param fetchConfig - Fetch configuration\n * @returns Station vote object\n */\n async voteForStation(\n id: string,\n fetchConfig?: RequestInit\n ): Promise<{\n ok: boolean\n message: string\n stationuuid: string\n name: string\n url: string\n }> {\n return this.runRequest(this.buildRequest('vote', id), fetchConfig)\n }\n\n /**\n * Gets stations by station id\n * @param ids - Array of station id's\n * @param fetchConfig - Fetch configuration\n * @returns Array of stations\n */\n async getStationsById(\n ids: string[],\n fetchConfig?: RequestInit\n ): Promise<Station[]> {\n const stationsIds = ids.join(',')\n const stations = await this.runRequest<StationResponse[]>(\n this.buildRequest(\n `stations/byuuid?uuids=${stationsIds}`,\n undefined,\n undefined,\n false\n ),\n fetchConfig\n )\n\n return this.normalizeStations(stations)\n }\n\n /**\n * Gets station by station url\n * @param url - Station url\n * @param fetchConfig - Fetch configuration\n * @returns Array of stations\n */\n async getStationByUrl(\n url: string,\n fetchConfig?: RequestInit\n ): Promise<Station[]> {\n const stations = await this.runRequest<StationResponse[]>(\n this.buildRequest(\n `stations/byurl?url=${url}`,\n undefined,\n undefined,\n false\n ),\n fetchConfig\n )\n\n return this.normalizeStations(stations)\n }\n\n protected async resolveGetStations(\n endPoint: string,\n limit?: number,\n fetchConfig?: RequestInit\n ): Promise<Station[]> {\n const limitStations = limit ? `/${limit}` : ''\n const stations = await this.runRequest<StationResponse[]>(\n this.buildRequest(\n `stations/${endPoint}${limitStations}`,\n undefined,\n undefined,\n false\n ),\n fetchConfig\n )\n\n return this.normalizeStations(stations)\n }\n\n /**\n * Builds request to the API\n * @param endPoint - API endpoint\n * @param search - Search term\n * @param query - Query\n * @param addHideBrokenParam - Hide broken stations from the results\n * @returns Built request string\n */\n protected buildRequest(\n endPoint: string,\n search?: string,\n query?: Query | AdvancedStationQuery | StationQuery,\n addHideBrokenParam = true\n ): string {\n search = search ? `/${encodeURIComponent(search)}` : ''\n\n let queryCopy\n if (query) {\n queryCopy = { ...query }\n if ('tagList' in queryCopy && Array.isArray(queryCopy.tagList)) {\n queryCopy.tagList = [...queryCopy.tagList]\n }\n if (addHideBrokenParam && queryCopy.hideBroken === undefined) {\n queryCopy.hideBroken = this.hideBroken\n }\n }\n\n const queryParams = queryCopy ? this.createQueryParams(queryCopy) : ''\n\n return `${endPoint}${search}${queryParams}`\n }\n\n /**\n * Fires of the request to the API\n * @param url - Request url\n * @param fetchConfig - Fetch configuration\n * @returns Fetch response\n */\n protected async runRequest<T>(\n url: string,\n fetchConfig: RequestInit = {}\n ): Promise<T> {\n const finalConfig = {\n ...this.fetchConfig,\n ...fetchConfig,\n headers: {\n ...this.fetchConfig.headers,\n ...fetchConfig.headers\n }\n }\n\n if (!this.baseUrl) {\n const results = await this.resolveBaseUrl()\n const random = Math.floor(Math.random() * results.length)\n this.baseUrl = `https://${results[random].name}`\n }\n\n const response = await fetch(`${this.baseUrl}/json/${url}`, finalConfig)\n\n if (response.ok) {\n return response.json()\n } else {\n throw response\n }\n }\n\n /**\n * Encodes query parameters\n * @param params - Object that represents paramters as key value pairs\n * @returns String of encoded query parameters\n */\n protected createQueryParams(params?: object): string {\n let result = ''\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n let finalKey = key.toLowerCase()\n\n switch (finalKey) {\n case 'hasgeoinfo':\n finalKey = 'has_geo_info'\n break\n case 'hidebroken':\n finalKey = 'hidebroken'\n break\n\n case 'taglist':\n // github.com/segler-alex/radiobrowser-api-rust/issues/80\n finalKey = 'tagList' // tagList is the only one that is not lowercased\n }\n\n result += `&${finalKey}=${encodeURIComponent(value)}`\n }\n }\n\n return result.length ? `?${result.slice(1)}` : ''\n }\n}\n"],"names":["StationSearchType","byUuid","byName","byNameExact","byCodec","byCodexExact","byCountry","byCountryExact","byCountryCodeExact","byState","byStateExact","byLanguage","byLanguageExact","byTag","byTagExact","RadioBrowserApi","appName","hideBroken","baseUrl","fetchConfig","method","redirect","this","Error","headers","_proto","prototype","resolveBaseUrl","config","Promise","resolve","fetch","then","response","ok","json","_response$json","e","reject","setBaseUrl","url","getBaseUrl","getCountries","search","query","runRequest","buildRequest","getCountryCodes","toUpperCase","getCodecs","getCountryStates","country","getLanguages","language","getTags","tag","toLowerCase","getStationsBy","searchType","removeDuplicates","_this7","stations","normalizeStations","_step","result","duplicates","_iterator","_createForOfIteratorHelperLoose","done","value","nameAndUrl","name","trim","station","changeId","changeuuid","id","stationuuid","urlResolved","url_resolved","homepage","favicon","countryCode","countrycode","state","votes","codec","bitrate","clickCount","clickcount","clickTrend","clicktrend","hls","Boolean","lastCheckOk","lastcheckok","lastChangeTime","Date","lastchangetime","lastCheckOkTime","lastcheckoktime","clickTimestamp","clicktimestamp","lastLocalCheckTime","lastlocalchecktime","split","lastCheckTime","lastchecktime","geoLat","geo_lat","geoLong","geo_long","tags","Array","from","Set","filter","length","push","getAllStations","_this8","searchStations","_this9","undefined","getStationsByClicks","limit","resolveGetStations","getStationsByVotes","getStationsByRecentClicks","sendStationClick","voteForStation","getStationsById","ids","_this15","stationsIds","join","getStationByUrl","_this16","endPoint","_this17","addHideBrokenParam","queryCopy","encodeURIComponent","_extends","isArray","tagList","concat","createQueryParams","_temp2","_this18","finalConfig","_temp","results","random","Math","floor","params","_i","_Object$entries","Object","entries","_Object$entries$_i","finalKey","slice","version","lastCheckOK","clickTimeStamp"],"mappings":"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"}