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}?)?[/]+(song|album)[/]+([^/]+)[/]+([^?]+)/ ); const query = querystring.parse(parsed.query); let itunesId = matches[4]; 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;