import { parse } from 'url'; import querystring from 'querystring'; import request from 'superagent'; import urlMatch from './url.js'; const apiRoot = 'https://itunes.apple.com'; export async function parseUrl(url) { const parsed = parse(url); const matches = parsed.path.match(/[/]?([/]?[a-z]{2}?)?[/]+album[/]+([^/]+)[/]+([^?]+)/); const query = querystring.parse(parsed.query); let itunesId = matches[3]; if (matches) { let type = 'album'; if (matches[3].match(/^id/)) { itunesId = matches[3].substr(2); if (query.i) { type = 'track'; itunesId = query.i; } } return await lookupId(itunesId, type, matches[1] || 'us'); } throw new Error(); } export async function lookupId(possibleId, type, countrycode) { let cc = countrycode; let id = possibleId; if (String(possibleId).match(/^[a-z]{2}/)) { cc = possibleId.substr(0, 2); id = possibleId.substr(2); } let path = `/lookup?id=${id}`; if (cc) { path = `/${cc}${path}`; } try { const response = await request.get(apiRoot + path); let result = JSON.parse(response.text); if (!result.results || result.resultCount === 0 || !result.results[0].collectionId) { throw new Error(); } else { result = result.results[0]; const item = { service: 'itunes', type, id: cc + id, name: result.trackName ? result.trackName : result.collectionName, streamUrl: null, purchaseUrl: result.collectionViewUrl, artwork: { small: `${result.artworkUrl100.replace('100x100', '200x200').replace('.mzstatic.com', '.mzstatic.com').replace('http://', 'https://')}`, large: `${result.artworkUrl100.replace('100x100', '600x600').replace('.mzstatic.com', '.mzstatic.com').replace('http://', 'https://')}`, }, artist: { name: result.artistName, }, }; if (type === 'track') { item.album = { name: result.collectionName, }; } return item; } } catch(e) { const error = new Error('Not Found'); error.status = 404; return Promise.reject(error); } } export async function search(data) { const markets = ['us', 'gb', 'jp', 'br', 'de', 'es']; let query; let album; let entity; const type = data.type; if (type === 'album') { query = `${data.artist.name} ${data.name}`; album = data.name; entity = 'album'; } else if (type === 'track') { query = `${data.artist.name} ${data.albumName} ${data.name}`; album = data.albumName; entity = 'musicTrack'; } for (const market of markets) { // eslint-disable-line const path = `/${market}/search?term=${encodeURIComponent(query)}&media=music&entity=${entity}`; const response = await request.get(apiRoot + path); let result = JSON.parse(response.text); if (!result.results[0]) { const matches = album.match(/^[^([]+/); if (matches && matches[0] && matches[0] !== album) { const cleanedData = JSON.parse(JSON.stringify(data)); if (type === 'album') { cleanedData.name = matches[0].trim(); } else if (type === 'track') { cleanedData.albumName = matches[0].trim(); } return await search(cleanedData); } } else { result = result.results[0]; const item = { service: 'itunes', type, id: `us${result.collectionId}`, name: result.trackName ? result.trackName : result.collectionName, streamUrl: result.collectionViewUrl, purchaseUrl: result.collectionViewUrl, artwork: { small: `${result.artworkUrl100.replace('100x100', '200x200').replace('.mzstatic.com', '.mzstatic.com').replace('http://', 'https://')}`, large: `${result.artworkUrl100.replace('100x100', '600x600').replace('.mzstatic.com', '.mzstatic.com').replace('http://', 'https://')}`, }, artist: { name: result.artistName, }, }; if (type === 'track') { item.album = { name: result.collectionName, }; } return item; } } return { service: 'itunes' }; } export const id = 'itunes'; export const match = urlMatch;