From 147ad4f3d24e3e4e230ba12799e4aa7857ebfbd1 Mon Sep 17 00:00:00 2001
From: Jonathan Cremin <jonathan@crem.in>
Date: Sun, 7 May 2017 22:16:43 +0100
Subject: [PATCH] Search additional markets for itunes and spotify

---
 lib/services/itunes/index.js  | 74 ++++++++++++++++++-----------------
 lib/services/spotify/index.js | 68 ++++++++++++++++++++++----------
 test/services/itunes.js       |  5 +++
 test/services/spotify.js      |  5 +++
 4 files changed, 95 insertions(+), 57 deletions(-)

diff --git a/lib/services/itunes/index.js b/lib/services/itunes/index.js
index 966ac6d..d78004a 100644
--- a/lib/services/itunes/index.js
+++ b/lib/services/itunes/index.js
@@ -76,6 +76,7 @@ export function* lookupId(id, type, cc) {
 };
 
 export function* search(data) {
+  const markets = ['us', 'gb', 'jp', 'br', 'de', 'es'];
   let query, album, entity;
   const type = data.type;
 
@@ -89,47 +90,48 @@ export function* search(data) {
     entity = 'musicTrack';
   }
 
-  const path = '/search?term=' + encodeURIComponent(query) + '&media=music&entity=' + entity;
-  const response = yield request.get(apiRoot + path);
-  let result = JSON.parse(response.text);
+  for (let market of markets) {
+    const path = '/' + market + '/search?term=' + encodeURIComponent(query) + '&media=music&entity=' + entity;
+    const response = yield request.get(apiRoot + path);
 
-  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();
+    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 yield search(cleanedData);
       }
-      return yield search(cleanedData);
     } else {
-      return {service: 'itunes'};
-    }
-  } else {
-    result = result.results[0];
+      result = result.results[0];
 
-    const item = {
-      service: 'itunes',
-      type: type,
-      id: 'us' + result.collectionId,
-      name: result.trackName ? result.trackName : result.collectionName,
-      streamUrl: null,
-      purchaseUrl: result.collectionViewUrl,
-      artwork: {
-        small: 'https://match.audio/itunes/' + result.artworkUrl100.replace('100x100', '200x200').replace('http://', ''),
-        large: 'https://match.audio/itunes/' + result.artworkUrl100.replace('100x100', '600x600').replace('http://', '')
-      },
-      artist: {
-        name: result.artistName
-      }
-    };
-
-    if (type === 'track') {
-      item.album = {
-        name: result.collectionName
+      const item = {
+        service: 'itunes',
+        type: type,
+        id: 'us' + result.collectionId,
+        name: result.trackName ? result.trackName : result.collectionName,
+        streamUrl: null,
+        purchaseUrl: result.collectionViewUrl,
+        artwork: {
+          small: 'https://match.audio/itunes/' + result.artworkUrl100.replace('100x100', '200x200').replace('http://', ''),
+          large: 'https://match.audio/itunes/' + result.artworkUrl100.replace('100x100', '600x600').replace('http://', '')
+        },
+        artist: {
+          name: result.artistName
+        }
       };
+
+      if (type === 'track') {
+        item.album = {
+          name: result.collectionName
+        };
+      }
+      return item;
     }
-    return item;
   }
+  return {service: 'itunes'};
 };
diff --git a/lib/services/spotify/index.js b/lib/services/spotify/index.js
index 2af1d5d..009dc7b 100644
--- a/lib/services/spotify/index.js
+++ b/lib/services/spotify/index.js
@@ -1,6 +1,8 @@
 import { parse } from 'url';
 import bluebird from 'bluebird';
 import spotifyCB from 'spotify';
+import request from 'superagent';
+import 'superagent-bluebird-promise';
 const spotify = bluebird.promisifyAll(spotifyCB);
 import { match as urlMatch }  from './url';
 
@@ -64,8 +66,9 @@ export function* lookupId(id, type) {
   }
 }
 
-export function* search(data) {
-  var cleanParam = function(str) {
+export function* search(data, original={}) {
+  const markets = ['US', 'GB', 'JP', 'BR', 'DE', 'ES'];
+  const cleanParam = function(str) {
     var chopChars = ['&', '[', '('];
     chopChars.forEach(function(chr) {
       if (data.artist.name.indexOf('&') > 0) {
@@ -74,8 +77,8 @@ export function* search(data) {
     })
     return str.replace(/[\:\?]+/, "");
   }
-  var query, album;
-  var type = data.type;
+  let query, album;
+  const type = data.type;
 
   if (type == "album") {
     query = "artist:" + cleanParam(data.artist.name) + " album:" + cleanParam(data.name);
@@ -85,27 +88,50 @@ export function* search(data) {
     album = data.albumName;
   }
 
-  const results = yield spotify.searchAsync({query: query, type: type});
+  for (let market of markets) {
+    const response = yield request.get('https://api.spotify.com/v1/search?type=' + type + '&q=' + encodeURI(query) + '&market=' + market);
+    const items = response.body[type + 's'].items;
 
-  if (!results[type + "s"].items[0]) {
-    return {service: "spotify"};
-  } else {
-    let found;
-    const choppedAlbum = data.type == "album" ? cleanParam(data.name) : cleanParam(data.albumName);
-    if (!choppedAlbum.length) {
-      return yield lookupId(results[type + "s"].items[0].id, type);
+    const name = original.name || data.name;
+
+    let match;
+    if (!(match = exactMatch(name, items, type))) {
+      match = looseMatch(name, items, type);
     }
 
-    results[type + "s"].items.forEach(function(item) {
-      const albumName = data.type == "album" ? item.name : item.album.name;
-      const matches = cleanParam(albumName).match(/^[^\(\[]+/);
-      if(choppedAlbum.indexOf(matches[0]) >= 0) {
-        found = item;
+    if (match) {
+      if (type === 'album') {
+        return yield lookupId(match.id, type);
+      } else if (type === 'track') {
+        return yield lookupId(match.id, type);
       }
-    });
-    if (!found) {
-      return {service: "spotify"};
     }
-    return yield lookupId(results[type + "s"].items[0].id, type);
   }
+  return {service: "spotify"};
+}
+
+function exactMatch(needle, haystack, type) {
+  // try to find exact match
+  return haystack.find(function(entry) {
+    if (entry.type !== type) {
+      return false;
+    }
+
+    if (entry.name === needle) {
+      return entry;
+    }
+  });
+}
+
+function looseMatch(needle, haystack, type) {
+  // try to find exact match
+  return haystack.find(function(entry) {
+    if (entry.type !== type) {
+      return false;
+    }
+
+    if (entry.name.indexOf(needle) >= 0) {
+      return entry
+    }
+  });
 }
diff --git a/test/services/itunes.js b/test/services/itunes.js
index 277a465..cf7719e 100644
--- a/test/services/itunes.js
+++ b/test/services/itunes.js
@@ -20,6 +20,11 @@ describe('iTunes Music', function(){
       result.name.should.equal('White Pony');
     });
 
+    it('should find awkward album by search', function* (){
+      const result = yield itunes.search({type: 'album', artist: {name: 'Anavitória'}, name: 'Fica'});
+      result.name.should.equal('Fica (feat. Matheus & Kauan) - Single');
+    });
+
     it('should find track by search', function* (){
       const result = yield itunes.search({type: 'track', artist: {name: 'Deftones'}, albumName: 'Deftones', name: 'Hexagram'});
       result.name.should.equal('Hexagram');
diff --git a/test/services/spotify.js b/test/services/spotify.js
index 60c7bad..bca91ab 100644
--- a/test/services/spotify.js
+++ b/test/services/spotify.js
@@ -20,6 +20,11 @@ describe('Spotify', function(){
       result.name.should.equal('Listen (Deluxe)');
     });
 
+    it('should find br album by search', function* (){
+      const result = yield spotify.search({type: 'album', artist: {name: 'Anavitória'}, name: 'Fica'});
+      result.name.should.equal('Fica');
+    });
+
     it('should find album by various artists by search', function* (){
       const result = yield spotify.search({type: 'album', artist: {name: 'Various Artists'}, name: 'The Get Down Part II: Original Soundtrack From The Netflix Original Series'});
       result.name.should.equal('The Get Down Part II: Original Soundtrack From The Netflix Original Series');