[musicdb] musicdb restructure for musicbrainz artist IDs - remove artist/album ID...
authornight199uk <night199uk@xbmc.org>
Wed, 20 Jun 2012 22:36:30 +0000 (10:36 +1200)
committernight199uk <night199uk@xbmc.org>
Wed, 1 May 2013 16:36:32 +0000 (00:36 +0800)
19 files changed:
xbmc/addons/Scraper.cpp
xbmc/addons/Scraper.h
xbmc/interfaces/json-rpc/AudioLibrary.cpp
xbmc/interfaces/legacy/ListItem.cpp
xbmc/music/Album.cpp
xbmc/music/Album.h
xbmc/music/Artist.cpp
xbmc/music/Artist.h
xbmc/music/MusicDatabase.cpp
xbmc/music/MusicDatabase.h
xbmc/music/Song.cpp
xbmc/music/Song.h
xbmc/music/infoscanner/MusicInfoScanner.cpp
xbmc/music/infoscanner/MusicInfoScanner.h
xbmc/music/tags/MusicInfoTag.cpp
xbmc/music/tags/MusicInfoTag.h
xbmc/music/tags/TagLoaderTagLib.cpp
xbmc/music/windows/GUIWindowMusicBase.cpp
xbmc/music/windows/GUIWindowMusicBase.h

index 4240531..8e7c5f1 100644 (file)
@@ -248,7 +248,7 @@ vector<CStdString> CScraper::Run(const CStdString& function,
   CStdString strXML = InternalRun(function,scrURL,http,extras);
   if (strXML.IsEmpty())
   {
-    if (function != "NfoUrl")
+    if (function != "NfoUrl" && function != "ResolveIDToUrl")
       CLog::Log(LOGERROR, "%s: Unable to parse web site",__FUNCTION__);
     throw CScraperError();
   }
@@ -487,6 +487,68 @@ CScraperUrl CScraper::NfoUrl(const CStdString &sNfoContent)
   return scurlRet;
 }
 
+CScraperUrl CScraper::ResolveIDToUrl(const CStdString& externalID)
+{
+  CScraperUrl scurlRet;
+  
+  // scraper function takes an external ID, returns XML (see below)
+  vector<CStdString> vcsIn;
+  vcsIn.push_back(externalID);
+  CScraperUrl scurl;
+  CCurlFile fcurl;
+  vector<CStdString> vcsOut = Run("ResolveIDToUrl", scurl, fcurl, &vcsIn);
+  if (vcsOut.empty() || vcsOut[0].empty())
+    return scurlRet;
+  if (vcsOut.size() > 1)
+    CLog::Log(LOGWARNING, "%s: scraper returned multiple results; using first", __FUNCTION__);
+
+  // parse returned XML: either <error> element on error, blank on failure,
+  // or <url>...</url> or <url>...</url><id>...</id> on success
+  for (unsigned int i=0; i < vcsOut.size(); ++i)
+  {
+    CXBMCTinyXML doc;
+    doc.Parse(vcsOut[i], 0, TIXML_ENCODING_UTF8);
+    CheckScraperError(doc.RootElement());
+
+    if (doc.RootElement())
+    {
+      /*
+       NOTE: Scrapers might return invalid xml with some loose
+       elements (eg. '<url>http://some.url</url><id>123</id>').
+       Since XMLUtils::GetString() is assuming well formed xml
+       with start and end-tags we're not able to use it.
+       Check for the desired Elements instead.
+       */
+      TiXmlElement* pxeUrl=NULL;
+      TiXmlElement* pId=NULL;
+      if (!strcmp(doc.RootElement()->Value(),"details"))
+      {
+        pxeUrl = doc.RootElement()->FirstChildElement("url");
+        pId = doc.RootElement()->FirstChildElement("id");
+      }
+      else
+      {
+        pId = doc.FirstChildElement("id");
+        pxeUrl = doc.FirstChildElement("url");
+      }
+      if (pId && pId->FirstChild())
+        scurlRet.strId = pId->FirstChild()->Value();
+
+      if (pxeUrl && pxeUrl->Attribute("function"))
+        continue;
+
+      if (pxeUrl)
+        scurlRet.ParseElement(pxeUrl);
+      else if (!strcmp(doc.RootElement()->Value(), "url"))
+        scurlRet.ParseElement(doc.RootElement());
+      else
+        continue;
+      break;
+    }
+  }
+  return scurlRet;
+}
+
 static bool RelevanceSortFunction(const CScraperUrl &left, const CScraperUrl &right)
 {
   return left.relevance > right.relevance;
index 0d54287..e9c0b42 100644 (file)
@@ -121,6 +121,17 @@ public:
   // scraper media functions
   CScraperUrl NfoUrl(const CStdString &sNfoContent);
 
+  /*! \brief Resolve an external ID (e.g. MusicBrainz IDs) to a URL using scrapers
+   If we have an ID in hand, e.g. MusicBrainz IDs or TheTVDB Season IDs
+   we can get directly to a URL instead of searching by name and choosing from 
+   the search results. The correct scraper type should be used to get the right
+   URL for a given ID, so we can differentiate albums, artists, TV Seasons, etc.
+   \param externalID the external ID - e.g. MusicBrainzArtist/AlbumID
+   \return a populated URL pointing to the details page for the given ID or
+           an empty URL if we couldn't resolve the ID.
+   */
+  CScraperUrl ResolveIDToUrl(const CStdString &externalID);
+
   std::vector<CScraperUrl> FindMovie(XFILE::CCurlFile &fcurl,
     const CStdString &sMovie, bool fFirst);
   std::vector<MUSIC_GRABBER::CMusicAlbumInfo> FindAlbum(XFILE::CCurlFile &fcurl,
index 9f91cc2..1162a05 100644 (file)
@@ -511,12 +511,6 @@ JSONRPC_STATUS CAudioLibrary::SetSongDetails(const CStdString &method, ITranspor
     song.strComment = parameterObject["comment"].asString();
   if (ParameterNotNull(parameterObject, "musicbrainztrackid"))
     song.strMusicBrainzTrackID = parameterObject["musicbrainztrackid"].asString();
-  if (ParameterNotNull(parameterObject, "musicbrainzartistid"))
-    song.strMusicBrainzArtistID = parameterObject["musicbrainzartistid"].asString();
-  if (ParameterNotNull(parameterObject, "musicbrainzalbumid"))
-    song.strMusicBrainzAlbumID = parameterObject["musicbrainzalbumid"].asString();
-  if (ParameterNotNull(parameterObject, "musicbrainzalbumartistid"))
-    song.strMusicBrainzAlbumArtistID = parameterObject["musicbrainzalbumartistid"].asString();
 
   if (musicdatabase.UpdateSong(song, id) <= 0)
     return InternalError;
index f432de6..6d88dd8 100644 (file)
@@ -418,11 +418,11 @@ namespace XBMCAddon
           else if (key == "musicbrainztrackid")
             item->GetMusicInfoTag()->SetMusicBrainzTrackID(value);
           else if (key == "musicbrainzartistid")
-            item->GetMusicInfoTag()->SetMusicBrainzArtistID(value);
+            item->GetMusicInfoTag()->SetMusicBrainzArtistID(StringUtils::Split(value, g_advancedSettings.m_musicItemSeparator));
           else if (key == "musicbrainzalbumid")
             item->GetMusicInfoTag()->SetMusicBrainzAlbumID(value);
           else if (key == "musicbrainzalbumartistid")
-            item->GetMusicInfoTag()->SetMusicBrainzAlbumArtistID(value);
+            item->GetMusicInfoTag()->SetMusicBrainzAlbumArtistID(StringUtils::Split(value, g_advancedSettings.m_musicItemSeparator));
           else if (key == "musicbrainztrmid")
             item->GetMusicInfoTag()->SetMusicBrainzTRMID(value);
           else if (key == "comment")
index 95f2cc1..030e0be 100644 (file)
 #include "utils/StringUtils.h"
 #include "utils/XMLUtils.h"
 #include "utils/MathUtils.h"
+#include "FileItem.h"
 
 using namespace std;
 using namespace MUSIC_INFO;
 
+CAlbum::CAlbum(const CFileItem& item)
+{
+  Reset();
+  const CMusicInfoTag& tag = *item.GetMusicInfoTag();
+  SYSTEMTIME stTime;
+  tag.GetReleaseDate(stTime);
+  strAlbum = tag.GetAlbum();
+  strMusicBrainzAlbumID = tag.GetMusicBrainzAlbumID();
+  genre = tag.GetGenre();
+  artist = tag.GetAlbumArtist();
+  iYear = stTime.wYear;
+  bCompilation = tag.GetCompilation();
+  iTimesPlayed = 0;
+}
+
+CStdString CAlbum::GetArtistString() const
+{
+  return StringUtils::Join(artist, g_advancedSettings.m_musicItemSeparator);
+}
+
+CStdString CAlbum::GetGenreString() const
+{
+  return StringUtils::Join(artist, g_advancedSettings.m_musicItemSeparator);
+}
+
 bool CAlbum::operator<(const CAlbum &a) const
 {
-  return strAlbum +StringUtils::Join(artist, g_advancedSettings.m_musicItemSeparator) < a.strAlbum + StringUtils::Join(a.artist, g_advancedSettings.m_musicItemSeparator);
+  if (strAlbum < a.strAlbum) return true;
+  if (strAlbum > a.strAlbum) return false;
+  if (strMusicBrainzAlbumID < a.strMusicBrainzAlbumID) return true;
+  if (strMusicBrainzAlbumID > a.strMusicBrainzAlbumID) return false;
+  return false;
 }
 
 bool CAlbum::Load(const TiXmlElement *album, bool append, bool prioritise)
@@ -38,7 +68,8 @@ bool CAlbum::Load(const TiXmlElement *album, bool append, bool prioritise)
   if (!append)
     Reset();
 
-  XMLUtils::GetString(album,"title",strAlbum);
+  XMLUtils::GetString(album,              "title", strAlbum);
+  XMLUtils::GetString(album, "musicBrainzAlbumID", strMusicBrainzAlbumID);
 
   XMLUtils::GetStringArray(album, "artist", artist, prioritise, g_advancedSettings.m_musicItemSeparator);
   XMLUtils::GetStringArray(album, "genre", genre, prioritise, g_advancedSettings.m_musicItemSeparator);
@@ -88,6 +119,25 @@ bool CAlbum::Load(const TiXmlElement *album, bool append, bool prioritise)
     thumbURL.m_xml = xmlAdd;
   }
 
+  const TiXmlElement* albumArtistCreditsNode = album->FirstChildElement("albumArtistCredits");
+  if (albumArtistCreditsNode)
+    artistCredits.clear();
+
+  while (albumArtistCreditsNode)
+  {
+    if (albumArtistCreditsNode->FirstChild())
+    {
+      CArtistCredit artistCredit;
+      XMLUtils::GetString(albumArtistCreditsNode,  "artist",               artistCredit.m_strArtist);
+      XMLUtils::GetString(albumArtistCreditsNode,  "musicBrainzArtistID",  artistCredit.m_strMusicBrainzArtistID);
+      XMLUtils::GetString(albumArtistCreditsNode,  "joinphrase",           artistCredit.m_strJoinPhrase);
+      XMLUtils::GetBoolean(albumArtistCreditsNode, "featuring",            artistCredit.m_boolFeatured);
+      artistCredits.push_back(artistCredit);
+    }
+
+    albumArtistCreditsNode = albumArtistCreditsNode->NextSiblingElement("albumArtistCredits");
+  }
+
   const TiXmlElement* node = album->FirstChildElement("track");
   if (node)
     songs.clear();  // this means that the tracks can't be spread over separate pages
@@ -130,12 +180,13 @@ bool CAlbum::Save(TiXmlNode *node, const CStdString &tag, const CStdString& strP
 
   if (!album) return false;
 
-  XMLUtils::SetString(album,  "title", strAlbum);
-  XMLUtils::SetStringArray(album, "artist", artist);
-  XMLUtils::SetStringArray(album,  "genre", genre);
-  XMLUtils::SetStringArray(album,  "style", styles);
-  XMLUtils::SetStringArray(album,   "mood", moods);
-  XMLUtils::SetStringArray(album,  "theme", themes);
+  XMLUtils::SetString(album,                    "title", strAlbum);
+  XMLUtils::SetString(album,       "musicBrainzAlbumID", strMusicBrainzAlbumID);
+  XMLUtils::SetStringArray(album,              "artist", artist);
+  XMLUtils::SetStringArray(album,               "genre", genre);
+  XMLUtils::SetStringArray(album,               "style", styles);
+  XMLUtils::SetStringArray(album,                "mood", moods);
+  XMLUtils::SetStringArray(album,               "theme", themes);
 
   XMLUtils::SetString(album,      "review", strReview);
   XMLUtils::SetString(album,        "type", strType);
@@ -158,6 +209,17 @@ bool CAlbum::Save(TiXmlNode *node, const CStdString &tag, const CStdString& strP
   XMLUtils::SetInt(album,         "rating", iRating);
   XMLUtils::SetInt(album,           "year", iYear);
 
+  for( VECARTISTCREDITS::const_iterator it = artistCredits.begin();it != artistCredits.end();++it)
+  {
+    // add an <albumArtistCredits> tag
+    TiXmlElement albumArtistCreditsElement("albumArtistCredits");
+    TiXmlNode *albumArtistCreditsNode = album->InsertEndChild(albumArtistCreditsElement);
+    XMLUtils::SetString(albumArtistCreditsNode,               "artist", it->m_strArtist);
+    XMLUtils::SetString(albumArtistCreditsNode,  "musicBrainzArtistID", it->m_strMusicBrainzArtistID);
+    XMLUtils::SetString(albumArtistCreditsNode,           "joinphrase", it->m_strJoinPhrase);
+    XMLUtils::SetString(albumArtistCreditsNode,            "featuring", it->GetArtist());
+  }
+
   for( VECSONGS::const_iterator it = songs.begin();it != songs.end();++it)
   {
     // add a <song> tag
index a10200b..4db82e4 100644 (file)
 
 #include <map>
 #include <vector>
-
+#include "Artist.h"
 #include "Song.h"
 #include "utils/ScraperUrl.h"
 
 class TiXmlNode;
-
+class CFileItem;
 class CAlbum
 {
 public:
+  CAlbum(const CFileItem& item);
   CAlbum() { idAlbum = 0; iRating = 0; iYear = 0; iTimesPlayed = 0; };
   bool operator<(const CAlbum &a) const;
 
@@ -42,7 +43,9 @@ public:
   {
     idAlbum = -1;
     strAlbum.Empty();
+    strMusicBrainzAlbumID.Empty();
     artist.clear();
+    artistCredits.clear();
     genre.clear();
     thumbURL.Clear();
     moods.clear();
@@ -52,6 +55,7 @@ public:
     strReview.Empty();
     strLabel.Empty();
     strType.Empty();
+    strPath.Empty();
     m_strDateOfRelease.Empty();
     iRating=-1;
     iYear=-1;
@@ -60,6 +64,9 @@ public:
     songs.clear();
   }
 
+  CStdString GetArtistString() const;
+  CStdString GetGenreString() const;
+
   /*! \brief Load album information from an XML file.
    See CVideoInfoTag::Load for a description of the types of elements we load.
    \param element    the root XML element to parse.
@@ -72,7 +79,9 @@ public:
 
   long idAlbum;
   CStdString strAlbum;
+  CStdString strMusicBrainzAlbumID;
   std::vector<std::string> artist;
+  VECARTISTCREDITS artistCredits;
   std::vector<std::string> genre;
   CScraperUrl thumbURL;
   std::vector<std::string> moods;
@@ -82,6 +91,7 @@ public:
   CStdString strReview;
   CStdString strLabel;
   CStdString strType;
+  CStdString strPath;
   CStdString m_strDateOfRelease;
   int iRating;
   int iYear;
index 8d85c96..e29f68c 100644 (file)
@@ -30,10 +30,12 @@ bool CArtist::Load(const TiXmlElement *artist, bool append, bool prioritise)
   if (!append)
     Reset();
 
-  XMLUtils::GetString(artist,"name",strArtist);
-  XMLUtils::GetStringArray(artist, "genre", genre, prioritise, g_advancedSettings.m_musicItemSeparator);
-  XMLUtils::GetStringArray(artist, "style", styles, prioritise, g_advancedSettings.m_musicItemSeparator);
-  XMLUtils::GetStringArray(artist, "mood", moods, prioritise, g_advancedSettings.m_musicItemSeparator);
+  XMLUtils::GetString(artist,                "name", strArtist);
+  XMLUtils::GetString(artist, "musicBrainzArtistID", strMusicBrainzArtistID);
+
+  XMLUtils::GetStringArray(artist,       "genre", genre, prioritise, g_advancedSettings.m_musicItemSeparator);
+  XMLUtils::GetStringArray(artist,       "style", styles, prioritise, g_advancedSettings.m_musicItemSeparator);
+  XMLUtils::GetStringArray(artist,        "mood", moods, prioritise, g_advancedSettings.m_musicItemSeparator);
   XMLUtils::GetStringArray(artist, "yearsactive", yearsActive, prioritise, g_advancedSettings.m_musicItemSeparator);
   XMLUtils::GetStringArray(artist, "instruments", instruments, prioritise, g_advancedSettings.m_musicItemSeparator);
 
@@ -111,17 +113,18 @@ bool CArtist::Save(TiXmlNode *node, const CStdString &tag, const CStdString& str
 
   if (!artist) return false;
 
-  XMLUtils::SetString(artist,       "name", strArtist);
-  XMLUtils::SetStringArray(artist, "genre", genre);
-  XMLUtils::SetStringArray(artist, "style", styles);
-  XMLUtils::SetStringArray(artist,  "mood", moods);
-  XMLUtils::SetStringArray(artist, "yearsactive", yearsActive);
-  XMLUtils::SetStringArray(artist, "instruments", instruments);
-  XMLUtils::SetString(artist,        "born", strBorn);
-  XMLUtils::SetString(artist,      "formed", strFormed);
-  XMLUtils::SetString(artist,   "biography", strBiography);
-  XMLUtils::SetString(artist,        "died", strDied);
-  XMLUtils::SetString(artist,   "disbanded", strDisbanded);
+  XMLUtils::SetString(artist,                      "name", strArtist);
+  XMLUtils::SetString(artist,       "musicBrainzArtistID", strMusicBrainzArtistID);
+  XMLUtils::SetStringArray(artist,                "genre", genre);
+  XMLUtils::SetStringArray(artist,                "style", styles);
+  XMLUtils::SetStringArray(artist,                 "mood", moods);
+  XMLUtils::SetStringArray(artist,          "yearsactive", yearsActive);
+  XMLUtils::SetStringArray(artist,          "instruments", instruments);
+  XMLUtils::SetString(artist,                      "born", strBorn);
+  XMLUtils::SetString(artist,                    "formed", strFormed);
+  XMLUtils::SetString(artist,                 "biography", strBiography);
+  XMLUtils::SetString(artist,                      "died", strDied);
+  XMLUtils::SetString(artist,                 "disbanded", strDisbanded);
   if (!thumbURL.m_xml.empty())
   {
     CXBMCTinyXML doc;
index e9b9ad5..a2ad470 100644 (file)
@@ -27,6 +27,8 @@
 #include "utils/Fanart.h"
 
 class TiXmlNode;
+class CAlbum;
+class CMusicDatabase;
 
 class CArtist
 {
@@ -34,7 +36,11 @@ public:
   long idArtist;
   bool operator<(const CArtist& a) const
   {
-    return strArtist < a.strArtist;
+    if (strArtist < a.strArtist) return true;
+    if (strArtist > a.strArtist) return false;
+    if (strMusicBrainzArtistID < a.strMusicBrainzArtistID) return true;
+    if (strMusicBrainzArtistID > a.strMusicBrainzArtistID) return false;
+    return false;
   }
 
   void Reset()
@@ -53,6 +59,7 @@ public:
     thumbURL.Clear();
     discography.clear();
     idArtist = -1;
+    strPath.Empty();
   }
 
   /*! \brief Load artist information from an XML file.
@@ -66,6 +73,7 @@ public:
   bool Save(TiXmlNode *node, const CStdString &tag, const CStdString& strPath);
 
   CStdString strArtist;
+  CStdString strMusicBrainzArtistID;
   std::vector<std::string> genre;
   CStdString strBiography;
   std::vector<std::string> styles;
@@ -76,9 +84,46 @@ public:
   CStdString strDied;
   CStdString strDisbanded;
   std::vector<std::string> yearsActive;
+  CStdString strPath;
   CScraperUrl thumbURL;
   CFanart fanart;
   std::vector<std::pair<CStdString,CStdString> > discography;
 };
 
+class CArtistCredit
+{
+  friend class CAlbum;
+  friend class CMusicDatabase;
+
+public:
+  CArtistCredit() { }
+  CArtistCredit(std::string strArtist, std::string strJoinPhrase) : m_strArtist(strArtist), m_strJoinPhrase(strJoinPhrase), m_boolFeatured(false) { }
+  CArtistCredit(std::string strArtist, std::string strMusicBrainzArtistID, std::string strJoinPhrase)
+  : m_strArtist(strArtist), m_strMusicBrainzArtistID(strMusicBrainzArtistID), m_strJoinPhrase(strJoinPhrase), m_boolFeatured(false)  {  }
+  bool operator<(const CArtistCredit& a) const
+  {
+    if (m_strArtist < a.m_strArtist) return true;
+    if (m_strArtist > a.m_strArtist) return false;
+    if (m_strMusicBrainzArtistID < a.m_strMusicBrainzArtistID) return true;
+    if (m_strMusicBrainzArtistID > a.m_strMusicBrainzArtistID) return false;
+    return false;
+  }
+
+  std::string GetArtist() const                { return m_strArtist; }
+  std::string GetMusicBrainzArtistID() const   { return m_strMusicBrainzArtistID; }
+  std::string GetJoinPhrase() const            { return m_strJoinPhrase; }
+  void SetArtist(const std::string &strArtist) { m_strArtist = strArtist; }
+  void SetMusicBrainzArtistID(const std::string &strMusicBrainzArtistID) { m_strMusicBrainzArtistID = strMusicBrainzArtistID; }
+  void SetJoinPhrase(const std::string &strJoinPhrase) { m_strJoinPhrase = strJoinPhrase; }
+
+private:
+  long idArtist;
+  std::string m_strArtist;
+  std::string m_strMusicBrainzArtistID;
+  std::string m_strJoinPhrase;
+  bool m_boolFeatured;
+};
+
 typedef std::vector<CArtist> VECARTISTS;
+typedef std::vector<CArtistCredit> VECARTISTCREDITS;
+
index c74e09a..9563e00 100644 (file)
@@ -102,11 +102,11 @@ bool CMusicDatabase::CreateTables()
     CDatabase::CreateTables();
 
     CLog::Log(LOGINFO, "create artist table");
-    m_pDS->exec("CREATE TABLE artist ( idArtist integer primary key, strArtist varchar(256))\n");
+    m_pDS->exec("CREATE TABLE artist ( idArtist integer primary key, strArtist varchar(256), strMusicBrainzArtistID text)\n");
     CLog::Log(LOGINFO, "create album table");
-    m_pDS->exec("CREATE TABLE album ( idAlbum integer primary key, strAlbum varchar(256), strArtists text, strGenres text, iYear integer, idThumb integer, bCompilation integer not null default '0' )\n");
+    m_pDS->exec("CREATE TABLE album ( idAlbum integer primary key, strAlbum varchar(256), strArtists text, strGenres text, iYear integer, idThumb integer, bCompilation integer not null default '0', strMusicBrainzAlbumID text )\n");
     CLog::Log(LOGINFO, "create album_artist table");
-    m_pDS->exec("CREATE TABLE album_artist ( idArtist integer, idAlbum integer, boolFeatured integer, iOrder integer )\n");
+    m_pDS->exec("CREATE TABLE album_artist ( idArtist integer, idAlbum integer, strJoinPhrase text, boolFeatured integer, iOrder integer )\n");
     CLog::Log(LOGINFO, "create album_genre table");
     m_pDS->exec("CREATE TABLE album_genre ( idGenre integer, idAlbum integer, iOrder integer )\n");
 
@@ -115,9 +115,9 @@ bool CMusicDatabase::CreateTables()
     CLog::Log(LOGINFO, "create path table");
     m_pDS->exec("CREATE TABLE path ( idPath integer primary key, strPath varchar(512), strHash text)\n");
     CLog::Log(LOGINFO, "create song table");
-    m_pDS->exec("CREATE TABLE song ( idSong integer primary key, idAlbum integer, idPath integer, strArtists text, strGenres text, strTitle varchar(512), iTrack integer, iDuration integer, iYear integer, dwFileNameCRC text, strFileName text, strMusicBrainzTrackID text, strMusicBrainzArtistID text, strMusicBrainzAlbumID text, strMusicBrainzAlbumArtistID text, strMusicBrainzTRMID text, iTimesPlayed integer, iStartOffset integer, iEndOffset integer, idThumb integer, lastplayed varchar(20) default NULL, rating char default '0', comment text)\n");
+    m_pDS->exec("CREATE TABLE song ( idSong integer primary key, idAlbum integer, idPath integer, strArtists text, strGenres text, strTitle varchar(512), iTrack integer, iDuration integer, iYear integer, dwFileNameCRC text, strFileName text, strMusicBrainzTrackID text, iTimesPlayed integer, iStartOffset integer, iEndOffset integer, idThumb integer, lastplayed varchar(20) default NULL, rating char default '0', comment text)\n");
     CLog::Log(LOGINFO, "create song_artist table");
-    m_pDS->exec("CREATE TABLE song_artist ( idArtist integer, idSong integer, boolFeatured integer, iOrder integer )\n");
+    m_pDS->exec("CREATE TABLE song_artist ( idArtist integer, idSong integer, strJoinPhrase text, boolFeatured integer, iOrder integer )\n");
     CLog::Log(LOGINFO, "create song_genre table");
     m_pDS->exec("CREATE TABLE song_genre ( idGenre integer, idSong integer, iOrder integer )\n");
 
@@ -140,6 +140,8 @@ bool CMusicDatabase::CreateTables()
     m_pDS->exec("CREATE INDEX idxAlbum ON album(strAlbum)");
     CLog::Log(LOGINFO, "create album compilation index");
     m_pDS->exec("CREATE INDEX idxAlbum_1 ON album(bCompilation)");
+    CLog::Log(LOGINFO, "create unique album name index");
+    m_pDS->exec("CREATE UNIQUE INDEX idxAlbum_2 ON album(strMusicBrainzAlbumID(36))");
 
     CLog::Log(LOGINFO, "create album_artist indexes");
     m_pDS->exec("CREATE UNIQUE INDEX idxAlbumArtist_1 ON album_artist ( idAlbum, idArtist )\n");
@@ -152,8 +154,11 @@ bool CMusicDatabase::CreateTables()
 
     CLog::Log(LOGINFO, "create genre index");
     m_pDS->exec("CREATE INDEX idxGenre ON genre(strGenre)");
-    CLog::Log(LOGINFO, "create artist index");
+
+    CLog::Log(LOGINFO, "create artist indexes");
     m_pDS->exec("CREATE INDEX idxArtist ON artist(strArtist)");
+    m_pDS->exec("CREATE UNIQUE INDEX idxArtist1 ON artist(strMusicBrainzArtistID(36))");
+
     CLog::Log(LOGINFO, "create path index");
     m_pDS->exec("CREATE INDEX idxPath ON path(strPath)");
 
@@ -167,6 +172,8 @@ bool CMusicDatabase::CreateTables()
     m_pDS->exec("CREATE INDEX idxSong3 ON song(idAlbum)");
     CLog::Log(LOGINFO, "create song index6");
     m_pDS->exec("CREATE UNIQUE INDEX idxSong6 ON song( idPath, strFileName(255) )");
+    CLog::Log(LOGINFO, "create song index7");
+    m_pDS->exec("CREATE UNIQUE INDEX idxSong7 ON song( idAlbum, strMusicBrainzTrackID(36) )");
 
     CLog::Log(LOGINFO, "create song_artist indexes");
     m_pDS->exec("CREATE UNIQUE INDEX idxSongArtist_1 ON song_artist ( idSong, idArtist )\n");
@@ -224,8 +231,7 @@ void CMusicDatabase::CreateViews()
               "  song.strGenres AS strGenres,"
               "  strTitle, iTrack, iDuration,"
               "  song.iYear AS iYear, dwFileNameCRC, strFileName, strMusicBrainzTrackID,"
-              "  strMusicBrainzArtistID, strMusicBrainzAlbumID, strMusicBrainzAlbumArtistID,"
-              "  strMusicBrainzTRMID, iTimesPlayed, iStartOffset, iEndOffset, lastplayed,"
+              "  iTimesPlayed, iStartOffset, iEndOffset, lastplayed,"
               "  rating, comment, song.idAlbum AS idAlbum, strAlbum, strPath,"
               "  iKaraNumber, iKaraDelay, strKaraEncoding,"
               "  album.bCompilation AS bCompilation,"
@@ -240,26 +246,70 @@ void CMusicDatabase::CreateViews()
 
   CLog::Log(LOGINFO, "create album view");
   m_pDS->exec("DROP VIEW IF EXISTS albumview");
-  m_pDS->exec("CREATE VIEW albumview AS SELECT"
-              "  album.idAlbum AS idAlbum, strAlbum, "
-              "  album.strArtists AS strArtists,"
-              "  album.strGenres AS strGenres, "
-              "  album.iYear AS iYear,"
-              "  idAlbumInfo, strMoods, strStyles, strThemes,"
-              "  strReview, strLabel, strType, strImage, iRating, "
-              "  bCompilation, "
-              "  MIN(song.iTimesPlayed) AS iTimesPlayed "
-              "FROM album "
-              "  LEFT OUTER JOIN albuminfo ON"
-              "    album.idAlbum=albuminfo.idAlbum"
-              "  LEFT OUTER JOIN song ON"
-              "    album.idAlbum=song.idAlbum "
-              "GROUP BY album.idAlbum");
+  if (m_sqlite)
+  {
+    m_pDS->exec("CREATE VIEW albumview AS SELECT "
+                "        album.idAlbum AS idAlbum, "
+                "        strAlbum, "
+                "        strMusicBrainzAlbumID, "
+                "        GROUP_CONCAT(strArtist || strJoinPhrase, '') as strArtists, "
+                "        album.strGenres AS strGenres, "
+                "        album.iYear AS iYear, "
+                "        idAlbumInfo, "
+                "        strMoods, "
+                "        strStyles, "
+                "        strThemes, "
+                "        strReview, "
+                "        strLabel, "
+                "        strType, "
+                "        strImage, "
+                "        iRating, "
+                "        bCompilation, "
+                "        (SELECT MIN(iTimesPlayed) AS iTimesPlayed FROM song WHERE song.idAlbum = album.idAlbum)"
+                "   FROM album  "
+                "   LEFT OUTER JOIN "
+                "       albuminfo ON album.idAlbum = albuminfo.idAlbum "
+                "   LEFT OUTER JOIN album_artist ON "
+                "       album.idAlbum = album_artist.idAlbum "
+                "   LEFT OUTER JOIN artist ON "
+                "       album_artist.idArtist = artist.idArtist "
+                "   GROUP BY album.idAlbum");
+  }
+  else
+  {
+    m_pDS->exec("CREATE VIEW albumview AS SELECT "
+                "        album.idAlbum AS idAlbum, "
+                "        strAlbum, "
+                "        strMusicBrainzAlbumID, "
+                "        GROUP_CONCAT(strArtist, strJoinPhrase ORDER BY iOrder SEPARATOR '') as strArtists "
+                "        album.strGenres AS strGenres, "
+                "        album.iYear AS iYear, "
+                "        idAlbumInfo, "
+                "        strMoods, "
+                "        strStyles, "
+                "        strThemes, "
+                "        strReview, "
+                "        strLabel, "
+                "        strType, "
+                "        strImage, "
+                "        iRating, "
+                "        bCompilation, "
+                "        (SELECT MIN(iTimesPlayed) AS iTimesPlayed FROM song WHERE song.idAlbum = album.idAlbum)"
+                "   FROM album  "
+                "   LEFT OUTER JOIN "
+                "       albuminfo ON album.idAlbum = albuminfo.idAlbum "
+                "   LEFT OUTER JOIN album_artist ON "
+                "       album.idAlbum = album_artist.idAlbum "
+                "   LEFT OUTER JOIN artist ON "
+                "       album_artist.idArtist = artist.idArtist "
+                "   GROUP BY album.idAlbum");
+  }
 
   CLog::Log(LOGINFO, "create artist view");
   m_pDS->exec("DROP VIEW IF EXISTS artistview");
   m_pDS->exec("CREATE VIEW artistview AS SELECT"
               "  artist.idArtist AS idArtist, strArtist, "
+              "  artist.strMusicBrainzArtistID AS strMusicBrainzArtistID, "
               "  strBorn, strFormed, strGenres,"
               "  strMoods, strStyles, strInstruments, "
               "  strBiography, strDied, strDisbanded, "
@@ -269,20 +319,6 @@ void CMusicDatabase::CreateViews()
               "    artist.idArtist = artistinfo.idArtist");
 }
 
-int CMusicDatabase::AddAlbum(const CAlbum &album, vector<int> &songIDs)
-{
-  // add the album
-  int idAlbum = AddAlbum(album.strAlbum, StringUtils::Join(album.artist, g_advancedSettings.m_musicItemSeparator), StringUtils::Join(album.genre, g_advancedSettings.m_musicItemSeparator), album.iYear, album.bCompilation);
-
-  SetArtForItem(idAlbum, "album", album.art);
-
-  // add the songs
-  for (VECSONGS::const_iterator i = album.songs.begin(); i != album.songs.end(); ++i)
-    songIDs.push_back(AddSong(*i, false, idAlbum));
-
-  return idAlbum;
-}
-
 int CMusicDatabase::AddSong(const CSong& song, bool bCheck, int idAlbum)
 {
   int idSong = -1;
@@ -293,21 +329,12 @@ int CMusicDatabase::AddSong(const CSong& song, bool bCheck, int idAlbum)
     if (song.strTitle.IsEmpty())
       return -1;
 
-    CStdString strPath, strFileName;
-    URIUtils::Split(song.strFileName, strPath, strFileName);
-
     if (NULL == m_pDB.get()) return -1;
     if (NULL == m_pDS.get()) return -1;
 
+    CStdString strPath, strFileName;
+    URIUtils::Split(song.strFileName, strPath, strFileName);
     int idPath = AddPath(strPath);
-    if (idAlbum < 0)
-    {
-      if (!song.albumArtist.empty())  // have an album artist
-        idAlbum = AddAlbum(song.strAlbum, StringUtils::Join(song.albumArtist, g_advancedSettings.m_musicItemSeparator), StringUtils::Join(song.genre, g_advancedSettings.m_musicItemSeparator), song.iYear, song.bCompilation);
-      else
-        idAlbum = AddAlbum(song.strAlbum, StringUtils::Join(song.artist, g_advancedSettings.m_musicItemSeparator), StringUtils::Join(song.genre, g_advancedSettings.m_musicItemSeparator), song.iYear, song.bCompilation);
-    }
-
     DWORD crc = ComputeCRC(song.strFileName);
 
     bool bInsert = true;
@@ -318,8 +345,12 @@ int CMusicDatabase::AddSong(const CSong& song, bool bCheck, int idAlbum)
 
     if (bCheck)
     {
-      strSQL=PrepareSQL("select * from song where idAlbum=%i and dwFileNameCRC='%ul' and strTitle='%s'",
-                    idAlbum, crc, song.strTitle.c_str());
+      strSQL=PrepareSQL("SELECT * FROM song WHERE (idAlbum = %i AND strMusicBrainzTrackID = '%s') OR (idAlbum=%i AND dwFileNameCRC='%ul' AND strTitle='%s' AND strMusicBrainzTrackID IS NULL)",
+                        idAlbum,
+                        song.strMusicBrainzTrackID.c_str(),
+                        idAlbum,
+                        crc,
+                        song.strTitle.c_str());
 
       if (!m_pDS->query(strSQL.c_str()))
         return -1;
@@ -343,19 +374,20 @@ int CMusicDatabase::AddSong(const CSong& song, bool bCheck, int idAlbum)
 
       // we use replace because it can handle both inserting a new song
       // and replacing an existing song's record if the given idSong already exists
-      strSQL=PrepareSQL("replace into song (idSong,idAlbum,idPath,strArtists,strGenres,strTitle,iTrack,iDuration,iYear,dwFileNameCRC,strFileName,strMusicBrainzTrackID,strMusicBrainzArtistID,strMusicBrainzAlbumID,strMusicBrainzAlbumArtistID,strMusicBrainzTRMID,iTimesPlayed,iStartOffset,iEndOffset,lastplayed,rating,comment) values (%s,%i,%i,'%s','%s','%s',%i,%i,%i,'%ul','%s','%s','%s','%s','%s','%s'",
+      strSQL=PrepareSQL("replace into song (idSong,idAlbum,idPath,strArtists,strGenres,strTitle,iTrack,iDuration,iYear,dwFileNameCRC,strFileName,strMusicBrainzTrackID,iTimesPlayed,iStartOffset,iEndOffset,lastplayed,rating,comment) values (%s,%i,%i,'%s','%s','%s',%i,%i,%i,'%ul','%s'",
                     strIdSong.c_str(),
-                    idAlbum, idPath, 
+                    song.idAlbum,
+                    idPath,
                     StringUtils::Join(song.artist, g_advancedSettings.m_musicItemSeparator).c_str(),
                     StringUtils::Join(song.genre, g_advancedSettings.m_musicItemSeparator).c_str(),
                     song.strTitle.c_str(),
                     song.iTrack, song.iDuration, song.iYear,
-                    crc, strFileName.c_str(),
-                    song.strMusicBrainzTrackID.c_str(),
-                    song.strMusicBrainzArtistID.c_str(),
-                    song.strMusicBrainzAlbumID.c_str(),
-                    song.strMusicBrainzAlbumArtistID.c_str(),
-                    song.strMusicBrainzTRMID.c_str());
+                    crc, strFileName.c_str());
+
+      if (song.strMusicBrainzTrackID.IsEmpty())
+        strSQL += PrepareSQL(",NULL");
+      else
+        strSQL += PrepareSQL(",'%s'", song.strMusicBrainzTrackID.c_str());
 
       if (song.lastPlayed.IsValid())
         strSQL1=PrepareSQL(",%i,%i,%i,'%s','%c','%s')",
@@ -376,18 +408,6 @@ int CMusicDatabase::AddSong(const CSong& song, bool bCheck, int idAlbum)
     if (!song.strThumb.empty())
       SetArtForItem(idSong, "song", "thumb", song.strThumb);
 
-    for (unsigned int index = 0; index < song.albumArtist.size(); index++)
-    {
-      int idAlbumArtist = AddArtist(song.albumArtist[index]);
-      AddAlbumArtist(idAlbumArtist, idAlbum, index > 0 ? true : false, index);
-    }
-
-    for (unsigned int index = 0; index < song.artist.size(); index++)
-    {
-      int idArtist = AddArtist(song.artist[index]);
-      AddSongArtist(idArtist, idSong, index > 0 ? true : false, index);
-    }
-
     unsigned int index = 0;
     // If this is karaoke song, change the genre to 'Karaoke' (and add it if it's not there)
     if ( bHasKaraoke && g_advancedSettings.m_karaokeChangeGenreForKaraokeSongs )
@@ -427,16 +447,6 @@ int CMusicDatabase::UpdateSong(const CSong& song, int idSong /* = -1 */)
   if (idSong < 0)
     return -1;
 
-  // delete linked songs
-  // we don't delete from the song table here because
-  // AddSong will update the existing record
-  sql.Format("delete from song_artist where idSong=%d", idSong);
-  ExecuteQuery(sql);
-  sql.Format("delete from song_genre where idSong=%d", idSong);
-  ExecuteQuery(sql);
-  sql.Format("delete from karaokedata where idSong=%d", idSong);
-  ExecuteQuery(sql);
-
   CSong newSong = song;
   // Make sure newSong.idSong has a valid value (> 0)
   newSong.idSong = idSong;
@@ -448,58 +458,57 @@ int CMusicDatabase::UpdateSong(const CSong& song, int idSong /* = -1 */)
   return newSong.idSong;
 }
 
-int CMusicDatabase::AddAlbum(const CStdString& strAlbum1, const CStdString &strArtist, const CStdString& strGenre, int year, bool bCompilation)
+int CMusicDatabase::AddAlbum(const CStdString& strAlbum, const CStdString& strMusicBrainzAlbumID, const CStdString& strArtist, const CStdString& strGenre, int year, bool bCompilation)
 {
   CStdString strSQL;
   try
   {
-    CStdString strAlbum=strAlbum1;
-    strAlbum.Trim();
-
     if (NULL == m_pDB.get()) return -1;
     if (NULL == m_pDS.get()) return -1;
 
-    map <CStdString, CAlbum>::const_iterator it;
-
-    it = m_albumCache.find(strAlbum + strArtist);
-    if (it != m_albumCache.end())
-      return it->second.idAlbum;
-
-    strSQL=PrepareSQL("select * from album where strArtists='%s' and strAlbum like '%s'", strArtist.c_str(), strAlbum.c_str());
+    strSQL=PrepareSQL("SELECT * FROM album WHERE strMusicBrainzAlbumID = '%s' OR (strArtists = '%s' AND strAlbum like '%s' and strMusicBrainzAlbumID IS NULL)",
+                      strMusicBrainzAlbumID.c_str(),
+                      strArtist.c_str(),
+                      strAlbum.c_str());
     m_pDS->query(strSQL.c_str());
 
     if (m_pDS->num_rows() == 0)
     {
       m_pDS->close();
       // doesnt exists, add it
-      strSQL=PrepareSQL("insert into album (idAlbum, strAlbum, strArtists, strGenres, iYear, bCompilation) values( NULL, '%s', '%s', '%s', %i, %i)", strAlbum.c_str(), strArtist.c_str(), strGenre.c_str(), year, bCompilation);
+      if (strMusicBrainzAlbumID.IsEmpty())
+        strSQL=PrepareSQL("insert into album (idAlbum, strAlbum, strMusicBrainzAlbumID, strArtists, strGenres, iYear, bCompilation) values( NULL, '%s', NULL, '%s', '%s', %i, %i)",
+                          strAlbum.c_str(),
+                          strArtist.c_str(),
+                          strGenre.c_str(),
+                          year,
+                          bCompilation);
+      else
+        strSQL=PrepareSQL("insert into album (idAlbum, strAlbum, strMusicBrainzAlbumID, strArtists, strGenres, iYear, bCompilation) values( NULL, '%s', '%s', '%s', '%s', %i, %i)",
+                          strAlbum.c_str(),
+                          strMusicBrainzAlbumID.c_str(),
+                          strArtist.c_str(),
+                          strGenre.c_str(),
+                          year,
+                          bCompilation);
       m_pDS->exec(strSQL.c_str());
 
-      CAlbum album;
-      album.idAlbum = (int)m_pDS->lastinsertid();
-      album.strAlbum = strAlbum;
-      album.artist = StringUtils::Split(strArtist, g_advancedSettings.m_musicItemSeparator);
-      m_albumCache.insert(pair<CStdString, CAlbum>(album.strAlbum + strArtist, album));
-      return album.idAlbum;
+      return (int)m_pDS->lastinsertid();
     }
     else
     {
       // exists in our database and not scanned during this scan, so we should update it as the details
       // may have changed (there's a reason we're rescanning, afterall!)
-      CAlbum album;
-      album.idAlbum = m_pDS->fv("idAlbum").get_asInt();
-      album.strAlbum = strAlbum;
-      album.artist = StringUtils::Split(strArtist, g_advancedSettings.m_musicItemSeparator);
-      m_albumCache.insert(pair<CStdString, CAlbum>(album.strAlbum + strArtist, album));
+      int idAlbum = m_pDS->fv("idAlbum").get_asInt();
       m_pDS->close();
-      strSQL=PrepareSQL("update album set strGenres='%s', iYear=%i where idAlbum=%i", strGenre.c_str(), year, album.idAlbum);
+      strSQL=PrepareSQL("update album set strGenres='%s', iYear=%i where idAlbum=%i", strGenre.c_str(), year, idAlbum);
       m_pDS->exec(strSQL.c_str());
       // and clear the link tables - these are updated in AddSong()
-      strSQL=PrepareSQL("delete from album_artist where idAlbum=%i", album.idAlbum);
+      strSQL=PrepareSQL("delete from album_artist where idAlbum=%i", idAlbum);
       m_pDS->exec(strSQL.c_str());
-      strSQL=PrepareSQL("delete from album_genre where idAlbum=%i", album.idAlbum);
+      strSQL=PrepareSQL("delete from album_genre where idAlbum=%i", idAlbum);
       m_pDS->exec(strSQL.c_str());
-      return album.idAlbum;
+      return idAlbum;
     }
   }
   catch (...)
@@ -559,43 +568,37 @@ int CMusicDatabase::AddGenre(const CStdString& strGenre1)
   return -1;
 }
 
-int CMusicDatabase::AddArtist(const CStdString& strArtist1)
+int CMusicDatabase::AddArtist(const CStdString& strArtist, const CStdString& strMusicBrainzArtistID)
 {
   CStdString strSQL;
   try
   {
-    CStdString strArtist = strArtist1;
-    strArtist.Trim();
-
-    if (strArtist.IsEmpty())
-      strArtist=g_localizeStrings.Get(13205); // Unknown
-
     if (NULL == m_pDB.get()) return -1;
     if (NULL == m_pDS.get()) return -1;
 
-    map <CStdString, int>::const_iterator it;
-
-    it = m_artistCache.find(strArtist);
-    if (it != m_artistCache.end())
-      return it->second;//.idArtist;
-
-    strSQL=PrepareSQL("select * from artist where strArtist like '%s'", strArtist.c_str());
+     strSQL = PrepareSQL("SELECT * FROM artist WHERE strMusicBrainzArtistID = '%s' OR (strArtist = '%s' AND strMusicBrainzArtistID IS NULL)",
+                         strMusicBrainzArtistID.IsEmpty() ? "x" : strMusicBrainzArtistID.c_str(),
+                         strArtist.c_str());
     m_pDS->query(strSQL.c_str());
 
     if (m_pDS->num_rows() == 0)
     {
       m_pDS->close();
       // doesnt exists, add it
-      strSQL=PrepareSQL("insert into artist (idArtist, strArtist) values( NULL, '%s' )", strArtist.c_str());
+      if (strMusicBrainzArtistID.IsEmpty())
+        strSQL = PrepareSQL("INSERT INTO artist (idArtist, strArtist, strMusicBrainzArtistID) VALUES( NULL, '%s', NULL )",
+                            strArtist.c_str());
+      else
+        strSQL = PrepareSQL("INSERT INTO artist (idArtist, strArtist, strMusicBrainzArtistID) VALUES( NULL, '%s', '%s' )",
+                            strArtist.c_str(),
+                            strMusicBrainzArtistID.c_str());
       m_pDS->exec(strSQL.c_str());
       int idArtist = (int)m_pDS->lastinsertid();
-      m_artistCache.insert(pair<CStdString, int>(strArtist1, idArtist));
       return idArtist;
     }
     else
     {
       int idArtist = (int)m_pDS->fv("idArtist").get_asInt();
-      m_artistCache.insert(pair<CStdString, int>(strArtist1, idArtist));
       m_pDS->close();
       return idArtist;
     }
@@ -608,19 +611,19 @@ int CMusicDatabase::AddArtist(const CStdString& strArtist1)
   return -1;
 }
 
-bool CMusicDatabase::AddSongArtist(int idArtist, int idSong, bool featured, int iOrder)
+bool CMusicDatabase::AddSongArtist(int idArtist, int idSong, std::string joinPhrase, bool featured, int iOrder)
 {
   CStdString strSQL;
-  strSQL=PrepareSQL("replace into song_artist (idArtist, idSong, boolFeatured, iOrder) values(%i,%i,%i,%i)",
-                    idArtist, idSong, featured == true ? 1 : 0, iOrder);
+  strSQL=PrepareSQL("replace into song_artist (idArtist, idSong, strJoinPhrase, boolFeatured, iOrder) values(%i,%i,'%s',%i,%i)",
+                    idArtist, idSong, joinPhrase.c_str(), featured == true ? 1 : 0, iOrder);
   return ExecuteQuery(strSQL);
 };
 
-bool CMusicDatabase::AddAlbumArtist(int idArtist, int idAlbum, bool featured, int iOrder)
+bool CMusicDatabase::AddAlbumArtist(int idArtist, int idAlbum, std::string joinPhrase, bool featured, int iOrder)
 {
   CStdString strSQL;
-  strSQL=PrepareSQL("replace into album_artist (idArtist, idAlbum, boolFeatured, iOrder) values(%i,%i,%i,%i)",
-                    idArtist, idAlbum, featured == true ? 1 : 0, iOrder);
+  strSQL=PrepareSQL("replace into album_artist (idArtist, idAlbum, strJoinPhrase, boolFeatured, iOrder) values(%i,%i,'%s',%i,%i)",
+                    idArtist, idAlbum, joinPhrase.c_str(), featured == true ? 1 : 0, iOrder);
   return ExecuteQuery(strSQL);
 };
 
@@ -906,10 +909,6 @@ CSong CMusicDatabase::GetSongFromDataset(bool bWithMusicDbPath/*=false*/)
   song.iStartOffset = m_pDS->fv(song_iStartOffset).get_asInt();
   song.iEndOffset = m_pDS->fv(song_iEndOffset).get_asInt();
   song.strMusicBrainzTrackID = m_pDS->fv(song_strMusicBrainzTrackID).get_asString();
-  song.strMusicBrainzArtistID = m_pDS->fv(song_strMusicBrainzArtistID).get_asString();
-  song.strMusicBrainzAlbumID = m_pDS->fv(song_strMusicBrainzAlbumID).get_asString();
-  song.strMusicBrainzAlbumArtistID = m_pDS->fv(song_strMusicBrainzAlbumArtistID).get_asString();
-  song.strMusicBrainzTRMID = m_pDS->fv(song_strMusicBrainzTRMID).get_asString();
   song.rating = m_pDS->fv(song_rating).get_asChar();
   song.strComment = m_pDS->fv(song_comment).get_asString();
   song.iKaraokeNumber = m_pDS->fv(song_iKarNumber).get_asInt();
@@ -957,10 +956,6 @@ void CMusicDatabase::GetFileItemFromDataset(const dbiplus::sql_record* const rec
   item->SetProperty("item_start", item->m_lStartOffset);
   item->m_lEndOffset = record->at(song_iEndOffset).get_asInt();
   item->GetMusicInfoTag()->SetMusicBrainzTrackID(record->at(song_strMusicBrainzTrackID).get_asString());
-  item->GetMusicInfoTag()->SetMusicBrainzArtistID(record->at(song_strMusicBrainzArtistID).get_asString());
-  item->GetMusicInfoTag()->SetMusicBrainzAlbumID(record->at(song_strMusicBrainzAlbumID).get_asString());
-  item->GetMusicInfoTag()->SetMusicBrainzAlbumArtistID(record->at(song_strMusicBrainzAlbumArtistID).get_asString());
-  item->GetMusicInfoTag()->SetMusicBrainzTRMID(record->at(song_strMusicBrainzTRMID).get_asString());
   item->GetMusicInfoTag()->SetRating(record->at(song_rating).get_asChar());
   item->GetMusicInfoTag()->SetComment(record->at(song_comment).get_asString());
   item->GetMusicInfoTag()->SetPlayCount(record->at(song_iTimesPlayed).get_asInt());
@@ -1000,6 +995,7 @@ CAlbum CMusicDatabase::GetAlbumFromDataset(const dbiplus::sql_record* const reco
   album.strAlbum = record->at(album_strAlbum).get_asString();
   if (album.strAlbum.IsEmpty())
     album.strAlbum = g_localizeStrings.Get(1050);
+  album.strMusicBrainzAlbumID = record->at(album_strMusicBrainzAlbumID).get_asString();
   album.artist = StringUtils::Split(record->at(album_strArtists).get_asString(), g_advancedSettings.m_musicItemSeparator);
   album.genre = StringUtils::Split(record->at(album_strGenres).get_asString(), g_advancedSettings.m_musicItemSeparator);
   album.iYear = record->at(album_iYear).get_asInt();
@@ -1018,6 +1014,17 @@ CAlbum CMusicDatabase::GetAlbumFromDataset(const dbiplus::sql_record* const reco
   return album;
 }
 
+CArtistCredit CMusicDatabase::GetAlbumArtistCreditFromDataset(const dbiplus::sql_record* const record)
+{
+  CArtistCredit artistCredit;
+  artistCredit.idArtist = record->at(album_idArtist).get_asInt();
+  artistCredit.m_strArtist = record->at(album_strArtist).get_asString();
+  artistCredit.m_strMusicBrainzArtistID = record->at(album_strMusicBrainzArtistID).get_asString();
+  artistCredit.m_boolFeatured = record->at(album_bFeatured).get_asBool();
+  artistCredit.m_strJoinPhrase = record->at(album_strJoinPhrase).get_asString();
+  return artistCredit;
+}
+
 CArtist CMusicDatabase::GetArtistFromDataset(dbiplus::Dataset* pDS, bool needThumb)
 {
   return GetArtistFromDataset(pDS->get_sql_record(), needThumb);
@@ -1028,6 +1035,7 @@ CArtist CMusicDatabase::GetArtistFromDataset(const dbiplus::sql_record* const re
   CArtist artist;
   artist.idArtist = record->at(artist_idArtist).get_asInt();
   artist.strArtist = record->at(artist_strArtist).get_asString();
+  artist.strMusicBrainzArtistID = record->at(artist_strMusicBrainzArtistID).get_asString();
   artist.genre = StringUtils::Split(record->at(artist_strGenres).get_asString(), g_advancedSettings.m_musicItemSeparator);
   artist.strBiography = record->at(artist_strBiography).get_asString();
   artist.styles = StringUtils::Split(record->at(artist_strStyles).get_asString(), g_advancedSettings.m_musicItemSeparator);
@@ -1186,18 +1194,16 @@ bool CMusicDatabase::SearchArtists(const CStdString& search, CFileItemList &arti
     if (NULL == m_pDB.get()) return false;
     if (NULL == m_pDS.get()) return false;
 
-    // Exclude "Various Artists"
-    int idVariousArtist = AddArtist(g_localizeStrings.Get(340));
-
+    CStdString strVariousArtists = g_localizeStrings.Get(340).c_str();
     CStdString strSQL;
     if (search.GetLength() >= MIN_FULL_SEARCH_LENGTH)
       strSQL=PrepareSQL("select * from artist "
-                                "where (strArtist like '%s%%' or strArtist like '%% %s%%') and idArtist <> %i "
-                                , search.c_str(), search.c_str(), idVariousArtist );
+                                "where (strArtist like '%s%%' or strArtist like '%% %s%%') and strArtist <> '%s' "
+                                , search.c_str(), search.c_str(), strVariousArtists.c_str() );
     else
       strSQL=PrepareSQL("select * from artist "
-                                "where strArtist like '%s%%' and idArtist <> %i "
-                                , search.c_str(), idVariousArtist );
+                                "where strArtist like '%s%%' and strArtist <> '%s' "
+                                , search.c_str(), strVariousArtists.c_str() );
 
     if (!m_pDS->query(strSQL.c_str())) return false;
     if (m_pDS->num_rows() == 0)
@@ -1243,25 +1249,32 @@ bool CMusicDatabase::GetAlbumInfo(int idAlbum, CAlbum &info, VECSONGS* songs, bo
     if (idAlbum == -1)
       return false; // not in the database
 
-    CStdString strSQL=PrepareSQL("select * from albumview where idAlbum = %ld", idAlbum);
+    CStdString strSQL=PrepareSQL("SELECT albumview.*, album_artist.idArtist, artist.strArtist, artist.strMusicBrainzArtistID, album_artist.boolFeatured, album_artist.strJoinPhrase FROM albumview JOIN album_artist ON albumview.idAlbum = album_artist.idAlbum JOIN artist ON album_artist.idArtist = artist.idArtist WHERE albumview.idAlbum = %ld", idAlbum);
     if (scrapedInfo) // require additional information
       strSQL += " and idAlbumInfo > 0";
 
     if (!m_pDS2->query(strSQL.c_str())) return false;
-    int iRowsFound = m_pDS2->num_rows();
-    if (iRowsFound != 0)
+    if (m_pDS2->num_rows() == 0)
     {
-      info = GetAlbumFromDataset(m_pDS2.get(), true); // true to grab the thumburl rather than the thumb
-      int idAlbumInfo = m_pDS2->fv(album_idAlbumInfo).get_asInt();
-      m_pDS2->close(); // cleanup recordset data
-
-      if (songs)
-        GetAlbumInfoSongs(idAlbumInfo, *songs);
+      m_pDS2->close();
+      return false;
+    }
 
-      return true;
+    info = GetAlbumFromDataset(m_pDS2.get()->get_sql_record(), true); // true to grab the thumburl rather than the thumb
+    int idAlbumInfo = m_pDS2->fv(album_idAlbumInfo).get_asInt();
+    while (!m_pDS2->eof())
+    {
+      if (!info.artistCredits.empty() && (m_pDS2->fv(album_idArtist).get_asInt() != info.artistCredits.back().idArtist))
+      {
+        info.artistCredits.push_back(GetAlbumArtistCreditFromDataset(m_pDS2.get()->get_sql_record()));
+      }
+      m_pDS2->next();
     }
-    m_pDS2->close();
-    return false;
+    if (songs)
+      GetAlbumInfoSongs(idAlbumInfo, *songs);
+    
+    m_pDS2->close(); // cleanup recordset data
+    return true;
   }
   catch (...)
   {
@@ -2158,13 +2171,9 @@ bool CMusicDatabase::CleanupArtists()
     // (nested queries by Bobbin007)
     // must be executed AFTER the song, album and their artist link tables are cleaned.
     // don't delete the "Various Artists" string
-    CStdString strVariousArtists = g_localizeStrings.Get(340);
-    int idVariousArtists = AddArtist(strVariousArtists);
     CStdString strSQL = "delete from artist where idArtist not in (select idArtist from song_artist)";
     strSQL += " and idArtist not in (select idArtist from album_artist)";
     CStdString strSQL2;
-    strSQL2.Format(" and idArtist<>%i", idVariousArtists);
-    strSQL += strSQL2;
     m_pDS->exec(strSQL.c_str());
     m_pDS->exec("delete from artistinfo where idArtist not in (select idArtist from artist)");
     m_pDS->exec("delete from album_artist where idArtist not in (select idArtist from artist)");
@@ -3056,40 +3065,6 @@ bool CMusicDatabase::GetAlbumFromSong(int idSong, CAlbum &album)
   return false;
 }
 
-// This function won't be required if/when the fileitem tag has idSong information
-bool CMusicDatabase::GetAlbumFromSong(const CSong &song, CAlbum &album)
-{
-  try
-  {
-    if (NULL == m_pDB.get()) return false;
-    if (NULL == m_pDS.get()) return false;
-
-    if (song.idSong != -1) return GetAlbumFromSong(song.idSong, album);
-
-    CStdString path, file;
-    URIUtils::Split(song.strFileName, path, file);
-
-    CStdString strSQL = PrepareSQL("select albumview.* from song join albumview on song.idAlbum = albumview.idAlbum join path on song.idPath = path.idPath where song.strFileName='%s' and path.strPath='%s'", file.c_str(), path.c_str());
-    if (!m_pDS->query(strSQL.c_str())) return false;
-    int iRowsFound = m_pDS->num_rows();
-    if (iRowsFound != 1)
-    {
-      m_pDS->close();
-      return false;
-    }
-
-    album = GetAlbumFromDataset(m_pDS.get());
-
-    m_pDS->close();
-    return true;
-  }
-  catch (...)
-  {
-    CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
-  }
-  return false;
-}
-
 bool CMusicDatabase::GetAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre /* = -1 */, int idArtist /* = -1 */, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */, bool countOnly /* = false */)
 {
   CMusicDbUrl musicUrl;
@@ -3722,6 +3697,33 @@ bool CMusicDatabase::UpdateOldVersion(int version)
     m_pDS->exec("DROP INDEX idxSong6 ON song");
     m_pDS->exec("CREATE UNIQUE INDEX idxSong6 on song( idPath, strFileName(255) )");
   }
+
+  if (version < 34)
+  {
+    m_pDS->exec("ALTER TABLE artist ADD strMusicBrainzArtistID text\n");
+    m_pDS->exec("ALTER TABLE album ADD strMusicBrainzAlbumID text\n");
+    m_pDS->exec("CREATE TABLE song_new ( idSong integer primary key, idAlbum integer, idPath integer, strArtists text, strGenres text, strTitle varchar(512), iTrack integer, iDuration integer, iYear integer, dwFileNameCRC text, strFileName text, strMusicBrainzTrackID text, iTimesPlayed integer, iStartOffset integer, iEndOffset integer, idThumb integer, lastplayed varchar(20) default NULL, rating char default '0', comment text)\n");
+    m_pDS->exec("INSERT INTO song_new ( idSong, idAlbum, idPath, strArtists, strTitle, iTrack, iDuration, iYear, dwFileNameCRC, strFileName, strMusicBrainzTrackID, iTimesPlayed, iStartOffset, iEndOffset, idThumb, lastplayed, rating, comment) SELECT idSong, idAlbum, idPath, strArtists, strTitle, iTrack, iDuration, iYear, dwFileNameCRC, strFileName, strMusicBrainzTrackID, iTimesPlayed, iStartOffset, iEndOffset, idThumb, lastplayed, rating, comment FROM song");
+    
+    m_pDS->exec("DROP TABLE song");
+    m_pDS->exec("ALTER TABLE song_new RENAME TO song");
+    m_pDS->exec("CREATE INDEX idxSong ON song(strTitle)");
+    m_pDS->exec("CREATE INDEX idxSong1 ON song(iTimesPlayed)");
+    m_pDS->exec("CREATE INDEX idxSong2 ON song(lastplayed)");
+    m_pDS->exec("CREATE INDEX idxSong3 ON song(idAlbum)");
+    m_pDS->exec("CREATE INDEX idxSong6 ON song(idPath)");
+
+    m_pDS->exec("CREATE UNIQUE INDEX idxArtist1 ON artist(strMusicBrainzArtistID(36))");
+  }
+
+  if (version < 35)
+  {
+    m_pDS->exec("CREATE UNIQUE INDEX idxAlbum_2 ON album(strMusicBrainzAlbumID(36))");
+    m_pDS->exec("CREATE UNIQUE INDEX idxSong7 ON song( idAlbum, strMusicBrainzTrackID(36) )");
+    m_pDS->exec("ALTER TABLE album_artist ADD strJoinPhrase text\n");
+    m_pDS->exec("ALTER TABLE song_artist ADD strJoinPhrase text\n");
+  }
+
   // always recreate the views after any table change
   CreateViews();
 
@@ -3730,7 +3732,7 @@ bool CMusicDatabase::UpdateOldVersion(int version)
 
 int CMusicDatabase::GetMinVersion() const
 {
-  return 33;
+  return 35;
 }
 
 unsigned int CMusicDatabase::GetSongIDs(const Filter &filter, vector<pair<int,int> > &songIDs)
@@ -4223,7 +4225,7 @@ bool CMusicDatabase::RemoveSongsFromPath(const CStdString &path1, MAPSONGS& song
   return false;
 }
 
-bool CMusicDatabase::GetPaths(set<CStdString> &paths)
+bool CMusicDatabase::GetPaths(set<string> &paths)
 {
   try
   {
@@ -5416,8 +5418,7 @@ bool CMusicDatabase::GetFilter(CDbUrl &musicUrl, Filter &filter, SortDescription
     if (!albumArtistsOnly)
     {
       CStdString strVariousArtists = g_localizeStrings.Get(340);
-      int idVariousArtists = AddArtist(strVariousArtists);
-      strSQL += PrepareSQL(" and artistview.idArtist <> %i", idVariousArtists);
+      strSQL += PrepareSQL(" and artistview.strArtist <> '%s'", strVariousArtists.c_str());
     }
 
     filter.AppendWhere(strSQL);
@@ -5442,9 +5443,15 @@ bool CMusicDatabase::GetFilter(CDbUrl &musicUrl, Filter &filter, SortDescription
 
     option = options.find("artistid");
     if (option != options.end())
-      filter.AppendWhere(PrepareSQL("albumview.idAlbum IN (SELECT song.idAlbum FROM song JOIN song_artist ON song.idSong = song_artist.idSong WHERE song_artist.idArtist = %i)" // All albums linked to this artist via songs
-                                    " OR albumview.idAlbum IN (SELECT album_artist.idAlbum FROM album_artist WHERE album_artist.idArtist = %i)", // All albums where album artists fit
+    {
+      filter.AppendJoin("JOIN song ON song.idAlbum = albumview.idAlbum "
+                        "JOIN song_artist ON song.idSong = song_artist.idSong "
+                        "JOIN album_artist ON albumview.idAlbum = album_artist.idAlbum");
+      filter.AppendWhere(PrepareSQL("      song_artist.idArtist = %i" // All albums linked to this artist via songs
+                                    " OR  album_artist.idArtist = %i", // All albums where album artists fit
                                     (int)option->second.asInteger(), (int)option->second.asInteger()));
+      filter.AppendGroup("albumview.idAlbum");
+    }
     else
     {
       option = options.find("artist");
index 502494c..0b6fb66 100644 (file)
@@ -131,8 +131,7 @@ public:
    \param songIDs [out] the ids of the added songs
    \return the id of the album
    */
-  int  AddAlbum(const CAlbum &album, std::vector<int> &songIDs);
-  int  AddAlbum(const CStdString& strAlbum, const CStdString& strArtist, const CStdString& strGenre, int year, bool bCompilation);
+  int  AddAlbum(const CStdString& strAlbum, const CStdString& strMusicBrainzAlbumID, const CStdString& strArtist, const CStdString& strGenre, int year, bool bCompilation);
   bool GetAlbum(int idAlbum, CAlbum& album);
   int  UpdateAlbum(int idAlbum, const CAlbum &album);
   bool DeleteAlbum(int idAlbum);
@@ -140,7 +139,6 @@ public:
   //// Misc Album
   int  GetAlbumIdByPath(const CStdString& path);
   bool GetAlbumFromSong(int idSong, CAlbum &album);
-  bool GetAlbumFromSong(const CSong &song, CAlbum &album);
   int  GetAlbumByName(const CStdString& strAlbum, const CStdString& strArtist="");
   int  GetAlbumByName(const CStdString& strAlbum, const std::vector<std::string>& artist);
   CStdString GetAlbumById(int id);
@@ -148,7 +146,7 @@ public:
   /////////////////////////////////////////////////
   // Artist CRUD
   /////////////////////////////////////////////////
-  int  AddArtist(const CStdString& strArtist);
+  int  AddArtist(const CStdString& strArtist, const CStdString& strMusicBrainzArtistID);
   bool GetArtist(int idArtist, CArtist& artist);
   int  UpdateArtist(int idArtist, const CArtist& artist);
   bool DeleteArtist(int idArtist);
@@ -161,7 +159,7 @@ public:
   /////////////////////////////////////////////////
   int AddPath(const CStdString& strPath);
 
-  bool GetPaths(std::set<CStdString> &paths);
+  bool GetPaths(std::set<std::string> &paths);
   bool SetPathHash(const CStdString &path, const CStdString &hash);
   bool GetPathHash(const CStdString &path, CStdString &hash);
   bool GetAlbumPath(int idAlbum, CStdString &path);
@@ -199,11 +197,11 @@ public:
   /////////////////////////////////////////////////
   // Link tables
   /////////////////////////////////////////////////
-  bool AddAlbumArtist(int idArtist, int idAlbum, bool featured, int iOrder);
+  bool AddAlbumArtist(int idArtist, int idAlbum, std::string joinPhrase, bool featured, int iOrder);
   bool GetAlbumsByArtist(int idArtist, bool includeFeatured, std::vector<int>& albums);
   bool GetArtistsByAlbum(int idAlbum, bool includeFeatured, std::vector<int>& artists);
 
-  bool AddSongArtist(int idArtist, int idSong, bool featured, int iOrder);
+  bool AddSongArtist(int idArtist, int idSong, std::string joinPhrase, bool featured, int iOrder);
   bool GetSongsByArtist(int idArtist, bool includeFeatured, std::vector<int>& songs);
   bool GetArtistsBySong(int idSong, bool includeFeatured, std::vector<int>& artists);
 
@@ -380,6 +378,7 @@ protected:
 
   virtual bool CreateTables();
   virtual int GetMinVersion() const;
+
   const char *GetBaseDBName() const { return "MyMusic"; };
 
 
@@ -394,6 +393,7 @@ private:
   CArtist GetArtistFromDataset(const dbiplus::sql_record* const record, bool needThumb = true);
   CAlbum GetAlbumFromDataset(dbiplus::Dataset* pDS, bool imageURL=false);
   CAlbum GetAlbumFromDataset(const dbiplus::sql_record* const record, bool imageURL=false);
+  CArtistCredit GetAlbumArtistCreditFromDataset(const dbiplus::sql_record* const record);
   void GetFileItemFromDataset(CFileItem* item, const CStdString& strMusicDBbasePath);
   void GetFileItemFromDataset(const dbiplus::sql_record* const record, CFileItem* item, const CStdString& strMusicDBbasePath);
   bool CleanupSongs();
@@ -422,10 +422,6 @@ private:
     song_dwFileNameCRC,
     song_strFileName,
     song_strMusicBrainzTrackID,
-    song_strMusicBrainzArtistID,
-    song_strMusicBrainzAlbumID,
-    song_strMusicBrainzAlbumArtistID,
-    song_strMusicBrainzTRMID,
     song_iTimesPlayed,
     song_iStartOffset,
     song_iEndOffset,
@@ -448,6 +444,7 @@ private:
   {
     album_idAlbum=0,
     album_strAlbum,
+    album_strMusicBrainzAlbumID,
     album_strArtists,
     album_strGenres,
     album_iYear,
@@ -461,13 +458,21 @@ private:
     album_strThumbURL,
     album_iRating,
     album_bCompilation,
-    album_iTimesPlayed
+    album_iTimesPlayed,
+
+    // used for GetAlbumInfo to get the cascaded artist credits
+    album_idArtist,
+    album_strArtist,
+    album_strMusicBrainzArtistID,
+    album_bFeatured,
+    album_strJoinPhrase
   } AlbumFields;
 
   enum _ArtistFields
   {
     artist_idArtist=0,
     artist_strArtist,
+    artist_strMusicBrainzArtistID,
     artist_strBorn,
     artist_strFormed,
     artist_strGenres,
index 1fde6b3..52553e8 100644 (file)
 #include "Song.h"
 #include "music/tags/MusicInfoTag.h"
 #include "utils/Variant.h"
+#include "FileItem.h"
+#include "settings/AdvancedSettings.h"
+#include "utils/StringUtils.h"
 
 using namespace std;
 using namespace MUSIC_INFO;
 
-CSong::CSong(CMusicInfoTag& tag)
+CSong::CSong(CFileItem& item)
 {
+  CMusicInfoTag& tag = *item.GetMusicInfoTag();
   SYSTEMTIME stTime;
   tag.GetReleaseDate(stTime);
   strTitle = tag.GetTitle();
   genre = tag.GetGenre();
-  strFileName = tag.GetURL();
   artist = tag.GetArtist();
   strAlbum = tag.GetAlbum();
   albumArtist = tag.GetAlbumArtist();
   strMusicBrainzTrackID = tag.GetMusicBrainzTrackID();
-  strMusicBrainzArtistID = tag.GetMusicBrainzArtistID();
-  strMusicBrainzAlbumID = tag.GetMusicBrainzAlbumID();
-  strMusicBrainzAlbumArtistID = tag.GetMusicBrainzAlbumArtistID();
-  strMusicBrainzTRMID = tag.GetMusicBrainzTRMID();
   strComment = tag.GetComment();
   rating = tag.GetRating();
   iYear = stTime.wYear;
@@ -47,9 +46,10 @@ CSong::CSong(CMusicInfoTag& tag)
   iDuration = tag.GetDuration();
   bCompilation = tag.GetCompilation();
   embeddedArt = tag.GetCoverArtInfo();
-  strThumb = "";
-  iStartOffset = 0;
-  iEndOffset = 0;
+  strFileName = tag.GetURL().IsEmpty() ? item.GetPath() : tag.GetURL();
+  strThumb = item.GetUserMusicThumb(true);
+  iStartOffset = item.m_lStartOffset;
+  iEndOffset = item.m_lEndOffset;
   idSong = -1;
   iTimesPlayed = 0;
   iKaraokeNumber = 0;
@@ -74,10 +74,6 @@ void CSong::Serialize(CVariant& value) const
   value["track"] = iTrack;
   value["year"] = iYear;
   value["musicbrainztrackid"] = strMusicBrainzTrackID;
-  value["musicbrainzartistid"] = strMusicBrainzArtistID;
-  value["musicbrainzalbumid"] = strMusicBrainzAlbumID;
-  value["musicbrainzalbumartistid"] = strMusicBrainzAlbumArtistID;
-  value["musicbrainztrmid"] = strMusicBrainzTRMID;
   value["comment"] = strComment;
   value["rating"] = rating;
   value["timesplayed"] = iTimesPlayed;
@@ -96,10 +92,6 @@ void CSong::Clear()
   genre.clear();
   strThumb.Empty();
   strMusicBrainzTrackID.Empty();
-  strMusicBrainzArtistID.Empty();
-  strMusicBrainzAlbumID.Empty();
-  strMusicBrainzAlbumArtistID.Empty();
-  strMusicBrainzTRMID.Empty();
   strComment.Empty();
   rating = '0';
   iTrack = 0;
index 0a3b641..13d74bf 100644 (file)
@@ -27,7 +27,7 @@
 #include "utils/ISerializable.h"
 #include "XBDateTime.h"
 #include "music/tags/MusicInfoTag.h" // for EmbeddedArt
-
+#include "Artist.h"
 #include <map>
 #include <vector>
 
@@ -44,6 +44,7 @@ public:
   CStdString strGenre;
 };
 
+class CFileItem;
 
 /*!
  \ingroup music
@@ -54,7 +55,7 @@ class CSong: public ISerializable
 {
 public:
   CSong() ;
-  CSong(MUSIC_INFO::CMusicInfoTag& tag);
+  CSong(CFileItem& item);
   virtual ~CSong(){};
   void Clear() ;
   virtual void Serialize(CVariant& value) const;
@@ -82,16 +83,13 @@ public:
   CStdString strFileName;
   CStdString strTitle;
   std::vector<std::string> artist;
+  VECARTISTCREDITS artistCredits;
   CStdString strAlbum;
   std::vector<std::string> albumArtist;
   std::vector<std::string> genre;
   CStdString strThumb;
   MUSIC_INFO::EmbeddedArtInfo embeddedArt;
   CStdString strMusicBrainzTrackID;
-  CStdString strMusicBrainzArtistID;
-  CStdString strMusicBrainzAlbumID;
-  CStdString strMusicBrainzAlbumArtistID;
-  CStdString strMusicBrainzTRMID;
   CStdString strComment;
   char rating;
   int iTrack;
index 14f4b4c..89a0972 100644 (file)
 #include "music/MusicThumbLoader.h"
 #include "interfaces/AnnouncementManager.h"
 #include "GUIUserMessages.h"
+#include "addons/AddonManager.h"
+#include "addons/Scraper.h"
 
 #include <algorithm>
 
 using namespace std;
 using namespace MUSIC_INFO;
 using namespace XFILE;
+using namespace MUSICDATABASEDIRECTORY;
 using namespace MUSIC_GRABBER;
+using namespace ADDON;
 
 CMusicInfoScanner::CMusicInfoScanner() : CThread("MusicInfoScanner"), m_fileCountReader(this, "MusicFileCounter")
 {
@@ -115,7 +119,7 @@ void CMusicInfoScanner::Process()
 
       bool commit = false;
       bool cancelled = false;
-      while (!cancelled && m_pathsToScan.size())
+      for (std::set<std::string>::const_iterator it = m_pathsToScan.begin(); it != m_pathsToScan.end(); it++)
       {
         /*
          * A copy of the directory path is used because the path supplied is
@@ -123,8 +127,7 @@ void CMusicInfoScanner::Process()
          * reference points to the entry in the set a null reference error
          * occurs.
          */
-        CStdString directory = *m_pathsToScan.begin();
-        if (!DoScan(directory))
+        if (!DoScan(*it))
           cancelled = true;
         commit = !cancelled;
       }
@@ -153,27 +156,39 @@ void CMusicInfoScanner::Process()
       m_fileCountReader.StopThread();
 
       m_musicDatabase.EmptyCache();
-
-      m_musicDatabase.Close();
-      CLog::Log(LOGDEBUG, "%s - Finished scan", __FUNCTION__);
-
+      
       tick = XbmcThreads::SystemClockMillis() - tick;
       CLog::Log(LOGNOTICE, "My Music: Scanning for music info using worker thread, operation took %s", StringUtils::SecondsToTimeString(tick / 1000).c_str());
     }
     bool bCanceled;
     if (m_scanType == 1) // load album info
     {
-      int iCurrentItem = 1;
-      for (set<CAlbum>::iterator it=m_albumsToScan.begin();it != m_albumsToScan.end();++it)
+      for (std::set<std::string>::const_iterator it = m_pathsToScan.begin(); it != m_pathsToScan.end(); ++it)
       {
+        CQueryParams params;
+        CDirectoryNode::GetDatabaseInfo(*it, params);
+        if (m_musicDatabase.HasAlbumInfo(params.GetArtistId())) // should this be here?
+          continue;
+
+        CAlbum album;
+        m_musicDatabase.GetAlbumInfo(params.GetAlbumId(), album, &album.songs);
         if (m_handle)
         {
-          m_handle->SetText(StringUtils::Join(it->artist, g_advancedSettings.m_musicItemSeparator)+" - "+it->strAlbum);
-          m_handle->SetPercentage(iCurrentItem++/(float)m_albumsToScan.size());
+          float percentage = (float) std::distance(it, m_pathsToScan.end()) / m_pathsToScan.size();
+          m_handle->SetText(StringUtils::Join(album.artist, g_advancedSettings.m_musicItemSeparator) + " - " + album.strAlbum);
+          m_handle->SetPercentage(percentage);
         }
 
+        // find album info
+        ADDON::ScraperPtr scraper;
+        if (!m_musicDatabase.GetScraperForPath(*it, scraper, ADDON::ADDON_SCRAPER_ALBUMS) || !scraper)
+          continue;
+        
+        CLog::Log(LOGDEBUG, "%s downloading info for: %s", __FUNCTION__, album.strAlbum.c_str());
         CMusicAlbumInfo albumInfo;
-        DownloadAlbumInfo(it->genre[0],StringUtils::Join(it->artist, g_advancedSettings.m_musicItemSeparator),it->strAlbum, bCanceled, albumInfo); // genre field holds path - see fetchalbuminfo()
+        DownloadAlbumInfo(album, scraper, albumInfo); // genre field holds path - see fetchalbuminfo()
+        m_musicDatabase.SetAlbumInfo(params.GetAlbumId(), albumInfo.GetAlbum(), albumInfo.GetAlbum().songs);
+        GetAlbumArtwork(params.GetAlbumId(), albumInfo.GetAlbum());
 
         if (m_bStop || bCanceled)
           break;
@@ -181,16 +196,34 @@ void CMusicInfoScanner::Process()
     }
     if (m_scanType == 2) // load artist info
     {
-      int iCurrentItem=1;
-      for (set<CArtist>::iterator it=m_artistsToScan.begin();it != m_artistsToScan.end();++it)
+      for (std::set<std::string>::const_iterator it = m_pathsToScan.begin(); it != m_pathsToScan.end(); ++it)
       {
+        CQueryParams params;
+        CDirectoryNode::GetDatabaseInfo(*it, params);
+        if (m_musicDatabase.HasArtistInfo(params.GetArtistId())) // should this be here?
+            continue;
+
+        CArtist artist;
+        m_musicDatabase.GetArtistInfo(params.GetArtistId(), artist);
+        m_musicDatabase.GetArtistPath(params.GetArtistId(), artist.strPath);
+
         if (m_handle)
         {
-          m_handle->SetText(it->strArtist);
-          m_handle->SetPercentage(iCurrentItem++/(float)m_artistsToScan.size()*100);
+          float percentage = (float) (std::distance(m_pathsToScan.begin(), it) / m_pathsToScan.size()) * 100;
+          m_handle->SetText(artist.strArtist);
+          m_handle->SetPercentage(percentage);
         }
-
-        DownloadArtistInfo(it->genre[0],it->strArtist,bCanceled); // genre field holds path - see fetchartistinfo()
+        
+        // find album info
+        ADDON::ScraperPtr scraper;
+        if (!m_musicDatabase.GetScraperForPath(*it, scraper, ADDON::ADDON_SCRAPER_ARTISTS) || !scraper)
+          continue;
+
+        CMusicArtistInfo artistInfo;
+        DownloadArtistInfo(artist, scraper, artistInfo); // genre field holds path - see fetchartistinfo()
+        m_musicDatabase.SetArtistInfo(params.GetArtistId(), artistInfo.GetArtist());
+        map<string, string> artwork = GetArtistArtwork(params.GetArtistId(), &artist);
+        m_musicDatabase.SetArtForItem(params.GetArtistId(), "artist", artwork);
 
         if (m_bStop || bCanceled)
           break;
@@ -202,7 +235,9 @@ void CMusicInfoScanner::Process()
   {
     CLog::Log(LOGERROR, "MusicInfoScanner: Exception while scanning.");
   }
-
+  m_musicDatabase.Close();
+  CLog::Log(LOGDEBUG, "%s - Finished scan", __FUNCTION__);
+  
   m_bRunning = false;
   ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::AudioLibrary, "xbmc", "OnScanFinished");
   
@@ -221,8 +256,6 @@ void CMusicInfoScanner::Start(const CStdString& strDirectory, int flags)
   m_fileCountReader.StopThread();
   StopThread();
   m_pathsToScan.clear();
-  m_albumsScanned.clear();
-  m_artistsScanned.clear();
   m_flags = flags;
 
   if (strDirectory.IsEmpty())
@@ -234,7 +267,7 @@ void CMusicInfoScanner::Start(const CStdString& strDirectory, int flags)
   }
   else
     m_pathsToScan.insert(strDirectory);
-  m_pathsToCount = m_pathsToScan;
+
   m_scanType = 0;
   Create();
   m_bRunning = true;
@@ -243,10 +276,9 @@ void CMusicInfoScanner::Start(const CStdString& strDirectory, int flags)
 void CMusicInfoScanner::FetchAlbumInfo(const CStdString& strDirectory,
                                        bool refresh)
 {
-  m_albumsToScan.clear();
   m_fileCountReader.StopThread();
   StopThread();
-  m_albumsScanned.clear();
+  m_pathsToScan.clear();
 
   CFileItemList items;
   if (strDirectory.IsEmpty())
@@ -272,16 +304,10 @@ void CMusicInfoScanner::FetchAlbumInfo(const CStdString& strDirectory,
     if (CMusicDatabaseDirectory::IsAllItem(items[i]->GetPath()) || items[i]->IsParentFolder())
       continue;
 
-    CAlbum album;
-    album.strAlbum = items[i]->GetMusicInfoTag()->GetAlbum();
-    album.artist = items[i]->GetMusicInfoTag()->GetArtist();
-    album.genre.push_back(items[i]->GetPath()); // a bit hacky use of field
-    m_albumsToScan.insert(album);
+    m_pathsToScan.insert(items[i]->GetPath());
     if (refresh)
     {
-      int id = m_musicDatabase.GetAlbumByName(album.strAlbum, album.artist);
-      if (id > -1)
-        m_musicDatabase.DeleteAlbumInfo(id);
+      m_musicDatabase.DeleteAlbumInfo(items[i]->GetMusicInfoTag()->GetDatabaseId());
     }
   }
   m_musicDatabase.Close();
@@ -294,10 +320,9 @@ void CMusicInfoScanner::FetchAlbumInfo(const CStdString& strDirectory,
 void CMusicInfoScanner::FetchArtistInfo(const CStdString& strDirectory,
                                         bool refresh)
 {
-  m_artistsToScan.clear();
   m_fileCountReader.StopThread();
   StopThread();
-  m_artistsScanned.clear();
+  m_pathsToScan.clear();
   CFileItemList items;
 
   if (strDirectory.IsEmpty())
@@ -323,15 +348,10 @@ void CMusicInfoScanner::FetchArtistInfo(const CStdString& strDirectory,
     if (CMusicDatabaseDirectory::IsAllItem(items[i]->GetPath()) || items[i]->IsParentFolder())
       continue;
 
-    CArtist artist;
-    artist.strArtist = StringUtils::Join(items[i]->GetMusicInfoTag()->GetArtist(), g_advancedSettings.m_musicItemSeparator);
-    artist.genre.push_back(items[i]->GetPath()); // a bit hacky use of field
-    m_artistsToScan.insert(artist);
+    m_pathsToScan.insert(items[i]->GetPath());
     if (refresh)
     {
-      int id = m_musicDatabase.GetArtistByName(artist.strArtist);
-      if (id > -1)
-        m_musicDatabase.DeleteArtistInfo(id);
+      m_musicDatabase.DeleteArtistInfo(items[i]->GetMusicInfoTag()->GetDatabaseId());
     }
   }
   m_musicDatabase.Close();
@@ -375,19 +395,8 @@ bool CMusicInfoScanner::DoScan(const CStdString& strDirectory)
   if (m_handle)
     m_handle->SetText(Prettify(strDirectory));
 
-  /*
-   * remove this path from the list we're processing. This must be done prior to
-   * the check for file or folder exclusion to prevent an infinite while loop
-   * in Process().
-   */
-  set<CStdString>::iterator it = m_pathsToScan.find(strDirectory);
-  if (it != m_pathsToScan.end())
-    m_pathsToScan.erase(it);
-
   // Discard all excluded files defined by m_musicExcludeRegExps
-
   CStdStringArray regexps = g_advancedSettings.m_audioExcludeFromScanRegExps;
-
   if (CUtil::ExcludeFileOrFolder(strDirectory, regexps))
     return true;
 
@@ -416,7 +425,7 @@ bool CMusicInfoScanner::DoScan(const CStdString& strDirectory)
     items.Sort(SORT_METHOD_LABEL, SortOrderAscending);
 
     // and then scan in the new information
-    if (RetrieveMusicInfo(items, strDirectory) > 0)
+    if (RetrieveMusicInfo(strDirectory, items) > 0)
     {
       if (m_handle)
         OnDirectoryScanned(strDirectory);
@@ -464,209 +473,364 @@ INFO_RET CMusicInfoScanner::ScanTags(const CFileItemList& items, CFileItemList&
 {
   CStdStringArray regexps = g_advancedSettings.m_audioExcludeFromScanRegExps;
 
-  // for every file found, but skip folder
   for (int i = 0; i < items.Size(); ++i)
   {
-    CFileItemPtr pItem = items[i];
-
     if (m_bStop)
       return INFO_CANCELLED;
 
-    // Discard all excluded files defined by m_musicExcludeRegExps
+    CFileItemPtr pItem = items[i];
+
     if (CUtil::ExcludeFileOrFolder(pItem->GetPath(), regexps))
       continue;
 
-    // dont try reading id3tags for folders, playlists or shoutcast streams
-    if (!pItem->m_bIsFolder && !pItem->IsPlayList() && !pItem->IsPicture() && !pItem->IsLyrics() )
-    {
-      m_currentItem++;
+    if (pItem->m_bIsFolder || pItem->IsPlayList() || pItem->IsPicture() || pItem->IsLyrics())
+      continue;
 
-      CMusicInfoTag& tag = *pItem->GetMusicInfoTag();
-      if (!tag.Loaded())
-      {
-        // read the tag from a file
-        auto_ptr<IMusicInfoTagLoader> pLoader (CMusicInfoTagLoaderFactory::CreateLoader(pItem->GetPath()));
-        if (NULL != pLoader.get())
-          pLoader->Load(pItem->GetPath(), tag);
-      }
+    m_currentItem++;
 
-      // if we have the itemcount, update our
-      // dialog with the progress we made
-      if (m_handle && m_itemCount>0)
-        m_handle->SetPercentage(m_currentItem/(float)m_itemCount*100);
+    CMusicInfoTag& tag = *pItem->GetMusicInfoTag();
+    if (!tag.Loaded())
+    {
+      auto_ptr<IMusicInfoTagLoader> pLoader (CMusicInfoTagLoaderFactory::CreateLoader(pItem->GetPath()));
+      if (NULL != pLoader.get())
+        pLoader->Load(pItem->GetPath(), tag);
+    }
 
-      if (tag.Loaded())
-        scannedItems.Add(pItem);
-      else
-        CLog::Log(LOGDEBUG, "%s - No tag found for: %s", __FUNCTION__, pItem->GetPath().c_str());
+    if (m_handle && m_itemCount>0)
+      m_handle->SetPercentage(m_currentItem/(float)m_itemCount*100);
+
+    if (!tag.Loaded())
+    {
+      CLog::Log(LOGDEBUG, "%s - No tag found for: %s", __FUNCTION__, pItem->GetPath().c_str());
+      continue;
     }
+    scannedItems.Add(pItem);
   }
   return INFO_ADDED;
 }
 
-int CMusicInfoScanner::RetrieveMusicInfo(CFileItemList& items, const CStdString& strDirectory)
+void CMusicInfoScanner::FileItemsToAlbums(CFileItemList& items, VECALBUMS& albums, MAPSONGS* songsMap /* = NULL */)
 {
-  MAPSONGS songsMap;
-
-  // get all information for all files in current directory from database, and remove them
-  if (m_musicDatabase.RemoveSongsFromPath(strDirectory, songsMap))
-    m_needsCleanup = true;
-
-  CFileItemList scannedItems;
-  if (ScanTags(items, scannedItems) == INFO_CANCELLED)
-    return 0;
-
-  VECSONGS songsToAdd;
-  for (int i = 0; i < scannedItems.Size(); ++i)
+  for (int i = 0; i < items.Size(); ++i)
   {
-    CFileItemPtr pItem = scannedItems[i];
+    CFileItemPtr pItem = items[i];
     CMusicInfoTag& tag = *pItem->GetMusicInfoTag();
-    CSong song(tag);
+    CSong song(*pItem);
 
-    // ensure our song has a valid filename or else it will assert in AddSong()
-    if (song.strFileName.IsEmpty())
+    // keep the db-only fields intact on rescan...
+    if (songsMap != NULL)
     {
-      // copy filename from path in case UPnP or other tag loaders didn't specify one (FIXME?)
-      song.strFileName = pItem->GetPath();
-
-      // if we still don't have a valid filename, skip the song
-      if (song.strFileName.IsEmpty())
+      MAPSONGS::iterator it = songsMap->find(pItem->GetPath());
+      if (it != songsMap->end())
       {
-        // this shouldn't ideally happen!
-        CLog::Log(LOGERROR, "Skipping song since it doesn't seem to have a filename");
-        continue;
+        song.iTimesPlayed = it->second.iTimesPlayed;
+        song.lastPlayed = it->second.lastPlayed;
+        song.iKaraokeNumber = it->second.iKaraokeNumber;
+        if (song.rating == '0')    song.rating = it->second.rating;
+        if (song.strThumb.empty()) song.strThumb = it->second.strThumb;
       }
     }
 
-    song.iStartOffset = pItem->m_lStartOffset;
-    song.iEndOffset = pItem->m_lEndOffset;
-    song.strThumb = pItem->GetUserMusicThumb(true);
+    if (!tag.GetMusicBrainzArtistID().empty())
+    {
+      for (vector<string>::const_iterator it = tag.GetMusicBrainzArtistID().begin(); it != tag.GetMusicBrainzArtistID().end(); ++it)
+      {
+        CStdString strJoinPhrase = (it == --tag.GetMusicBrainzArtistID().end() ? "" : g_advancedSettings.m_musicItemSeparator);
+        CArtistCredit mbartist(song.artist[0], *it, strJoinPhrase);
+        song.artistCredits.push_back(mbartist);
+      }
+      song.artist = tag.GetArtist();
+    }
+    else
+    {
+      for (vector<string>::const_iterator it = tag.GetArtist().begin(); it != tag.GetArtist().end(); ++it)
+      {
+        CStdString strJoinPhrase = (it == --tag.GetArtist().end() ? "" : g_advancedSettings.m_musicItemSeparator);
+        CArtistCredit nonmbartist(*it, strJoinPhrase);
+        song.artistCredits.push_back(nonmbartist);
+      }
+      song.artist = tag.GetArtist();
+    }
 
-    // grab info from the song
-    MAPSONGS::iterator it = songsMap.find(pItem->GetPath());
-    if (it != songsMap.end())
+    bool found = false;
+    for (VECALBUMS::iterator it = albums.begin(); it != albums.end(); ++it)
+    {
+      if (it->strAlbum == tag.GetAlbum() && it->strMusicBrainzAlbumID == tag.GetMusicBrainzAlbumID())
+      {
+        it->songs.push_back(song);
+        found = true;
+      }
+    }
+    if (!found)
     {
-      // keep the db-only fields intact on rescan...
-      song.iTimesPlayed = it->second.iTimesPlayed;
-      song.lastPlayed = it->second.lastPlayed;
-      song.iKaraokeNumber = it->second.iKaraokeNumber;
-
-      if (song.rating == '0')
-        song.rating = it->second.rating;
-      if (song.strThumb.empty())
-        song.strThumb = it->second.strThumb;
+      CAlbum album(*pItem.get());
+      if (!tag.GetMusicBrainzAlbumArtistID().empty())
+      {
+        for (vector<string>::const_iterator it = tag.GetMusicBrainzAlbumArtistID().begin(); it != tag.GetMusicBrainzAlbumArtistID().end(); ++it)
+        {
+          // Picard always stored the display artist string in the first artist slot, no need to split it
+          CStdString strJoinPhrase = (it == --tag.GetMusicBrainzAlbumArtistID().end() ? "" : g_advancedSettings.m_musicItemSeparator);
+          CArtistCredit mbartist(tag.GetAlbumArtist()[0], *it, strJoinPhrase);
+          album.artistCredits.push_back(mbartist);
+        }
+        album.artist = tag.GetAlbumArtist();
+      }
+      else
+      {
+        for (vector<string>::const_iterator it = tag.GetAlbumArtist().begin(); it != tag.GetAlbumArtist().end(); ++it)
+        {
+          CStdString strJoinPhrase = (it == --tag.GetAlbumArtist().end() ? "" : g_advancedSettings.m_musicItemSeparator);
+          CArtistCredit nonmbartist(*it, strJoinPhrase);
+          album.artistCredits.push_back(nonmbartist);
+        }
+        album.artist = tag.GetAlbumArtist();
+      }
+      album.songs.push_back(song);
+      albums.push_back(album);
     }
-    songsToAdd.push_back(song);
   }
+}
+
+int CMusicInfoScanner::RetrieveMusicInfo(const CStdString& strDirectory, CFileItemList& items)
+{
+  MAPSONGS songsMap;
+
+  // get all information for all files in current directory from database, and remove them
+  if (m_musicDatabase.RemoveSongsFromPath(strDirectory, songsMap))
+    m_needsCleanup = true;
+
+  CFileItemList scannedItems;
+  if (ScanTags(items, scannedItems) == INFO_CANCELLED)
+    return 0;
 
   VECALBUMS albums;
-  CategoriseAlbums(songsToAdd, albums);
+  FileItemsToAlbums(scannedItems, albums, &songsMap);
+  if (!(m_flags & SCAN_ONLINE))
+    FixupAlbums(albums);
   FindArtForAlbums(albums, items.GetPath());
 
-  // finally, add these to the database
-  m_musicDatabase.BeginTransaction();
   int numAdded = 0;
-  set<int> albumsToScan;
-  set<int> artistsToScan;
-  for (VECALBUMS::iterator i = albums.begin(); i != albums.end(); ++i)
+  ADDON::AddonPtr addon;
+  ADDON::ScraperPtr albumScraper;
+  ADDON::ScraperPtr artistScraper;
+  if(ADDON::CAddonMgr::Get().GetDefault(ADDON::ADDON_SCRAPER_ALBUMS, addon))
+    albumScraper = boost::dynamic_pointer_cast<ADDON::CScraper>(addon);
+
+  if(ADDON::CAddonMgr::Get().GetDefault(ADDON::ADDON_SCRAPER_ARTISTS, addon))
+    artistScraper = boost::dynamic_pointer_cast<ADDON::CScraper>(addon);
+
+  // Add each album
+  for (VECALBUMS::iterator album = albums.begin(); album != albums.end(); ++album)
   {
-    vector<int> songIDs;
-    int idAlbum = m_musicDatabase.AddAlbum(*i, songIDs);
-    numAdded += i->songs.size();
     if (m_bStop)
-    {
-      m_musicDatabase.RollbackTransaction();
-      return numAdded;
-    }
+      break;
 
-    // Build the artist & album sets
-    albumsToScan.insert(idAlbum);
-    for (vector<int>::iterator j = songIDs.begin(); j != songIDs.end(); ++j)
-    {
-      vector<int> songArtists;
-      m_musicDatabase.GetArtistsBySong(*j, false, songArtists);
-      artistsToScan.insert(songArtists.begin(), songArtists.end());
-    }
-    std::vector<int> albumArtists;
-    m_musicDatabase.GetArtistsByAlbum(idAlbum, false, albumArtists);
-    artistsToScan.insert(albumArtists.begin(), albumArtists.end());
-  }
-  m_musicDatabase.CommitTransaction();
+    album->strPath = strDirectory;
+    m_musicDatabase.BeginTransaction();
 
-  // Download info & artwork
-  bool bCanceled;
-  for (set<int>::iterator it = artistsToScan.begin(); it != artistsToScan.end(); ++it)
-  {
-    bCanceled = false;
-    if (find(m_artistsScanned.begin(),m_artistsScanned.end(), *it) == m_artistsScanned.end())
+    // Check if the album has already been downloaded or failed
+    map<CAlbum, CAlbum>::iterator cachedAlbum = m_albumCache.find(*album);
+    if (cachedAlbum == m_albumCache.end())
     {
-      CStdString strArtist = m_musicDatabase.GetArtistById(*it);
-      m_artistsScanned.push_back(*it);
-      if (!m_bStop && (m_flags & SCAN_ONLINE))
-      {
-        CStdString strPath;
-        strPath.Format("musicdb://artists/%u/", *it);
+      // No - download the information
+      CMusicAlbumInfo albumInfo;
+      INFO_RET albumDownloadStatus = INFO_NOT_FOUND;
+      if (m_flags & SCAN_ONLINE)
+        albumDownloadStatus = DownloadAlbumInfo(*album, albumScraper, albumInfo);
 
-        if (!DownloadArtistInfo(strPath, strArtist, bCanceled)) // assume we want to retry
-          m_artistsScanned.pop_back();
+      if (albumDownloadStatus == INFO_ADDED || albumDownloadStatus == INFO_HAVE_ALREADY)
+      {
+        CAlbum &downloadedAlbum = albumInfo.GetAlbum();
+        downloadedAlbum.idAlbum = m_musicDatabase.AddAlbum(downloadedAlbum.strAlbum,
+                                                           downloadedAlbum.strMusicBrainzAlbumID,
+                                                           downloadedAlbum.GetArtistString(),
+                                                           downloadedAlbum.GetGenreString(),
+                                                           downloadedAlbum.iYear,
+                                                           downloadedAlbum.bCompilation);
+        m_musicDatabase.SetAlbumInfo(downloadedAlbum.idAlbum,
+                                     downloadedAlbum,
+                                     downloadedAlbum.songs);
+        m_musicDatabase.SetArtForItem(downloadedAlbum.idAlbum,
+                                      "album", album->art);
+        GetAlbumArtwork(downloadedAlbum.idAlbum, downloadedAlbum);
+        m_albumCache.insert(make_pair(*album, albumInfo.GetAlbum()));
       }
-      else
+      else if (albumDownloadStatus == INFO_CANCELLED)
+        break;
+      else // Cache the lookup failure so we don't retry
       {
-        map<string, string> artwork = GetArtistArtwork(*it);
-        m_musicDatabase.SetArtForItem(*it, "artist", artwork);
+        album->idAlbum = m_musicDatabase.AddAlbum(album->strAlbum,
+                                                  album->strMusicBrainzAlbumID,
+                                                  album->GetArtistString(),
+                                                  album->GetGenreString(),
+                                                  album->iYear,
+                                                  album->bCompilation);
+        m_albumCache.insert(make_pair(*album, *album));
       }
+
+      // Update the cache pointer with our newly created info
+      cachedAlbum = m_albumCache.find(*album);
     }
-  }
 
-  if (m_flags & SCAN_ONLINE)
-  {
-    for (set<int>::iterator it = albumsToScan.begin(); it != albumsToScan.end(); ++it)
+    if (m_bStop)
+      break;
+
+    // Add the album artists
+    for (VECARTISTCREDITS::iterator artistCredit = cachedAlbum->second.artistCredits.begin(); artistCredit != cachedAlbum->second.artistCredits.end(); ++artistCredit)
     {
       if (m_bStop)
-        return songsToAdd.size();
+        break;
+
+      // Check if the artist has already been downloaded or failed
+      map<CArtistCredit, CArtist>::iterator cachedArtist = m_artistCache.find(*artistCredit);
+      if (cachedArtist == m_artistCache.end())
+      {
+        CArtist artistTmp;
+        artistTmp.strArtist = artistCredit->GetArtist();
+        artistTmp.strMusicBrainzArtistID = artistCredit->GetMusicBrainzArtistID();
+        URIUtils::GetParentPath(album->strPath, artistTmp.strPath);
+
+        // No - download the information
+        CMusicArtistInfo artistInfo;
+        INFO_RET artistDownloadStatus = INFO_NOT_FOUND;
+        if (m_flags & SCAN_ONLINE)
+          artistDownloadStatus = DownloadArtistInfo(artistTmp, artistScraper, artistInfo);
+
+        if (artistDownloadStatus == INFO_ADDED || artistDownloadStatus == INFO_HAVE_ALREADY)
+        {
+          CArtist &downloadedArtist = artistInfo.GetArtist();
+          downloadedArtist.idArtist = m_musicDatabase.AddArtist(downloadedArtist.strArtist,
+                                                                downloadedArtist.strMusicBrainzArtistID);
+          m_musicDatabase.SetArtistInfo(downloadedArtist.idArtist,
+                                        downloadedArtist);
+
+          map<string, string> artwork = GetArtistArtwork(downloadedArtist.idArtist,
+                                                         &downloadedArtist);
+          // check thumb stuff
+          m_musicDatabase.SetArtForItem(downloadedArtist.idArtist, "artist", artwork);
+          m_artistCache.insert(make_pair(*artistCredit, downloadedArtist));
+        }
+        else if (artistDownloadStatus == INFO_CANCELLED)
+          break;
+        else
+        {
+          // Cache the lookup failure so we don't retry
+          artistTmp.idArtist = m_musicDatabase.AddArtist(artistCredit->GetArtist(), artistCredit->GetMusicBrainzArtistID());
+          m_artistCache.insert(make_pair(*artistCredit, artistTmp));
+        }
+        
+        // Update the cache pointer with our newly created info
+        cachedArtist = m_artistCache.find(*artistCredit);
+      }
 
-      CStdString strPath;
-      strPath.Format("musicdb://albums/%u/",*it);
+      m_musicDatabase.AddAlbumArtist(cachedArtist->second.idArtist,
+                                     cachedAlbum->second.idAlbum,
+                                     artistCredit->GetJoinPhrase(),
+                                     artistCredit == album->artistCredits.begin() ? false : true,
+                                     std::distance(cachedAlbum->second.artistCredits.begin(), artistCredit));
+    }
 
-      CAlbum album;
-      m_musicDatabase.GetAlbumInfo(*it, album, NULL);
-      bCanceled = false;
-      if (find(m_albumsScanned.begin(), m_albumsScanned.end(), *it) == m_albumsScanned.end())
+    if (m_bStop)
+      break;
+
+    for (VECSONGS::iterator song = album->songs.begin(); song != album->songs.end(); ++song)
+    {
+      song->idAlbum = cachedAlbum->second.idAlbum;
+      song->idSong = m_musicDatabase.AddSong(*song);
+      for (VECARTISTCREDITS::iterator artistCredit = song->artistCredits.begin(); artistCredit != song->artistCredits.end(); ++artistCredit)
       {
-        CMusicAlbumInfo albumInfo;
-        if (DownloadAlbumInfo(strPath, StringUtils::Join(album.artist, g_advancedSettings.m_musicItemSeparator), album.strAlbum, bCanceled, albumInfo))
-          m_albumsScanned.push_back(*it);
+        if (m_bStop)
+          break;
+
+        // Check if the artist has already been downloaded or failed
+        map<CArtistCredit, CArtist>::iterator cachedArtist = m_artistCache.find(*artistCredit);
+        if (cachedArtist == m_artistCache.end())
+        {
+          CArtist artistTmp;
+          artistTmp.strArtist = artistCredit->GetArtist();
+          artistTmp.strMusicBrainzArtistID = artistCredit->GetMusicBrainzArtistID();
+          URIUtils::GetParentPath(album->strPath, artistTmp.strPath); // FIXME
+
+          // No - download the information
+          CMusicArtistInfo artistInfo;
+          INFO_RET artistDownloadStatus = INFO_NOT_FOUND;
+          if (m_flags & SCAN_ONLINE)
+            artistDownloadStatus = DownloadArtistInfo(artistTmp, artistScraper, artistInfo);
+
+          if (artistDownloadStatus == INFO_ADDED || artistDownloadStatus == INFO_HAVE_ALREADY)
+          {
+            CArtist &downloadedArtist = artistInfo.GetArtist();
+            downloadedArtist.idArtist = m_musicDatabase.AddArtist(downloadedArtist.strArtist,
+                                                                  downloadedArtist.strMusicBrainzArtistID);
+            m_musicDatabase.SetArtistInfo(downloadedArtist.idArtist,
+                                          downloadedArtist);
+            // check thumb stuff
+            map<string, string> artwork = GetArtistArtwork(downloadedArtist.idArtist,
+                                                           &downloadedArtist);
+            m_musicDatabase.SetArtForItem(downloadedArtist.idArtist, "artist", artwork);
+            m_artistCache.insert(make_pair(*artistCredit, downloadedArtist));
+          }
+          else if (artistDownloadStatus == INFO_CANCELLED)
+            break;
+          else
+          {
+            // Cache the lookup failure so we don't retry
+            artistTmp.idArtist = m_musicDatabase.AddArtist(artistCredit->GetArtist(), artistCredit->GetMusicBrainzArtistID());
+            m_artistCache.insert(make_pair(*artistCredit, artistTmp));
+          }
+
+          // Update the cache pointer with our newly created info
+          cachedArtist = m_artistCache.find(*artistCredit);
+        }
+
+        m_musicDatabase.AddSongArtist(cachedArtist->second.idArtist,
+                                      song->idSong,
+                                      g_advancedSettings.m_musicItemSeparator, // we don't have song artist breakdowns from scrapers, yet
+                                      artistCredit == song->artistCredits.begin() ? false : true,
+                                      std::distance(song->artistCredits.begin(), artistCredit));
       }
     }
+
+    if (m_bStop)
+      break;
+
+    // Commit the album to the DB
+    m_musicDatabase.CommitTransaction();
+    numAdded += album->songs.size();
   }
+
+  if (m_bStop)
+    m_musicDatabase.RollbackTransaction();
+
   if (m_handle)
     m_handle->SetTitle(g_localizeStrings.Get(505));
 
-  return songsToAdd.size();
+  return numAdded;
 }
 
-static bool SortSongsByTrack(CSong *song, CSong *song2)
+static bool SortSongsByTrack(const CSong& song, const CSong& song2)
 {
-  return song->iTrack < song2->iTrack;
+  return song.iTrack < song2.iTrack;
 }
 
-void CMusicInfoScanner::CategoriseAlbums(VECSONGS &songsToCheck, VECALBUMS &albums)
+void CMusicInfoScanner::FixupAlbums(VECALBUMS &albums)
 {
-  /* Step 1: categorise on the album name */
-  map<string, vector<CSong *> > albumNames;
-  for (VECSONGS::iterator i = songsToCheck.begin(); i != songsToCheck.end(); ++i)
-    albumNames[i->strAlbum].push_back(&(*i));
-
   /*
    Step 2: Split into unique albums based on album name and album artist
    In the case where the album artist is unknown, we use the primary artist
    (i.e. first artist from each song).
    */
-  albums.clear();
-  for (map<string, vector<CSong *> >::iterator i = albumNames.begin(); i != albumNames.end(); ++i)
+  for (VECALBUMS::iterator album = albums.begin(); album != albums.end(); ++album)
   {
+    /*
+     * If we have a valid MusicBrainz tag for the album, assume everything
+     * is okay with our tags, as Picard should set everything up correctly.
+     */
+    if (!album->strMusicBrainzAlbumID.IsEmpty())
+      continue;
+
+    VECSONGS &songs = album->songs;
     // sort the songs by tracknumber to identify duplicate track numbers
-    vector<CSong *> &songs = i->second;
     sort(songs.begin(), songs.end(), SortSongsByTrack);
 
     // map the songs to their primary artists
@@ -675,14 +839,13 @@ void CMusicInfoScanner::CategoriseAlbums(VECSONGS &songsToCheck, VECALBUMS &albu
     bool isCompilation = true;
 
     map<string, vector<CSong *> > artists;
-    for (vector<CSong *>::iterator j = songs.begin(); j != songs.end(); ++j)
+    for (VECSONGS::iterator song = songs.begin(); song != songs.end(); ++song)
     {
-      CSong *song = *j;
       // test for song overlap
-      if (j != songs.begin() && song->iTrack == (*(j-1))->iTrack)
+      if (song != songs.begin() && song->iTrack == (song - 1)->iTrack)
         tracksOverlap = true;
 
-      if (! song->bCompilation)
+      if (!song->bCompilation)
         isCompilation = false;
 
       // get primary artist
@@ -696,7 +859,7 @@ void CMusicInfoScanner::CategoriseAlbums(VECSONGS &songsToCheck, VECALBUMS &albu
         primary = song->artist[0];
 
       // add to the artist map
-      artists[primary].push_back(song);
+      artists[primary].push_back(&(*song));
     }
 
     /*
@@ -707,7 +870,7 @@ void CMusicInfoScanner::CategoriseAlbums(VECSONGS &songsToCheck, VECALBUMS &albu
      3a. a unique primary artist is specified as "various" or "various artists" OR
      3b. we have at least two primary artists and no album artist specified.
      */
-    bool compilation = !i->first.empty() && (isCompilation || !tracksOverlap); // 1+2b+2a
+    bool compilation = !album->strAlbum.empty() && (isCompilation || !tracksOverlap); // 1+2b+2a
     if (artists.size() == 1)
     {
       string artist = artists.begin()->first; StringUtils::ToLower(artist);
@@ -720,13 +883,15 @@ void CMusicInfoScanner::CategoriseAlbums(VECSONGS &songsToCheck, VECALBUMS &albu
 
     if (compilation)
     {
-      CLog::Log(LOGDEBUG, "Album '%s' is a compilation as there's no overlapping tracks and %s", i->first.c_str(), hasAlbumArtist ? "the album artist is 'Various'" : "there is more than one unique artist");
+      CLog::Log(LOGDEBUG, "Album '%s' is a compilation as there's no overlapping tracks and %s", album->strAlbum.c_str(), hasAlbumArtist ? "the album artist is 'Various'" : "there is more than one unique artist");
       artists.clear();
       std::string various = g_localizeStrings.Get(340); // Various Artists
       vector<string> va; va.push_back(various);
-      for (vector<CSong *>::iterator j = songs.begin(); j != songs.end(); ++j)
-        (*j)->albumArtist = va;
-      artists.insert(make_pair(various, songs));
+      for (VECSONGS::iterator song = songs.begin(); song != songs.end(); ++song)
+      {
+        song->albumArtist = va;
+        artists[various].push_back(&(*song));
+      }
     }
 
     /*
@@ -754,24 +919,25 @@ void CMusicInfoScanner::CategoriseAlbums(VECSONGS &songsToCheck, VECALBUMS &albu
        Step 4: Assign the album artist for each song that doesn't have it set
        and add to the album vector
        */
-      CAlbum album;
-      album.strAlbum = i->first;
-      album.artist = common;
-      album.bCompilation = compilation;
+      album->artistCredits.clear();
+      for (vector<string>::iterator it = common.begin(); it != common.end(); ++it)
+      {
+        CStdString strJoinPhrase = (it == --common.end() ? "" : g_advancedSettings.m_musicItemSeparator);
+        CArtistCredit artistCredit(*it, strJoinPhrase);
+        album->artistCredits.push_back(artistCredit);
+      }
+      album->bCompilation = compilation;
       for (vector<CSong *>::iterator k = artistSongs.begin(); k != artistSongs.end(); ++k)
       {
         if ((*k)->albumArtist.empty())
           (*k)->albumArtist = common;
-        album.songs.push_back(*(*k));
         // TODO: in future we may wish to union up the genres, for now we assume they're the same
-        if (album.genre.empty())
-          album.genre = (*k)->genre;
+        if (album->genre.empty())
+          album->genre = (*k)->genre;
         //       in addition, we may want to use year as discriminating for albums
-        if (album.iYear == 0)
-          album.iYear = (*k)->iYear;
+        if (album->iYear == 0)
+          album->iYear = (*k)->iYear;
       }
-
-      albums.push_back(album);
     }
   }
 }
@@ -877,65 +1043,56 @@ int CMusicInfoScanner::GetPathHash(const CFileItemList &items, CStdString &hash)
 
 #define THRESHOLD .95f
 
-bool CMusicInfoScanner::DownloadAlbumInfo(const CStdString& strPath, const CStdString& strArtist, const CStdString& strAlbum, bool& bCanceled, CMusicAlbumInfo& albumInfo, CGUIDialogProgress* pDialog)
+INFO_RET CMusicInfoScanner::DownloadAlbumInfo(const CAlbum& album, ADDON::ScraperPtr& info, CMusicAlbumInfo& albumInfo, CGUIDialogProgress* pDialog)
 {
-  CAlbum album;
-  VECSONGS songs;
-  XFILE::MUSICDATABASEDIRECTORY::CQueryParams params;
-  XFILE::MUSICDATABASEDIRECTORY::CDirectoryNode::GetDatabaseInfo(strPath, params);
-  bCanceled = false;
-  m_musicDatabase.Open();
-  if (m_musicDatabase.HasAlbumInfo(params.GetAlbumId()) && m_musicDatabase.GetAlbumInfo(params.GetAlbumId(),album,&songs))
-    return true;
-
-  // find album info
-  ADDON::ScraperPtr info;
-  if (!m_musicDatabase.GetScraperForPath(strPath, info, ADDON::ADDON_SCRAPER_ALBUMS) || !info)
-  {
-    m_musicDatabase.Close();
-    return false;
-  }
-
   if (m_handle)
   {
     m_handle->SetTitle(StringUtils::Format(g_localizeStrings.Get(20321), info->Name().c_str()));
-    m_handle->SetText(strArtist+" - "+strAlbum);
+    m_handle->SetText(StringUtils::Join(album.artist, g_advancedSettings.m_musicItemSeparator) + " - " + album.strAlbum);
   }
 
   // clear our scraper cache
   info->ClearCache();
 
   CMusicInfoScraper scraper(info);
+  bool bMusicBrainz = false;
+  if (!album.strMusicBrainzAlbumID.empty())
+  {
+    CScraperUrl musicBrainzURL;
+    if (ResolveMusicBrainz(album.strMusicBrainzAlbumID, info, scraper, musicBrainzURL))
+    {
+      CMusicAlbumInfo albumNfo("nfo", musicBrainzURL);
+      scraper.GetAlbums().clear();
+      scraper.GetAlbums().push_back(albumNfo);
+      bMusicBrainz = true;
+    }
+  }
 
   // handle nfo files
-  CStdString strAlbumPath, strNfo;
-  m_musicDatabase.GetAlbumPath(params.GetAlbumId(),strAlbumPath);
-  URIUtils::AddFileToFolder(strAlbumPath,"album.nfo",strNfo);
-  CNfoFile::NFOResult result=CNfoFile::NO_NFO;
+  CStdString strNfo;
+  URIUtils::AddFileToFolder(album.strPath, "album.nfo", strNfo);
+  CNfoFile::NFOResult result = CNfoFile::NO_NFO;
   CNfoFile nfoReader;
   if (XFILE::CFile::Exists(strNfo))
   {
     CLog::Log(LOGDEBUG,"Found matching nfo file: %s", strNfo.c_str());
-    result = nfoReader.Create(strNfo, info, -1, strPath);
+    result = nfoReader.Create(strNfo, info, -1, album.strPath);
     if (result == CNfoFile::FULL_NFO)
     {
       CLog::Log(LOGDEBUG, "%s Got details from nfo", __FUNCTION__);
-      CAlbum album;
-      nfoReader.GetDetails(album);
-      m_musicDatabase.SetAlbumInfo(params.GetAlbumId(), album, album.songs);
-      GetAlbumArtwork(params.GetAlbumId(), album);
-      m_musicDatabase.Close();
-      return true;
+      nfoReader.GetDetails(albumInfo.GetAlbum());
+      return INFO_ADDED;
     }
     else if (result == CNfoFile::URL_NFO || result == CNfoFile::COMBINED_NFO)
     {
       CScraperUrl scrUrl(nfoReader.ScraperUrl());
-      CMusicAlbumInfo album("nfo",scrUrl);
+      CMusicAlbumInfo albumNfo("nfo",scrUrl);
       info = nfoReader.GetScraperInfo();
       CLog::Log(LOGDEBUG,"-- nfo-scraper: %s",info->Name().c_str());
       CLog::Log(LOGDEBUG,"-- nfo url: %s", scrUrl.m_url[0].m_url.c_str());
       scraper.SetScraperInfo(info);
-      scraper.GetAlbums().push_back(album);
+      scraper.GetAlbums().clear();
+      scraper.GetAlbums().push_back(albumNfo);
     }
     else
       CLog::Log(LOGERROR,"Unable to find an url in nfo file: %s", strNfo.c_str());
@@ -944,19 +1101,19 @@ bool CMusicInfoScanner::DownloadAlbumInfo(const CStdString& strPath, const CStdS
   if (!scraper.CheckValidOrFallback(g_guiSettings.GetString("musiclibrary.albumsscraper")))
   { // the current scraper is invalid, as is the default - bail
     CLog::Log(LOGERROR, "%s - current and default scrapers are invalid.  Pick another one", __FUNCTION__);
-    return false;
+    return INFO_ERROR;
   }
 
   if (!scraper.GetAlbumCount())
   {
-    scraper.FindAlbumInfo(strAlbum, strArtist);
+    scraper.FindAlbumInfo(album.strAlbum, StringUtils::Join(album.artist, g_advancedSettings.m_musicItemSeparator));
 
     while (!scraper.Completed())
     {
       if (m_bStop)
       {
         scraper.Cancel();
-        bCanceled = true;
+        return INFO_CANCELLED;
       }
       Sleep(1);
     }
@@ -964,7 +1121,7 @@ bool CMusicInfoScanner::DownloadAlbumInfo(const CStdString& strPath, const CStdS
 
   CGUIDialogSelect *pDlg = NULL;
   int iSelectedAlbum=0;
-  if (result == CNfoFile::NO_NFO)
+  if (result == CNfoFile::NO_NFO && !bMusicBrainz)
   {
     iSelectedAlbum = -1; // set negative so that we can detect a failure
     if (scraper.Succeeded() && scraper.GetAlbumCount() >= 1)
@@ -987,7 +1144,7 @@ bool CMusicInfoScanner::DownloadAlbumInfo(const CStdString& strPath, const CStdS
           CMusicAlbumInfo& info = scraper.GetAlbum(i);
           double relevance = info.GetRelevance();
           if (relevance < 0)
-            relevance = CUtil::AlbumRelevance(info.GetAlbum().strAlbum, strAlbum, StringUtils::Join(info.GetAlbum().artist, g_advancedSettings.m_musicItemSeparator), strArtist);
+            relevance = CUtil::AlbumRelevance(info.GetAlbum().strAlbum, album.strAlbum, StringUtils::Join(info.GetAlbum().artist, g_advancedSettings.m_musicItemSeparator), StringUtils::Join(album.artist, g_advancedSettings.m_musicItemSeparator));
 
           // if we're doing auto-selection (ie querying all albums at once, then allow 95->100% for perfect matches)
           // otherwise, perfect matches only
@@ -1018,22 +1175,25 @@ bool CMusicInfoScanner::DownloadAlbumInfo(const CStdString& strPath, const CStdS
           if (pDlg->GetSelectedLabel() < 0)
           { // none chosen
             if (!pDlg->IsButtonPressed())
-              return false;
+              return INFO_CANCELLED;
 
             // manual button pressed
             CStdString strNewAlbum = album.strAlbum;
-            if (!CGUIKeyboardFactory::ShowAndGetInput(strNewAlbum, g_localizeStrings.Get(16011), false)) return false;
-            if (strNewAlbum == "") return false;
+            if (!CGUIKeyboardFactory::ShowAndGetInput(strNewAlbum, g_localizeStrings.Get(16011), false)) return INFO_CANCELLED;
+            if (strNewAlbum == "") return INFO_CANCELLED;
 
             CStdString strNewArtist = StringUtils::Join(album.artist, g_advancedSettings.m_musicItemSeparator);
-            if (!CGUIKeyboardFactory::ShowAndGetInput(strNewArtist, g_localizeStrings.Get(16025), false)) return false;
+            if (!CGUIKeyboardFactory::ShowAndGetInput(strNewArtist, g_localizeStrings.Get(16025), false)) return INFO_CANCELLED;
 
             pDialog->SetLine(0, strNewAlbum);
             pDialog->SetLine(1, strNewArtist);
             pDialog->Progress();
 
-            m_musicDatabase.Close();
-            return DownloadAlbumInfo(strPath,strNewArtist,strNewAlbum,bCanceled,albumInfo,pDialog);
+            CAlbum newAlbum = album;
+            newAlbum.strAlbum = strNewAlbum;
+            newAlbum.artist = StringUtils::Split(strNewArtist, g_advancedSettings.m_musicItemSeparator);
+
+            return DownloadAlbumInfo(newAlbum, info, albumInfo, pDialog);
           }
           iSelectedAlbum = pDlg->GetSelectedItem()->m_idepth;
         }
@@ -1043,21 +1203,20 @@ bool CMusicInfoScanner::DownloadAlbumInfo(const CStdString& strPath, const CStdS
         CMusicAlbumInfo& info = scraper.GetAlbum(0);
         double relevance = info.GetRelevance();
         if (relevance < 0)
-          relevance = CUtil::AlbumRelevance(info.GetAlbum().strAlbum, strAlbum, StringUtils::Join(info.GetAlbum().artist, g_advancedSettings.m_musicItemSeparator), strArtist);
+          relevance = CUtil::AlbumRelevance(info.GetAlbum().strAlbum,
+                                            album.strAlbum,
+                                            StringUtils::Join(info.GetAlbum().artist, g_advancedSettings.m_musicItemSeparator),
+                                            StringUtils::Join(album.artist, g_advancedSettings.m_musicItemSeparator));
         if (relevance < THRESHOLD)
-        {
-          m_musicDatabase.Close();
-          return false;
-        }
+          return INFO_NOT_FOUND;
+
         iSelectedAlbum = 0;
       }
     }
 
     if (iSelectedAlbum < 0)
-    {
-      m_musicDatabase.Close();
-      return false;
-    }
+      return INFO_NOT_FOUND;
+
   }
 
   scraper.LoadAlbumInfo(iSelectedAlbum);
@@ -1065,30 +1224,21 @@ bool CMusicInfoScanner::DownloadAlbumInfo(const CStdString& strPath, const CStdS
   {
     if (m_bStop)
     {
-      bCanceled = true;
       scraper.Cancel();
+      return INFO_CANCELLED;
     }
     Sleep(1);
   }
 
-  if (scraper.Succeeded())
-  {
-    albumInfo = scraper.GetAlbum(iSelectedAlbum);
-    album = scraper.GetAlbum(iSelectedAlbum).GetAlbum();
-    if (result == CNfoFile::COMBINED_NFO)
-      nfoReader.GetDetails(album,NULL,true);
-    m_musicDatabase.SetAlbumInfo(params.GetAlbumId(), album, scraper.GetAlbum(iSelectedAlbum).GetSongs(),false);
-  }
-  else
-  {
-    m_musicDatabase.Close();
-    return false;
-  }
+  if (!scraper.Succeeded())
+    return INFO_ERROR;
 
-  // check thumb stuff
-  GetAlbumArtwork(params.GetAlbumId(), album);
-  m_musicDatabase.Close();
-  return true;
+  albumInfo = scraper.GetAlbum(iSelectedAlbum);
+  
+  if (result == CNfoFile::COMBINED_NFO)
+    nfoReader.GetDetails(albumInfo.GetAlbum(), NULL, true);
+  
+  return INFO_ADDED;
 }
 
 void CMusicInfoScanner::GetAlbumArtwork(long id, const CAlbum &album)
@@ -1107,38 +1257,34 @@ void CMusicInfoScanner::GetAlbumArtwork(long id, const CAlbum &album)
   }
 }
 
-bool CMusicInfoScanner::DownloadArtistInfo(const CStdString& strPath, const CStdString& strArtist, bool& bCanceled, CGUIDialogProgress* pDialog)
+INFO_RET CMusicInfoScanner::DownloadArtistInfo(const CArtist& artist, ADDON::ScraperPtr& info, MUSIC_GRABBER::CMusicArtistInfo& artistInfo, CGUIDialogProgress* pDialog)
 {
-  XFILE::MUSICDATABASEDIRECTORY::CQueryParams params;
-  XFILE::MUSICDATABASEDIRECTORY::CDirectoryNode::GetDatabaseInfo(strPath, params);
-  bCanceled = false;
-  CArtist artist;
-  m_musicDatabase.Open();
-  if (m_musicDatabase.HasArtistInfo(params.GetArtistId()) && m_musicDatabase.GetArtistInfo(params.GetArtistId(),artist)) // already got the info
-    return true;
-
-  // find artist info
-  ADDON::ScraperPtr info;
-  if (!m_musicDatabase.GetScraperForPath(strPath, info, ADDON::ADDON_SCRAPER_ARTISTS) || !info)
-  {
-    m_musicDatabase.Close();
-    return false;
-  }
-
   if (m_handle)
   {
     m_handle->SetTitle(StringUtils::Format(g_localizeStrings.Get(20320), info->Name().c_str()));
-    m_handle->SetText(strArtist);
+    m_handle->SetText(artist.strArtist);
   }
 
   // clear our scraper cache
   info->ClearCache();
 
   CMusicInfoScraper scraper(info);
+  bool bMusicBrainz = false;
+  if (!artist.strMusicBrainzArtistID.empty())
+  {
+    CScraperUrl musicBrainzURL;
+    if (ResolveMusicBrainz(artist.strMusicBrainzArtistID, info, scraper, musicBrainzURL))
+    {
+      CMusicArtistInfo artistNfo("nfo", musicBrainzURL);
+      scraper.GetArtists().clear();
+      scraper.GetArtists().push_back(artistNfo);
+      bMusicBrainz = true;
+    }
+  }
+
   // handle nfo files
-  CStdString strArtistPath, strNfo;
-  m_musicDatabase.GetArtistPath(params.GetArtistId(),strArtistPath);
-  URIUtils::AddFileToFolder(strArtistPath,"artist.nfo",strNfo);
+  CStdString strNfo;
+  URIUtils::AddFileToFolder(artist.strPath, "artist.nfo", strNfo);
   CNfoFile::NFOResult result=CNfoFile::NO_NFO;
   CNfoFile nfoReader;
   if (XFILE::CFile::Exists(strNfo))
@@ -1148,23 +1294,18 @@ bool CMusicInfoScanner::DownloadArtistInfo(const CStdString& strPath, const CStd
     if (result == CNfoFile::FULL_NFO)
     {
       CLog::Log(LOGDEBUG, "%s Got details from nfo", __FUNCTION__);
-      CArtist artist;
-      nfoReader.GetDetails(artist);
-      m_musicDatabase.SetArtistInfo(params.GetArtistId(), artist);
-      map<string, string> artwork = GetArtistArtwork(params.GetArtistId(), &artist);
-      m_musicDatabase.SetArtForItem(params.GetArtistId(), "artist", artwork);
-      m_musicDatabase.Close();
-      return true;
+      nfoReader.GetDetails(artistInfo.GetArtist());
+      return INFO_ADDED;
     }
     else if (result == CNfoFile::URL_NFO || result == CNfoFile::COMBINED_NFO)
     {
       CScraperUrl scrUrl(nfoReader.ScraperUrl());
-      CMusicArtistInfo artist("nfo",scrUrl);
+      CMusicArtistInfo artistNfo("nfo",scrUrl);
       info = nfoReader.GetScraperInfo();
       CLog::Log(LOGDEBUG,"-- nfo-scraper: %s",info->Name().c_str());
       CLog::Log(LOGDEBUG,"-- nfo url: %s", scrUrl.m_url[0].m_url.c_str());
       scraper.SetScraperInfo(info);
-      scraper.GetArtists().push_back(artist);
+      scraper.GetArtists().push_back(artistNfo);
     }
     else
       CLog::Log(LOGERROR,"Unable to find an url in nfo file: %s", strNfo.c_str());
@@ -1172,23 +1313,23 @@ bool CMusicInfoScanner::DownloadArtistInfo(const CStdString& strPath, const CStd
 
   if (!scraper.GetArtistCount())
   {
-    scraper.FindArtistInfo(strArtist);
+    scraper.FindArtistInfo(artist.strArtist);
 
     while (!scraper.Completed())
     {
       if (m_bStop)
       {
         scraper.Cancel();
-        bCanceled = true;
+        return INFO_CANCELLED;
       }
       Sleep(1);
     }
   }
 
   int iSelectedArtist = 0;
-  if (result == CNfoFile::NO_NFO)
+  if (result == CNfoFile::NO_NFO && !bMusicBrainz)
   {
-    if (scraper.Succeeded() && scraper.GetArtistCount() >= 1)
+    if (scraper.GetArtistCount() >= 1)
     {
       // now load the first match
       if (pDialog && scraper.GetArtistCount() > 1)
@@ -1224,58 +1365,124 @@ bool CMusicInfoScanner::DownloadArtistInfo(const CStdString& strPath, const CStd
           if (pDlg->GetSelectedLabel() < 0)
           { // none chosen
             if (!pDlg->IsButtonPressed())
-            {
-              bCanceled = true;
-              return false;
-            }
+              return INFO_CANCELLED;
+
             // manual button pressed
-            CStdString strNewArtist = strArtist;
-            if (!CGUIKeyboardFactory::ShowAndGetInput(strNewArtist, g_localizeStrings.Get(16025), false)) return false;
+            CStdString strNewArtist = artist.strArtist;
+            if (!CGUIKeyboardFactory::ShowAndGetInput(strNewArtist, g_localizeStrings.Get(16025), false)) return INFO_CANCELLED;
 
             if (pDialog)
             {
               pDialog->SetLine(0, strNewArtist);
               pDialog->Progress();
             }
-            m_musicDatabase.Close();
-            return DownloadArtistInfo(strPath,strNewArtist,bCanceled,pDialog);
+
+            CArtist newArtist;
+            newArtist.strArtist = strNewArtist;
+            return DownloadArtistInfo(newArtist, info, artistInfo, pDialog);
           }
           iSelectedArtist = pDlg->GetSelectedItem()->m_idepth;
         }
       }
     }
     else
-    {
-      m_musicDatabase.Close();
-      return false;
-    }
+      return INFO_NOT_FOUND;
   }
 
-  scraper.LoadArtistInfo(iSelectedArtist, strArtist);
+  scraper.LoadArtistInfo(iSelectedArtist, artist.strArtist);
   while (!scraper.Completed())
   {
     if (m_bStop)
     {
       scraper.Cancel();
-      bCanceled = true;
+      return INFO_CANCELLED;
     }
     Sleep(1);
   }
 
-  if (scraper.Succeeded())
+  if (!scraper.Succeeded())
+    return INFO_ERROR;
+
+  artistInfo = scraper.GetArtist(iSelectedArtist);
+
+  if (result == CNfoFile::COMBINED_NFO)
+    nfoReader.GetDetails(artistInfo.GetArtist(), NULL, true);
+
+  return INFO_ADDED;
+}
+
+bool CMusicInfoScanner::ResolveMusicBrainz(const CStdString strMusicBrainzID, ScraperPtr &preferredScraper, CMusicInfoScraper &musicInfoScraper, CScraperUrl &musicBrainzURL)
+{
+  // We have a MusicBrainz ID
+  // Get a scraper that can resolve it to a MusicBrainz URL & force our
+  // search directly to the specific album.
+  bool bMusicBrainz = false;
+  ADDON::TYPE type = ScraperTypeFromContent(preferredScraper->Content());
+
+  CFileItemList items;
+  ADDON::AddonPtr addon;
+  ADDON::ScraperPtr defaultScraper;
+  if (ADDON::CAddonMgr::Get().GetDefault(type, addon))
+    defaultScraper = boost::dynamic_pointer_cast<CScraper>(addon);
+
+  vector<ScraperPtr> vecScrapers;
+
+  // add selected scraper - first proirity
+  if (preferredScraper)
+    vecScrapers.push_back(preferredScraper);
+
+  // Add all scrapers except selected and default
+  VECADDONS addons;
+  CAddonMgr::Get().GetAddons(type, addons);
+
+  for (unsigned i = 0; i < addons.size(); ++i)
   {
-    artist = scraper.GetArtist(iSelectedArtist).GetArtist();
-    if (result == CNfoFile::COMBINED_NFO)
-      nfoReader.GetDetails(artist,NULL,true);
-    m_musicDatabase.SetArtistInfo(params.GetArtistId(), artist);
+    ScraperPtr scraper = boost::dynamic_pointer_cast<CScraper>(addons[i]);
+
+    // skip if scraper requires settings and there's nothing set yet
+    if (!scraper || (scraper->RequiresSettings() && !scraper->HasUserSettings()))
+      continue;
+    
+    if((!preferredScraper || preferredScraper->ID() != scraper->ID()) && (!defaultScraper || defaultScraper->ID() != scraper->ID()) )
+      vecScrapers.push_back(scraper);
   }
 
-  // check thumb stuff
-  map<string, string> artwork = GetArtistArtwork(params.GetArtistId(), &artist);
-  m_musicDatabase.SetArtForItem(params.GetArtistId(), "artist", artwork);
+  // add default scraper - not user selectable so it's last priority
+  if(defaultScraper && 
+     (!preferredScraper || preferredScraper->ID() != defaultScraper->ID()) &&
+     (!defaultScraper->RequiresSettings() || defaultScraper->HasUserSettings()))
+    vecScrapers.push_back(defaultScraper);
 
-  m_musicDatabase.Close();
-  return true;
+  for (unsigned int i=0; i < vecScrapers.size(); ++i)
+  {
+    if (vecScrapers[i]->Type() != type)
+      continue;
+
+    vecScrapers[i]->ClearCache();
+    try
+    {
+      musicBrainzURL = vecScrapers[i]->ResolveIDToUrl(strMusicBrainzID);
+    }
+    catch (const ADDON::CScraperError &sce)
+    {
+      if (!sce.FAborted())
+        continue;
+    }
+    if (!musicBrainzURL.m_url.empty())
+    {
+      Sleep(2000); // MusicBrainz rate-limits queries to 1 p.s - once we hit the rate-limiter
+                   // they start serving up the 'you hit the rate-limiter' page fast - meaning
+                   // we will never get below the rate-limit threshold again in a specific run. 
+                   // This helps us to avoidthe rate-limiter as far as possible.
+      CLog::Log(LOGDEBUG,"-- nfo-scraper: %s",vecScrapers[i]->Name().c_str());
+      CLog::Log(LOGDEBUG,"-- nfo url: %s", musicBrainzURL.m_url[0].m_url.c_str());
+      musicInfoScraper.SetScraperInfo(vecScrapers[i]);
+      bMusicBrainz = true;
+      break;
+    }
+  }
+
+  return bMusicBrainz;
 }
 
 map<string, string> CMusicInfoScanner::GetArtistArtwork(long id, const CArtist *artist)
@@ -1320,9 +1527,9 @@ map<string, string> CMusicInfoScanner::GetArtistArtwork(long id, const CArtist *
 void CMusicInfoScanner::Run()
 {
   int count = 0;
-  while (!m_bStop && m_pathsToCount.size())
+  for (set<std::string>::iterator it = m_pathsToScan.begin(); it != m_pathsToScan.end() && !m_bStop; ++it)
   {
-    count+=CountFilesRecursively(*m_pathsToCount.begin());
+    count+=CountFilesRecursively(*it);
   }
   m_itemCount = count;
 }
@@ -1337,13 +1544,8 @@ int CMusicInfoScanner::CountFilesRecursively(const CStdString& strPath)
   if (m_bStop)
     return 0;
 
-  int count = CountFiles(items, true);
-
   // true for recursive counting
-  set<CStdString>::iterator it = m_pathsToCount.find(strPath);
-  if (it != m_pathsToCount.end())
-    m_pathsToCount.erase(it);
-
+  int count = CountFiles(items, true);
   return count;
 }
 
index 65afcfa..f959c38 100644 (file)
@@ -21,6 +21,7 @@
 #include "threads/Thread.h"
 #include "music/MusicDatabase.h"
 #include "MusicAlbumInfo.h"
+#include "MusicInfoScraper.h"
 
 class CAlbum;
 class CArtist;
@@ -62,28 +63,35 @@ public:
   //! \brief Set whether or not to show a progress dialog
   void ShowDialog(bool show) { m_showDialog = show; }
 
-  /*! \brief Categorise songs into albums
+  /*! \brief Categorize FileItems into Albums, Songs, and Artists
+   This takes a list of FileItems and turns it into a tree of Albums,
+   Artists, and Songs.
    Albums are defined uniquely by the album name and album artist.
+   
+   \param songs [in/out] list of songs to categorise - albumartist field may be altered.
+   \param albums [out] albums found within these songs.
+   */
+  static void FileItemsToAlbums(CFileItemList& items, VECALBUMS& albums, MAPSONGS* songsMap = NULL);
 
+  /*! \brief Fixup albums and songs
+   
    If albumartist is not available in a song, we determine it from the
    common portion of each song's artist list.
-
+   
    eg the common artist for
-     Bob Dylan / Tom Petty / Roy Orbison
-     Bob Dylan / Tom Petty
+   Bob Dylan / Tom Petty / Roy Orbison
+   Bob Dylan / Tom Petty
    would be "Bob Dylan / Tom Petty".
-
+   
    If all songs that share an album
-    1. have a non-empty album name
-    2. have at least two different primary artists
-    3. have no album artist set
-    4. and no track numbers overlap
+   1. have a non-empty album name
+   2. have at least two different primary artists
+   3. have no album artist set
+   4. and no track numbers overlap
    we assume it is a various artists album, and set the albumartist field accordingly.
-
-   \param songs [in/out] list of songs to categorise - albumartist field may be altered.
-   \param albums [out] albums found within these songs.
+   
    */
-  static void CategoriseAlbums(VECSONGS &songs, VECALBUMS &albums);
+  static void FixupAlbums(VECALBUMS &albums);
 
   /*! \brief Find art for albums
    Based on the albums in the folder, finds whether we have unique album art
@@ -102,13 +110,21 @@ public:
    */
   static void FindArtForAlbums(VECALBUMS &albums, const CStdString &path);
 
-  bool DownloadAlbumInfo(const CStdString& strPath, const CStdString& strArtist, const CStdString& strAlbum, bool& bCanceled, MUSIC_GRABBER::CMusicAlbumInfo& album, CGUIDialogProgress* pDialog=NULL);
-  bool DownloadArtistInfo(const CStdString& strPath, const CStdString& strArtist, bool& bCanceled, CGUIDialogProgress* pDialog=NULL);
+  INFO_RET DownloadAlbumInfo(const CAlbum& album, ADDON::ScraperPtr& scraper, MUSIC_GRABBER::CMusicAlbumInfo& albumInfo, CGUIDialogProgress* pDialog = NULL);
+  INFO_RET DownloadArtistInfo(const CArtist& artist, ADDON::ScraperPtr& scraper, MUSIC_GRABBER::CMusicArtistInfo& artistInfo, CGUIDialogProgress* pDialog = NULL);
 
   std::map<std::string, std::string> GetArtistArtwork(long id, const CArtist *artist = NULL);
 protected:
   virtual void Process();
-  int RetrieveMusicInfo(CFileItemList& items, const CStdString& strDirectory);
+
+  /*! \brief Scan in the ID3/Ogg/FLAC tags for a bunch of FileItems
+   Given a list of FileItems, scan in the tags for those FileItems
+   and populate a new FileItemList with the files that were successfully scanned.
+   Any files which couldn't be scanned (no/bad tags) are discarded in the process.
+   \param items [in] list of FileItems to scan
+   \param scannedItems [in] list to populate with the scannedItems
+   */
+  int RetrieveMusicInfo(const CStdString& strDirectory, CFileItemList& items);
 
   /*! \brief Scan in the ID3/Ogg/FLAC tags for a bunch of FileItems
     Given a list of FileItems, scan in the tags for those FileItems
@@ -127,6 +143,15 @@ protected:
   int CountFiles(const CFileItemList& items, bool recursive);
   int CountFilesRecursively(const CStdString& strPath);
 
+  /*! \brief Resolve a MusicBrainzID to a URL
+   If we have a MusicBrainz ID for an artist or album, 
+   resolve it to an MB URL and set up the scrapers accordingly.
+   
+   \param preferredScraper [in] A ScraperPtr to the preferred album/artist scraper.
+   \param musicBrainzURL [out] will be populated with the MB URL for the artist/album.
+   */
+  bool ResolveMusicBrainz(const CStdString strMusicBrainzID, ADDON::ScraperPtr &preferredScraper, MUSIC_GRABBER::CMusicInfoScraper &musicInfoScraper, CScraperUrl &musicBrainzURL);
+
 protected:
   bool m_showDialog;
   CGUIDialogProgressBarHandle* m_handle;
@@ -138,12 +163,10 @@ protected:
   int m_scanType; // 0 - load from files, 1 - albums, 2 - artists
   CMusicDatabase m_musicDatabase;
 
-  std::set<CStdString> m_pathsToScan;
-  std::set<CAlbum> m_albumsToScan;
-  std::set<CArtist> m_artistsToScan;
-  std::set<CStdString> m_pathsToCount;
-  std::vector<long> m_artistsScanned;
-  std::vector<long> m_albumsScanned;
+  std::map<CAlbum, CAlbum> m_albumCache;
+  std::map<CArtistCredit, CArtist> m_artistCache;
+
+  std::set<std::string> m_pathsToScan;
   int m_flags;
   CThread m_fileCountReader;
 };
index fe88fc4..1a28163 100644 (file)
@@ -91,9 +91,9 @@ const CMusicInfoTag& CMusicInfoTag::operator =(const CMusicInfoTag& tag)
   m_genre = tag.m_genre;
   m_strTitle = tag.m_strTitle;
   m_strMusicBrainzTrackID = tag.m_strMusicBrainzTrackID;
-  m_strMusicBrainzArtistID = tag.m_strMusicBrainzArtistID;
+  m_musicBrainzArtistID = tag.m_musicBrainzArtistID;
   m_strMusicBrainzAlbumID = tag.m_strMusicBrainzAlbumID;
-  m_strMusicBrainzAlbumArtistID = tag.m_strMusicBrainzAlbumArtistID;
+  m_musicBrainzAlbumArtistID = tag.m_musicBrainzAlbumArtistID;
   m_strMusicBrainzTRMID = tag.m_strMusicBrainzTRMID;
   m_strComment = tag.m_strComment;
   m_strLyrics = tag.m_strLyrics;
@@ -431,9 +431,9 @@ const CStdString& CMusicInfoTag::GetMusicBrainzTrackID() const
   return m_strMusicBrainzTrackID;
 }
 
-const CStdString& CMusicInfoTag::GetMusicBrainzArtistID() const
+const std::vector<std::string>& CMusicInfoTag::GetMusicBrainzArtistID() const
 {
-  return m_strMusicBrainzArtistID;
+  return m_musicBrainzArtistID;
 }
 
 const CStdString& CMusicInfoTag::GetMusicBrainzAlbumID() const
@@ -441,9 +441,9 @@ const CStdString& CMusicInfoTag::GetMusicBrainzAlbumID() const
   return m_strMusicBrainzAlbumID;
 }
 
-const CStdString& CMusicInfoTag::GetMusicBrainzAlbumArtistID() const
+const std::vector<std::string>& CMusicInfoTag::GetMusicBrainzAlbumArtistID() const
 {
-  return m_strMusicBrainzAlbumArtistID;
+  return m_musicBrainzAlbumArtistID;
 }
 
 const CStdString& CMusicInfoTag::GetMusicBrainzTRMID() const
@@ -456,9 +456,9 @@ void CMusicInfoTag::SetMusicBrainzTrackID(const CStdString& strTrackID)
   m_strMusicBrainzTrackID=strTrackID;
 }
 
-void CMusicInfoTag::SetMusicBrainzArtistID(const CStdString& strArtistID)
+void CMusicInfoTag::SetMusicBrainzArtistID(const std::vector<std::string>& musicBrainzArtistId)
 {
-  m_strMusicBrainzArtistID=strArtistID;
+  m_musicBrainzArtistID = musicBrainzArtistId;
 }
 
 void CMusicInfoTag::SetMusicBrainzAlbumID(const CStdString& strAlbumID)
@@ -466,9 +466,9 @@ void CMusicInfoTag::SetMusicBrainzAlbumID(const CStdString& strAlbumID)
   m_strMusicBrainzAlbumID=strAlbumID;
 }
 
-void CMusicInfoTag::SetMusicBrainzAlbumArtistID(const CStdString& strAlbumArtistID)
+void CMusicInfoTag::SetMusicBrainzAlbumArtistID(const std::vector<std::string>& musicBrainzAlbumArtistId)
 {
-  m_strMusicBrainzAlbumArtistID=strAlbumArtistID;
+  m_musicBrainzAlbumArtistID = musicBrainzAlbumArtistId;
 }
 
 void CMusicInfoTag::SetMusicBrainzTRMID(const CStdString& strTRMID)
@@ -542,10 +542,6 @@ void CMusicInfoTag::SetSong(const CSong& song)
   SetAlbum(song.strAlbum);
   SetAlbumArtist(song.albumArtist);
   SetMusicBrainzTrackID(song.strMusicBrainzTrackID);
-  SetMusicBrainzArtistID(song.strMusicBrainzArtistID);
-  SetMusicBrainzAlbumID(song.strMusicBrainzAlbumID);
-  SetMusicBrainzAlbumArtistID(song.strMusicBrainzAlbumArtistID);
-  SetMusicBrainzTRMID(song.strMusicBrainzTRMID);
   SetComment(song.strComment);
   SetPlayCount(song.iTimesPlayed);
   SetLastPlayed(song.lastPlayed);
@@ -581,9 +577,9 @@ void CMusicInfoTag::Serialize(CVariant& value) const
   value["loaded"] = m_bLoaded;
   value["year"] = m_dwReleaseDate.wYear;
   value["musicbrainztrackid"] = m_strMusicBrainzTrackID;
-  value["musicbrainzartistid"] = m_strMusicBrainzArtistID;
+  value["musicbrainzartistid"] = StringUtils::Join(m_musicBrainzArtistID, " / ");
   value["musicbrainzalbumid"] = m_strMusicBrainzAlbumID;
-  value["musicbrainzalbumartistid"] = m_strMusicBrainzAlbumArtistID;
+  value["musicbrainzalbumartistid"] = StringUtils::Join(m_musicBrainzAlbumArtistID, " / ");
   value["musicbrainztrmid"] = m_strMusicBrainzTRMID;
   value["comment"] = m_strComment;
   value["rating"] = (int)(m_rating - '0');
@@ -627,9 +623,9 @@ void CMusicInfoTag::Archive(CArchive& ar)
     ar << m_bLoaded;
     ar << m_dwReleaseDate;
     ar << m_strMusicBrainzTrackID;
-    ar << m_strMusicBrainzArtistID;
+    ar << m_musicBrainzArtistID;
     ar << m_strMusicBrainzAlbumID;
-    ar << m_strMusicBrainzAlbumArtistID;
+    ar << m_musicBrainzAlbumArtistID;
     ar << m_strMusicBrainzTRMID;
     ar << m_lastPlayed;
     ar << m_strComment;
@@ -655,9 +651,9 @@ void CMusicInfoTag::Archive(CArchive& ar)
     ar >> m_bLoaded;
     ar >> m_dwReleaseDate;
     ar >> m_strMusicBrainzTrackID;
-    ar >> m_strMusicBrainzArtistID;
+    ar >> m_musicBrainzArtistID;
     ar >> m_strMusicBrainzAlbumID;
-    ar >> m_strMusicBrainzAlbumArtistID;
+    ar >> m_musicBrainzAlbumArtistID;
     ar >> m_strMusicBrainzTRMID;
     ar >> m_lastPlayed;
     ar >> m_strComment;
@@ -681,9 +677,9 @@ void CMusicInfoTag::Clear()
   m_genre.clear();
   m_strTitle.Empty();
   m_strMusicBrainzTrackID.Empty();
-  m_strMusicBrainzArtistID.Empty();
+  m_musicBrainzArtistID.clear();
   m_strMusicBrainzAlbumID.Empty();
-  m_strMusicBrainzAlbumArtistID.Empty();
+  m_musicBrainzAlbumArtistID.clear();
   m_strMusicBrainzTRMID.Empty();
   m_iDuration = 0;
   m_iTrack = 0;
index 606af58..a4ea3ba 100644 (file)
@@ -89,9 +89,9 @@ public:
   void GetReleaseDate(SYSTEMTIME& dateTime) const;
   CStdString GetYearString() const;
   const CStdString& GetMusicBrainzTrackID() const;
-  const CStdString& GetMusicBrainzArtistID() const;
+  const std::vector<std::string>& GetMusicBrainzArtistID() const;
   const CStdString& GetMusicBrainzAlbumID() const;
-  const CStdString& GetMusicBrainzAlbumArtistID() const;
+  const std::vector<std::string>& GetMusicBrainzAlbumArtistID() const;
   const CStdString& GetMusicBrainzTRMID() const;
   const CStdString& GetComment() const;
   const CStdString& GetLyrics() const;
@@ -129,9 +129,9 @@ public:
   void SetAlbum(const CAlbum& album);
   void SetSong(const CSong& song);
   void SetMusicBrainzTrackID(const CStdString& strTrackID);
-  void SetMusicBrainzArtistID(const CStdString& strArtistID);
+  void SetMusicBrainzArtistID(const std::vector<std::string>& musicBrainzArtistId);
   void SetMusicBrainzAlbumID(const CStdString& strAlbumID);
-  void SetMusicBrainzAlbumArtistID(const CStdString& strAlbumArtistID);
+  void SetMusicBrainzAlbumArtistID(const std::vector<std::string>& musicBrainzAlbumArtistId);
   void SetMusicBrainzTRMID(const CStdString& strTRMID);
   void SetComment(const CStdString& comment);
   void SetLyrics(const CStdString& lyrics);
@@ -184,9 +184,9 @@ protected:
   std::vector<std::string> m_albumArtist;
   std::vector<std::string> m_genre;
   CStdString m_strMusicBrainzTrackID;
-  CStdString m_strMusicBrainzArtistID;
+  std::vector<std::string> m_musicBrainzArtistID;
   CStdString m_strMusicBrainzAlbumID;
-  CStdString m_strMusicBrainzAlbumArtistID;
+  std::vector<std::string> m_musicBrainzAlbumArtistID;
   CStdString m_strMusicBrainzTRMID;
   CStdString m_strComment;
   CStdString m_strLyrics;
index 6b88302..4fb2da3 100644 (file)
@@ -263,9 +263,9 @@ bool CTagLoaderTagLib::ParseASF(ASF::Tag *asf, EmbeddedArt *art, CMusicInfoTag&
     else if (it->first == "WM/ArtistSortOrder")          {} // Known unsupported, supress warnings
     else if (it->first == "WM/Script")                   {} // Known unsupported, supress warnings
     else if (it->first == "WM/Year")                     tag.SetYear(atoi(it->second.front().toString().toCString(true)));
-    else if (it->first == "MusicBrainz/Artist Id")       tag.SetMusicBrainzArtistID(it->second.front().toString().to8Bit(true));
+    else if (it->first == "MusicBrainz/Artist Id")       tag.SetMusicBrainzArtistID(GetASFStringList(it->second));
     else if (it->first == "MusicBrainz/Album Id")        tag.SetMusicBrainzAlbumID(it->second.front().toString().to8Bit(true));
-    else if (it->first == "MusicBrainz/Album Artist Id") tag.SetMusicBrainzAlbumArtistID(it->second.front().toString().to8Bit(true));
+    else if (it->first == "MusicBrainz/Album Artist Id") tag.SetMusicBrainzAlbumArtistID(GetASFStringList(it->second));
     else if (it->first == "MusicBrainz/Track Id")        tag.SetMusicBrainzTrackID(it->second.front().toString().to8Bit(true));
     else if (it->first == "MusicBrainz/Album Status")    {}
     else if (it->first == "MusicBrainz/Album Type")      {}
@@ -375,9 +375,9 @@ bool CTagLoaderTagLib::ParseID3v2Tag(ID3v2::Tag *id3v2, EmbeddedArt *art, CMusic
         // First field is the same as the description
         StringList stringList = frame->fieldList(); 
         stringList.erase(stringList.begin());
-        if      (frame->description() == "MusicBrainz Artist Id")       tag.SetMusicBrainzArtistID(stringList.front().to8Bit(true));
+        if      (frame->description() == "MusicBrainz Artist Id")       tag.SetMusicBrainzArtistID(StringListToVectorString(stringList));
         else if (frame->description() == "MusicBrainz Album Id")        tag.SetMusicBrainzAlbumID(stringList.front().to8Bit(true));
-        else if (frame->description() == "MusicBrainz Album Artist Id") tag.SetMusicBrainzAlbumArtistID(stringList.front().to8Bit(true));
+        else if (frame->description() == "MusicBrainz Album Artist Id") tag.SetMusicBrainzAlbumArtistID(StringListToVectorString(stringList));
         else if (frame->description() == "replaygain_track_gain")       tag.SetReplayGainTrackGain((int)(atof(stringList.front().toCString(true)) * 100 + 0.5));
         else if (frame->description() == "replaygain_album_gain")       tag.SetReplayGainAlbumGain((int)(atof(stringList.front().toCString(true)) * 100 + 0.5));
         else if (frame->description() == "replaygain_track_peak")       tag.SetReplayGainTrackPeak((float)atof(stringList.front().toCString(true)));
@@ -477,8 +477,8 @@ bool CTagLoaderTagLib::ParseAPETag(APE::Tag *ape, EmbeddedArt *art, CMusicInfoTa
     else if (it->first == "REPLAYGAIN_ALBUM_GAIN")     tag.SetReplayGainAlbumGain((int)(atof(it->second.toString().toCString(true)) * 100 + 0.5));
     else if (it->first == "REPLAYGAIN_TRACK_PEAK")     tag.SetReplayGainTrackPeak((float)atof(it->second.toString().toCString(true)));
     else if (it->first == "REPLAYGAIN_ALBUM_PEAK")     tag.SetReplayGainAlbumPeak((float)atof(it->second.toString().toCString(true)));
-    else if (it->first == "MUSICBRAINZ_ARTISTID")      tag.SetMusicBrainzArtistID(it->second.toString().to8Bit(true));
-    else if (it->first == "MUSICBRAINZ_ALBUMARTISTID") tag.SetMusicBrainzAlbumArtistID(it->second.toString().to8Bit(true));
+    else if (it->first == "MUSICBRAINZ_ARTISTID")      tag.SetMusicBrainzArtistID(StringListToVectorString(it->second.toStringList()));
+    else if (it->first == "MUSICBRAINZ_ALBUMARTISTID") tag.SetMusicBrainzAlbumArtistID(StringListToVectorString(it->second.toStringList()));
     else if (it->first == "MUSICBRAINZ_ALBUMID")       tag.SetMusicBrainzAlbumID(it->second.toString().to8Bit(true));
     else if (it->first == "MUSICBRAINZ_TRACKID")       tag.SetMusicBrainzTrackID(it->second.toString().to8Bit(true));
     else if (g_advancedSettings.m_logLevel == LOG_LEVEL_MAX)
@@ -517,8 +517,8 @@ bool CTagLoaderTagLib::ParseXiphComment(Ogg::XiphComment *xiph, EmbeddedArt *art
     else if (it->first == "REPLAYGAIN_ALBUM_GAIN")     tag.SetReplayGainAlbumGain((int)(atof(it->second.front().toCString(true)) * 100 + 0.5));
     else if (it->first == "REPLAYGAIN_TRACK_PEAK")     tag.SetReplayGainTrackPeak((float)atof(it->second.front().toCString(true)));
     else if (it->first == "REPLAYGAIN_ALBUM_PEAK")     tag.SetReplayGainAlbumPeak((float)atof(it->second.front().toCString(true)));
-    else if (it->first == "MUSICBRAINZ_ARTISTID")      tag.SetMusicBrainzArtistID(it->second.front().to8Bit(true));
-    else if (it->first == "MUSICBRAINZ_ALBUMARTISTID") tag.SetMusicBrainzAlbumArtistID(it->second.front().to8Bit(true));
+    else if (it->first == "MUSICBRAINZ_ARTISTID")      tag.SetMusicBrainzArtistID(StringListToVectorString(it->second));
+    else if (it->first == "MUSICBRAINZ_ALBUMARTISTID") tag.SetMusicBrainzAlbumArtistID(StringListToVectorString(it->second));
     else if (it->first == "MUSICBRAINZ_ALBUMID")       tag.SetMusicBrainzAlbumID(it->second.front().to8Bit(true));
     else if (it->first == "MUSICBRAINZ_TRACKID")       tag.SetMusicBrainzTrackID(it->second.front().to8Bit(true));
     else if (it->first == "RATING")
@@ -599,9 +599,9 @@ bool CTagLoaderTagLib::ParseMP4Tag(MP4::Tag *mp4, EmbeddedArt *art, CMusicInfoTa
     else if (it->first == "disk")    tag.SetPartOfSet(it->second.toIntPair().first);
     else if (it->first == "\251day") tag.SetYear(it->second.toStringList().front().toInt());
     else if (it->first == "----:com.apple.iTunes:MusicBrainz Artist Id")
-      tag.SetMusicBrainzArtistID(it->second.toStringList().front().to8Bit(true));
+      tag.SetMusicBrainzArtistID(StringListToVectorString(it->second.toStringList()));
     else if (it->first == "----:com.apple.iTunes:MusicBrainz Album Artist Id")
-      tag.SetMusicBrainzAlbumArtistID(it->second.toStringList().front().to8Bit(true));
+      tag.SetMusicBrainzAlbumArtistID(StringListToVectorString(it->second.toStringList()));
     else if (it->first == "----:com.apple.iTunes:MusicBrainz Album Id")
       tag.SetMusicBrainzAlbumID(it->second.toStringList().front().to8Bit(true));
     else if (it->first == "----:com.apple.iTunes:MusicBrainz Track Id")
index 3c8fbd6..82e8d08 100644 (file)
@@ -307,92 +307,56 @@ void CGUIWindowMusicBase::OnInfo(CFileItem *pItem, bool bShowInfo)
   // this function called from outside this window - make sure the database is open
   m_musicdatabase.Open();
 
-  CStdString strPath = pItem->GetPath();
-
-  // Try to find an album to lookup from the current item
-  CAlbum album;
-  CArtist artist;
-  bool foundAlbum = false;
-
-  album.idAlbum = -1;
-
   // we have a folder
   if (pItem->IsMusicDb())
   {
     CQueryParams params;
     CDirectoryNode::GetDatabaseInfo(pItem->GetPath(), params);
     if (params.GetAlbumId() == -1)
-    { // artist lookup
-      m_musicdatabase.GetArtistInfo(params.GetArtistId(), artist);
-    }
+      ShowArtistInfo(pItem);
     else
-    { // album lookup
-      m_musicdatabase.GetAlbumInfo(params.GetAlbumId(), album, NULL);
+      ShowAlbumInfo(pItem);
 
-      // we're going to need it's path as well (we assume that there's only one) - this is for
-      // assigning thumbs to folders, and obtaining the local folder.jpg
-      m_musicdatabase.GetAlbumPath(album.idAlbum, strPath);
-    }
+    if (m_dlgProgress && bShowInfo)
+      m_dlgProgress->Close();
+    return;
   }
-  else
-  { // from filemode, so find the albums in the folder
-    CFileItemList items;
-    GetDirectory(strPath, items);
 
-    // show dialog box indicating we're searching the album name
-    if (m_dlgProgress && bShowInfo)
-    {
-      m_dlgProgress->SetHeading(185);
-      m_dlgProgress->SetLine(0, 501);
-      m_dlgProgress->SetLine(1, "");
-      m_dlgProgress->SetLine(2, "");
-      m_dlgProgress->StartModal();
-      m_dlgProgress->Progress();
-      if (m_dlgProgress->IsCanceled())
-      {
-        m_musicdatabase.Close();
-        return;
-      }
-    }
-    // check the first song we find in the folder, and grab it's album info
-    for (int i = 0; i < items.Size() && !foundAlbum; i++)
+  CFileItemList items;
+  GetDirectory(pItem->GetPath(), items);
+
+  // show dialog box indicating we're searching the album name
+  if (m_dlgProgress && bShowInfo)
+  {
+    m_dlgProgress->SetHeading(185);
+    m_dlgProgress->SetLine(0, 501);
+    m_dlgProgress->SetLine(1, "");
+    m_dlgProgress->SetLine(2, "");
+    m_dlgProgress->StartModal();
+    m_dlgProgress->Progress();
+    if (m_dlgProgress->IsCanceled())
     {
-      CFileItemPtr pItem = items[i];
-      pItem->LoadMusicTag();
-      if (pItem->HasMusicInfoTag() && pItem->GetMusicInfoTag()->Loaded() &&
-         !pItem->GetMusicInfoTag()->GetAlbum().IsEmpty())
-      {
-        // great, have a song - use it.
-        CSong song(*pItem->GetMusicInfoTag());
-        // this function won't be needed if/when the tag has idSong information
-        if (!m_musicdatabase.GetAlbumFromSong(song, album))
-        { // album isn't in the database - construct it from the tag info we have
-          CMusicInfoTag *tag = pItem->GetMusicInfoTag();
-          album.strAlbum = tag->GetAlbum();
-          album.artist = tag->GetAlbumArtist().empty() ? tag->GetArtist() : tag->GetAlbumArtist();
-          album.idAlbum = -1; // the -1 indicates it's not in the database
-        }
-        foundAlbum = true;
-      }
+      return;
     }
-    if (!foundAlbum)
+  }
+
+  // check the first song we find in the folder, and grab it's album info
+  for (int i = 0; i < items.Size(); i++)
+  {
+    CFileItemPtr pItem = items[i];
+    pItem->LoadMusicTag();
+    if (pItem->HasMusicInfoTag() && pItem->GetMusicInfoTag()->Loaded() &&
+       !pItem->GetMusicInfoTag()->GetAlbum().IsEmpty())
     {
-      CLog::Log(LOGINFO, "%s called on a folder containing no songs with tag info - nothing can be done", __FUNCTION__);
+      ShowAlbumInfo(pItem.get());
       if (m_dlgProgress && bShowInfo)
         m_dlgProgress->Close();
-      m_musicdatabase.Close();
-      return;
     }
-
-    if (m_dlgProgress && bShowInfo)
-      m_dlgProgress->Close();
   }
 
-  if (album.idAlbum == -1 && foundAlbum == false)
-    ShowArtistInfo(artist, pItem->GetPath(), bShowInfo);
-  else
-    ShowAlbumInfo(album, strPath, bShowInfo);
-  m_musicdatabase.Close();
+  CLog::Log(LOGINFO, "%s called on a folder containing no songs with tag info - nothing can be done", __FUNCTION__);
+  if (m_dlgProgress && bShowInfo)
+      m_dlgProgress->Close();
 }
 
 void CGUIWindowMusicBase::OnManualAlbumInfo()
@@ -406,11 +370,17 @@ void CGUIWindowMusicBase::OnManualAlbumInfo()
   if (!CGUIKeyboardFactory::ShowAndGetInput(strArtist, g_localizeStrings.Get(16025), false))
     return;
 
-  ShowAlbumInfo(album,"",true);
+//  ShowAlbumInfo(album, true); FIXME
 }
 
-void CGUIWindowMusicBase::ShowArtistInfo(const CArtist& artist, const CStdString& path, bool bShowInfo /* = true */)
+void CGUIWindowMusicBase::ShowArtistInfo(const CFileItem *pItem, bool bShowInfo /* = true */)
 {
+  CArtist artist;
+  CQueryParams params;
+  CDirectoryNode::GetDatabaseInfo(pItem->GetPath(), params);
+  m_musicdatabase.GetArtistInfo(params.GetArtistId(), artist);
+  m_musicdatabase.GetArtistPath(artist.idArtist, artist.strPath);
+
   bool saveDb = artist.idArtist != -1;
   if (!CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() && !g_passwordManager.bMasterUser)
     saveDb = false;
@@ -418,6 +388,7 @@ void CGUIWindowMusicBase::ShowArtistInfo(const CArtist& artist, const CStdString
   CMusicArtistInfo artistInfo;
   while (1)
   {
+    // Check if we have the information in the database first
     if (!m_musicdatabase.HasArtistInfo(artist.idArtist) ||
         !m_musicdatabase.GetArtistInfo(artist.idArtist, artistInfo.GetArtist()))
     {
@@ -427,7 +398,9 @@ void CGUIWindowMusicBase::ShowArtistInfo(const CArtist& artist, const CStdString
         break;
       }
 
-      if (!FindArtistInfo(artist.strArtist, artistInfo, bShowInfo ? SELECTION_ALLOWED : SELECTION_AUTO))
+      if (!FindArtistInfo(pItem,
+                          artistInfo,
+                          bShowInfo ? SELECTION_ALLOWED : SELECTION_AUTO))
         break;
 
       if (!artistInfo.Loaded())
@@ -444,7 +417,7 @@ void CGUIWindowMusicBase::ShowArtistInfo(const CArtist& artist, const CStdString
     CGUIDialogMusicInfo *pDlgArtistInfo = (CGUIDialogMusicInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_MUSIC_INFO);
     if (pDlgArtistInfo)
     {
-      pDlgArtistInfo->SetArtist(artistInfo.GetArtist(), path);
+      pDlgArtistInfo->SetArtist(artistInfo.GetArtist(), artist.strPath);
       pDlgArtistInfo->DoModal();
 
       if (pDlgArtistInfo->NeedRefresh())
@@ -463,17 +436,20 @@ void CGUIWindowMusicBase::ShowArtistInfo(const CArtist& artist, const CStdString
     m_dlgProgress->Close();
 }
 
-void CGUIWindowMusicBase::ShowAlbumInfo(const CAlbum& album, const CStdString& path, bool bShowInfo /* = true */)
+void CGUIWindowMusicBase::ShowAlbumInfo(const CFileItem *pItem, bool bShowInfo /* = true */)
 {
-  bool saveDb = album.idAlbum != -1;
+  CQueryParams params;
+  CDirectoryNode::GetDatabaseInfo(pItem->GetPath(), params);
+
+  bool saveDb = params.GetAlbumId() != -1;
   if (!CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() && !g_passwordManager.bMasterUser)
     saveDb = false;
 
   CMusicAlbumInfo albumInfo;
   while (1)
   {
-    if (!m_musicdatabase.HasAlbumInfo(album.idAlbum) || 
-        !m_musicdatabase.GetAlbumInfo(album.idAlbum, albumInfo.GetAlbum(), &albumInfo.GetAlbum().songs))
+    if (!m_musicdatabase.HasAlbumInfo(params.GetAlbumId()) || 
+        !m_musicdatabase.GetAlbumInfo(params.GetAlbumId(), albumInfo.GetAlbum(), &albumInfo.GetAlbum().songs))
     {
       if (g_application.IsMusicScanning())
       {
@@ -481,8 +457,7 @@ void CGUIWindowMusicBase::ShowAlbumInfo(const CAlbum& album, const CStdString& p
         break;
       }
 
-      if (!FindAlbumInfo(album.strAlbum, 
-                         StringUtils::Join(album.artist, g_advancedSettings.m_musicItemSeparator),
+      if (!FindAlbumInfo(pItem,
                          albumInfo,
                          bShowInfo ? SELECTION_ALLOWED : SELECTION_AUTO))
         break;
@@ -493,26 +468,28 @@ void CGUIWindowMusicBase::ShowAlbumInfo(const CAlbum& album, const CStdString& p
         break;
       }
 
-      albumInfo.GetAlbum().strAlbum = album.strAlbum;
+//      albumInfo.GetAlbum().strAlbum = album.strAlbum; FIXME?
 
       if (saveDb)
-        m_musicdatabase.SetAlbumInfo(album.idAlbum, albumInfo.GetAlbum(), albumInfo.GetSongs());
+        m_musicdatabase.SetAlbumInfo(params.GetAlbumId(), albumInfo.GetAlbum(), albumInfo.GetSongs());
     }
 
     CGUIDialogMusicInfo *pDlgAlbumInfo = (CGUIDialogMusicInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_MUSIC_INFO);
     if (pDlgAlbumInfo)
     {
-      pDlgAlbumInfo->SetAlbum(albumInfo.GetAlbum(), path);
+      CStdString strPath;
+      m_musicdatabase.GetAlbumPath(params.GetAlbumId(), strPath);
+      pDlgAlbumInfo->SetAlbum(albumInfo.GetAlbum(), strPath);
       pDlgAlbumInfo->DoModal();
 
       if (pDlgAlbumInfo->NeedRefresh())
       {
-        m_musicdatabase.DeleteAlbumInfo(album.idAlbum);
+        m_musicdatabase.DeleteAlbumInfo(params.GetAlbumId());
         continue;
       }
       else if (pDlgAlbumInfo->HasUpdatedThumb())
       {
-        UpdateThumb(album, path);
+        UpdateThumb(albumInfo.GetAlbum(), strPath);
       }
     }
     break;
@@ -713,101 +690,108 @@ void CGUIWindowMusicBase::UpdateButtons()
   CGUIMediaWindow::UpdateButtons();
 }
 
-bool CGUIWindowMusicBase::FindAlbumInfo(const CStdString& strAlbum, const CStdString& strArtist, CMusicAlbumInfo& album, ALLOW_SELECTION allowSelection)
+// This is basically the same as the MusicInfoScanner->Process, with a different kind
+// of dialog, and should be merged.
+bool CGUIWindowMusicBase::FindAlbumInfo(const CFileItem* item, CMusicAlbumInfo& albumInfo, ALLOW_SELECTION allowSelection)
 {
+  CQueryParams params;
+  CDirectoryNode::GetDatabaseInfo(item->GetPath(), params);
+
+  CAlbum album(*item);
+  if (params.GetAlbumId() != -1 && m_musicdatabase.HasAlbumInfo(params.GetArtistId()))
+    m_musicdatabase.GetAlbumInfo(params.GetAlbumId(), album, &album.songs);
+
   // show dialog box indicating we're searching the album
   if (m_dlgProgress && allowSelection != SELECTION_AUTO)
   {
     m_dlgProgress->SetHeading(185);
-    m_dlgProgress->SetLine(0, strAlbum);
-    m_dlgProgress->SetLine(1, strArtist);
+    m_dlgProgress->SetLine(0, album.strAlbum);
+    m_dlgProgress->SetLine(1, StringUtils::Join(album.artist, g_advancedSettings.m_musicItemSeparator));
     m_dlgProgress->SetLine(2, "");
     m_dlgProgress->StartModal();
   }
+  
+  // find album info
+  ADDON::ScraperPtr scraper;
+  if (!m_musicdatabase.GetScraperForPath(item->GetPath(), scraper, ADDON::ADDON_SCRAPER_ALBUMS) || !scraper)
+    return false;
 
   CMusicInfoScanner scanner;
-  CStdString strPath;
-  CStdString strTempAlbum(strAlbum);
-  CStdString strTempArtist(strArtist);
-  long idAlbum = m_musicdatabase.GetAlbumByName(strAlbum,strArtist);
-  strPath.Format("musicdb://albums/%d/",idAlbum);
-
-  bool bCanceled(false);
-  bool needsRefresh(true);
-  do
+
+loop:
+  INFO_RET albumDownloadStatus = scanner.DownloadAlbumInfo(album, scraper, albumInfo, m_dlgProgress);
+  if (albumDownloadStatus == INFO_NOT_FOUND)
   {
-    if (!scanner.DownloadAlbumInfo(strPath,strTempArtist,strTempAlbum,bCanceled,album,m_dlgProgress))
+    if (m_dlgProgress && allowSelection != SELECTION_AUTO)
     {
-      if (bCanceled)
+      if (!CGUIKeyboardFactory::ShowAndGetInput(album.strAlbum, g_localizeStrings.Get(16011), false))
         return false;
-      if (m_dlgProgress && allowSelection != SELECTION_AUTO)
-      {
-        if (!CGUIKeyboardFactory::ShowAndGetInput(strTempAlbum, g_localizeStrings.Get(16011), false))
-          return false;
 
-        if (!CGUIKeyboardFactory::ShowAndGetInput(strTempArtist, g_localizeStrings.Get(16025), false))
-          return false;
-      }
-      else
-        needsRefresh = false;
+      CStdString strTempArtist(StringUtils::Join(album.artist, g_advancedSettings.m_musicItemSeparator));
+      if (!CGUIKeyboardFactory::ShowAndGetInput(strTempArtist, g_localizeStrings.Get(16025), false))
+        return false;
+      album.artist = StringUtils::Split(strTempArtist, g_advancedSettings.m_musicItemSeparator);
+      goto loop;
     }
-    else
-      needsRefresh = false;
   }
-  while (needsRefresh || bCanceled);
-
-  // Read the album information from the database if we are dealing with a DB album.
-  if (idAlbum != -1)
-    m_musicdatabase.GetAlbumInfo(idAlbum,album.GetAlbum(),&album.GetAlbum().songs);
+  else if (albumDownloadStatus == INFO_ADDED)
+  {
+    m_musicdatabase.SetAlbumInfo(params.GetAlbumId(), albumInfo.GetAlbum(), albumInfo.GetAlbum().songs);
+    albumInfo.SetLoaded(true);
+    return true;
+  }
 
-  album.SetLoaded(true);
-  return true;
+  return false;
 }
 
-bool CGUIWindowMusicBase::FindArtistInfo(const CStdString& strArtist, CMusicArtistInfo& artist, ALLOW_SELECTION allowSelection)
+// This is basically the same as the MusicInfoScanner->Process, with a different kind
+// of dialog, and should be merged.
+bool CGUIWindowMusicBase::FindArtistInfo(const CFileItem* item, CMusicArtistInfo& artistInfo, ALLOW_SELECTION allowSelection)
 {
+  CQueryParams params;
+  CDirectoryNode::GetDatabaseInfo(item->GetPath(), params);
+
+  // We can only ever be called with a database artist, so no need
+  // to check if we can convert the FileItem.
+  CArtist artist;
+  m_musicdatabase.GetArtistInfo(params.GetArtistId(), artist);
+  
   // show dialog box indicating we're searching the album
   if (m_dlgProgress && allowSelection != SELECTION_AUTO)
   {
     m_dlgProgress->SetHeading(21889);
-    m_dlgProgress->SetLine(0, strArtist);
+    m_dlgProgress->SetLine(0, artist.strArtist);
     m_dlgProgress->SetLine(1, "");
     m_dlgProgress->SetLine(2, "");
     m_dlgProgress->StartModal();
   }
 
+  // find album info
+  ADDON::ScraperPtr scraper;
+  if (!m_musicdatabase.GetScraperForPath(item->GetPath(), scraper, ADDON::ADDON_SCRAPER_ARTISTS) || !scraper)
+    return false;
+  
   CMusicInfoScanner scanner;
-  CStdString strPath;
-  CStdString strTempArtist(strArtist);
-  long idArtist = m_musicdatabase.GetArtistByName(strArtist);
-  strPath.Format("musicdb://artists/%u/",idArtist);
-
-  bool bCanceled(false);
-  bool needsRefresh(true);
-  do
+
+loop:
+  INFO_RET artistDownloadStatus = scanner.DownloadArtistInfo(artist, scraper, artistInfo, m_dlgProgress);
+  if (artistDownloadStatus == INFO_NOT_FOUND)
   {
-    if (!scanner.DownloadArtistInfo(strPath,strTempArtist,bCanceled,m_dlgProgress))
+    if (m_dlgProgress && allowSelection != SELECTION_AUTO)
     {
-      if (bCanceled)
+      if (!CGUIKeyboardFactory::ShowAndGetInput(artist.strArtist, g_localizeStrings.Get(16025), false))
         return false;
-      if (m_dlgProgress && allowSelection != SELECTION_AUTO)
-      {
-        if (!CGUIKeyboardFactory::ShowAndGetInput(strTempArtist, g_localizeStrings.Get(16025), false))
-          return false;
-      }
-      else
-        needsRefresh = false;
+      goto loop;
     }
-    else
-      needsRefresh = false;
   }
-  while (needsRefresh || bCanceled);
-
-  if (!m_musicdatabase.GetArtistInfo(idArtist,artist.GetArtist()))
-    return false;
+  else if (artistDownloadStatus == INFO_ADDED)
+  {
+    m_musicdatabase.SetArtistInfo(params.GetArtistId(), artistInfo.GetArtist());
+    artistInfo.SetLoaded();
+    return true;
+  }
 
-  artist.SetLoaded();
-  return true;
+  return false;
 }
 
 void CGUIWindowMusicBase::GetContextButtons(int itemNumber, CContextButtons &buttons)
@@ -1157,18 +1141,8 @@ void CGUIWindowMusicBase::UpdateThumb(const CAlbum &album, const CStdString &pat
     CFileItemList items;
     GetDirectory(albumPath, items);
     OnRetrieveMusicInfo(items);
-    VECSONGS songs;
-    for (int i = 0; i < items.Size(); i++)
-    {
-      CFileItemPtr item = items[i];
-      if (item->HasMusicInfoTag() && item->GetMusicInfoTag()->Loaded())
-      {
-        CSong song(*item->GetMusicInfoTag());
-        songs.push_back(song);
-      }
-    }
     VECALBUMS albums;
-    CMusicInfoScanner::CategoriseAlbums(songs, albums);
+    CMusicInfoScanner::FileItemsToAlbums(items, albums);
     if (albums.size() == 1)
     { // set as folder thumb as well
       CThumbLoader::SetCachedImage(items, "thumb", albumPath);
index c2e6b8f..bf9d4d2 100644 (file)
@@ -85,11 +85,11 @@ protected:
   void OnInfoAll(int iItem, bool bCurrent=false, bool refresh=false);
   virtual void OnQueueItem(int iItem);
   enum ALLOW_SELECTION { SELECTION_ALLOWED = 0, SELECTION_AUTO, SELECTION_FORCED };
-  bool FindAlbumInfo(const CStdString& strAlbum, const CStdString& strArtist, MUSIC_GRABBER::CMusicAlbumInfo& album, ALLOW_SELECTION allowSelection);
-  bool FindArtistInfo(const CStdString& strArtist, MUSIC_GRABBER::CMusicArtistInfo& artist, ALLOW_SELECTION allowSelection);
+  bool FindAlbumInfo(const CFileItem* album, MUSIC_GRABBER::CMusicAlbumInfo& albumInfo, ALLOW_SELECTION allowSelection);
+  bool FindArtistInfo(const CFileItem* artist, MUSIC_GRABBER::CMusicArtistInfo& artistInfo, ALLOW_SELECTION allowSelection);
 
-  void ShowAlbumInfo(const CAlbum& album, const CStdString& path, bool bShowInfo = true);
-  void ShowArtistInfo(const CArtist& artist, const CStdString& path, bool bShowInfo = true);
+  void ShowAlbumInfo(const CFileItem *pItem, bool bShowInfo = true);
+  void ShowArtistInfo(const CFileItem *pItem, bool bShowInfo = true);
   void ShowSongInfo(CFileItem* pItem);
   void UpdateThumb(const CAlbum &album, const CStdString &path);