1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "Artist.h"
10 
11 #include "ServiceBroker.h"
12 #include "settings/AdvancedSettings.h"
13 #include "settings/SettingsComponent.h"
14 #include "utils/Fanart.h"
15 #include "utils/XMLUtils.h"
16 
17 #include <algorithm>
18 
MergeScrapedArtist(const CArtist & source,bool override)19 void CArtist::MergeScrapedArtist(const CArtist& source, bool override /* = true */)
20 {
21   /*
22   Initial scraping of artist information when the mbid is derived from tags is done directly
23   using that ID, otherwise the lookup is based on name and can mis-identify the artist
24   (many have same name). It is useful to store the scraped mbid, but we need to be
25   able to correct any mistakes. Hence a manual refresh of artist information uses either
26   the mbid is derived from tags or the artist name, not any previously scraped mbid.
27 
28    A Musicbrainz artist ID derived from music file tags is always taken as accurate and so can
29    not be overwritten by a scraped value. When the artist does not already have an mbid or has
30    a previously scraped mbid, merge the new scraped value, flagging it as being from the
31    scraper rather than derived from music file tags.
32    */
33   if (!source.strMusicBrainzArtistID.empty() && (strMusicBrainzArtistID.empty() || bScrapedMBID))
34   {
35     strMusicBrainzArtistID = source.strMusicBrainzArtistID;
36     bScrapedMBID = true;
37   }
38 
39   if ((override && !source.strArtist.empty()) || strArtist.empty())
40     strArtist = source.strArtist;
41 
42   if ((override && !source.strSortName.empty()) || strSortName.empty())
43     strSortName = source.strSortName;
44 
45   strType = source.strType;
46   strGender = source.strGender;
47   strDisambiguation = source.strDisambiguation;
48   genre = source.genre;
49   strBiography = source.strBiography;
50   styles = source.styles;
51   moods = source.moods;
52   instruments = source.instruments;
53   strBorn = source.strBorn;
54   strFormed = source.strFormed;
55   strDied = source.strDied;
56   strDisbanded = source.strDisbanded;
57   yearsActive = source.yearsActive;
58 
59   thumbURL = source.thumbURL; // Available remote art
60   // Current artwork - thumb, fanart etc., to be stored in art table
61   if (!source.art.empty())
62     art = source.art;
63 
64   discography = source.discography;
65 }
66 
67 
Load(const TiXmlElement * artist,bool append,bool prioritise)68 bool CArtist::Load(const TiXmlElement *artist, bool append, bool prioritise)
69 {
70   if (!artist) return false;
71   if (!append)
72     Reset();
73 
74   const std::string itemSeparator = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_musicItemSeparator;
75 
76   XMLUtils::GetString(artist,                "name", strArtist);
77   XMLUtils::GetString(artist, "musicBrainzArtistID", strMusicBrainzArtistID);
78   XMLUtils::GetString(artist,            "sortname", strSortName);
79   XMLUtils::GetString(artist, "type", strType);
80   XMLUtils::GetString(artist, "gender", strGender);
81   XMLUtils::GetString(artist, "disambiguation", strDisambiguation);
82   XMLUtils::GetStringArray(artist,       "genre", genre, prioritise, itemSeparator);
83   XMLUtils::GetStringArray(artist,       "style", styles, prioritise, itemSeparator);
84   XMLUtils::GetStringArray(artist,        "mood", moods, prioritise, itemSeparator);
85   XMLUtils::GetStringArray(artist, "yearsactive", yearsActive, prioritise, itemSeparator);
86   XMLUtils::GetStringArray(artist, "instruments", instruments, prioritise, itemSeparator);
87 
88   XMLUtils::GetString(artist,      "born", strBorn);
89   XMLUtils::GetString(artist,    "formed", strFormed);
90   XMLUtils::GetString(artist, "biography", strBiography);
91   XMLUtils::GetString(artist,      "died", strDied);
92   XMLUtils::GetString(artist, "disbanded", strDisbanded);
93 
94   size_t iThumbCount = thumbURL.GetUrls().size();
95   std::string xmlAdd = thumbURL.GetData();
96 
97   // Available artist thumbs
98   const TiXmlElement* thumb = artist->FirstChildElement("thumb");
99   while (thumb)
100   {
101     thumbURL.ParseAndAppendUrl(thumb);
102     if (prioritise)
103     {
104       std::string temp;
105       temp << *thumb;
106       xmlAdd = temp+xmlAdd;
107     }
108     thumb = thumb->NextSiblingElement("thumb");
109   }
110   // prefix thumbs from nfos
111   if (prioritise && iThumbCount && iThumbCount != thumbURL.GetUrls().size())
112   {
113     auto thumbUrls = thumbURL.GetUrls();
114     rotate(thumbUrls.begin(), thumbUrls.begin() + iThumbCount, thumbUrls.end());
115     thumbURL.SetUrls(thumbUrls);
116     thumbURL.SetData(xmlAdd);
117   }
118 
119   // Discography
120   const TiXmlElement* node = artist->FirstChildElement("album");
121   if (node)
122     discography.clear();
123   while (node)
124   {
125     if (node->FirstChild())
126     {
127       CDiscoAlbum album;
128       XMLUtils::GetString(node, "title", album.strAlbum);
129       XMLUtils::GetString(node, "year", album.strYear);
130       XMLUtils::GetString(node, "musicbrainzreleasegroupid", album.strReleaseGroupMBID);
131       discography.push_back(album);
132     }
133     node = node->NextSiblingElement("album");
134   }
135 
136   // Support old style <fanart></fanart> for backwards compatibility of old nfo files and scrapers
137   const TiXmlElement *fanart2 = artist->FirstChildElement("fanart");
138   if (fanart2)
139   {
140     CFanart fanart;
141     // we prefix to handle mixed-mode nfo's with fanart set
142     if (prioritise)
143     {
144       std::string temp;
145       temp << *fanart2;
146       fanart.m_xml = temp+fanart.m_xml;
147     }
148     else
149       fanart.m_xml << *fanart2;
150     fanart.Unpack();
151     // Append fanart to other image URLs
152     for (unsigned int i = 0; i < fanart.GetNumFanarts(); i++)
153       thumbURL.AddParsedUrl(fanart.GetImageURL(i), "fanart", fanart.GetPreviewURL(i));
154   }
155 
156  // Current artwork  - thumb, fanart etc. (the chosen art, not the lists of those available)
157   node = artist->FirstChildElement("art");
158   if (node)
159   {
160     const TiXmlNode *artdetailNode = node->FirstChild();
161     while (artdetailNode && artdetailNode->FirstChild())
162     {
163       art.insert(make_pair(artdetailNode->ValueStr(), artdetailNode->FirstChild()->ValueStr()));
164       artdetailNode = artdetailNode->NextSibling();
165     }
166   }
167 
168   return true;
169 }
170 
Save(TiXmlNode * node,const std::string & tag,const std::string & strPath)171 bool CArtist::Save(TiXmlNode *node, const std::string &tag, const std::string& strPath)
172 {
173   if (!node) return false;
174 
175   // we start with a <tag> tag
176   TiXmlElement artistElement(tag.c_str());
177   TiXmlNode *artist = node->InsertEndChild(artistElement);
178 
179   if (!artist) return false;
180 
181   XMLUtils::SetString(artist,                      "name", strArtist);
182   XMLUtils::SetString(artist,       "musicBrainzArtistID", strMusicBrainzArtistID);
183   XMLUtils::SetString(artist,                  "sortname", strSortName);
184   XMLUtils::SetString(artist,                      "type", strType);
185   XMLUtils::SetString(artist,                    "gender", strGender);
186   XMLUtils::SetString(artist,            "disambiguation", strDisambiguation);
187   XMLUtils::SetStringArray(artist,                "genre", genre);
188   XMLUtils::SetStringArray(artist,                "style", styles);
189   XMLUtils::SetStringArray(artist,                 "mood", moods);
190   XMLUtils::SetStringArray(artist,          "yearsactive", yearsActive);
191   XMLUtils::SetStringArray(artist,          "instruments", instruments);
192   XMLUtils::SetString(artist,                      "born", strBorn);
193   XMLUtils::SetString(artist,                    "formed", strFormed);
194   XMLUtils::SetString(artist,                 "biography", strBiography);
195   XMLUtils::SetString(artist,                      "died", strDied);
196   XMLUtils::SetString(artist,                 "disbanded", strDisbanded);
197   // Available remote art
198   if (thumbURL.HasData())
199   {
200     CXBMCTinyXML doc;
201     doc.Parse(thumbURL.GetData());
202     const TiXmlNode* thumb = doc.FirstChild("thumb");
203     while (thumb)
204     {
205       artist->InsertEndChild(*thumb);
206       thumb = thumb->NextSibling("thumb");
207     }
208   }
209   XMLUtils::SetString(artist,        "path", strPath);
210 
211   // Discography
212   for (const auto& it : discography)
213   {
214     // add a <album> tag
215     TiXmlElement discoElement("album");
216     TiXmlNode* node = artist->InsertEndChild(discoElement);
217     XMLUtils::SetString(node, "title", it.strAlbum);
218     XMLUtils::SetString(node, "year", it.strYear);
219     XMLUtils::SetString(node, "musicbrainzreleasegroupid", it.strReleaseGroupMBID);
220   }
221 
222   return true;
223 }
224 
SetDateAdded(const std::string & strDateAdded)225 void CArtist::SetDateAdded(const std::string& strDateAdded)
226 {
227   dateAdded.SetFromDBDateTime(strDateAdded);
228 }
229 
SetDateUpdated(const std::string & strDateUpdated)230 void CArtist::SetDateUpdated(const std::string& strDateUpdated)
231 {
232   dateUpdated.SetFromDBDateTime(strDateUpdated);
233 }
234 
SetDateNew(const std::string & strDateNew)235 void CArtist::SetDateNew(const std::string& strDateNew)
236 {
237   dateNew.SetFromDBDateTime(strDateNew);
238 }
239 
240