2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
22 #include "settings/AdvancedSettings.h"
23 #include "utils/StringUtils.h"
24 #include "utils/XMLUtils.h"
25 #include "utils/MathUtils.h"
29 using namespace MUSIC_INFO;
31 CAlbum::CAlbum(const CFileItem& item)
34 const CMusicInfoTag& tag = *item.GetMusicInfoTag();
36 tag.GetReleaseDate(stTime);
37 strAlbum = tag.GetAlbum();
38 strMusicBrainzAlbumID = tag.GetMusicBrainzAlbumID();
39 genre = tag.GetGenre();
40 artist = tag.GetAlbumArtist();
41 if (!tag.GetMusicBrainzAlbumArtistID().empty())
42 { // have musicbrainz artist info, so use it
43 for (size_t i = 0; i < tag.GetMusicBrainzAlbumArtistID().size(); i++)
45 CStdString artistId = tag.GetMusicBrainzAlbumArtistID()[i];
46 CStdString artistName;
48 We try and get the corresponding artist name from the album artist tag.
49 We match on the same index, and if that fails just use the first name we have.
50 If no albumartist exists, try matching on artist if the MBArtistID matches.
53 artistName = (i < artist.size()) ? artist[i] : artist[0];
54 else if (!tag.GetMusicBrainzArtistID().empty() && !tag.GetArtist().empty())
56 vector<string>::const_iterator j = std::find(tag.GetMusicBrainzArtistID().begin(), tag.GetMusicBrainzArtistID().end(), artistId);
57 if (j != tag.GetMusicBrainzArtistID().end())
58 { // find corresponding artist
59 size_t d = std::distance(j,tag.GetMusicBrainzArtistID().begin());
60 artistName = (d < tag.GetArtist().size()) ? tag.GetArtist()[d] : tag.GetArtist()[0];
63 if (artistName.empty())
64 artistName = artistId;
65 CStdString strJoinPhrase = (i == tag.GetMusicBrainzAlbumArtistID().size()-1) ? "" : g_advancedSettings.m_musicItemSeparator;
66 CArtistCredit artistCredit(artistName, tag.GetMusicBrainzAlbumArtistID()[i], strJoinPhrase);
67 artistCredits.push_back(artistCredit);
71 { // no musicbrainz info, so fill in directly
72 for (vector<string>::const_iterator it = tag.GetAlbumArtist().begin(); it != tag.GetAlbumArtist().end(); ++it)
74 CStdString strJoinPhrase = (it == --tag.GetAlbumArtist().end() ? "" : g_advancedSettings.m_musicItemSeparator);
75 CArtistCredit artistCredit(*it, "", strJoinPhrase);
76 artistCredits.push_back(artistCredit);
80 bCompilation = tag.GetCompilation();
84 void CAlbum::MergeScrapedAlbum(const CAlbum& source, bool override /* = true */)
87 We don't merge musicbrainz album ID so that a refresh of album information
88 allows a lookup based on name rather than directly (re)using musicbrainz.
89 In future, we may wish to be able to override lookup by musicbrainz so
90 this might be dropped.
92 // strMusicBrainzAlbumID = source.strMusicBrainzAlbumID;
93 if ((override && !source.genre.empty()) || genre.empty())
95 if ((override && !source.strAlbum.empty()) || strAlbum.empty())
96 strAlbum = source.strAlbum;
97 if ((override && source.iYear > 0) || iYear == 0)
100 bCompilation = source.bCompilation;
101 // iTimesPlayed = source.iTimesPlayed; // times played is derived from songs
102 for (std::map<std::string, std::string>::const_iterator i = source.art.begin(); i != source.art.end(); ++i)
104 if (override || art.find(i->first) == art.end())
105 art[i->first] = i->second;
107 strLabel = source.strLabel;
108 thumbURL = source.thumbURL;
109 moods = source.moods;
110 styles = source.styles;
111 themes = source.themes;
112 strReview = source.strReview;
113 strType = source.strType;
114 // strPath = source.strPath; // don't merge the path
115 m_strDateOfRelease = source.m_strDateOfRelease;
116 iRating = source.iRating;
119 artistCredits = source.artistCredits;
120 artist = source.artist; // artist information is read-only from the database. artistCredits is what counts on scan
122 else if (source.artistCredits.size() > artistCredits.size())
123 artistCredits.insert(artistCredits.end(), source.artistCredits.begin()+artistCredits.size(), source.artistCredits.end());
124 if (!strMusicBrainzAlbumID.empty())
126 /* update local songs with MB information */
127 for (VECSONGS::iterator song = songs.begin(); song != songs.end(); ++song)
129 if (!song->strMusicBrainzTrackID.empty())
130 for (VECSONGS::const_iterator sourceSong = source.infoSongs.begin(); sourceSong != source.infoSongs.end(); ++sourceSong)
131 if (sourceSong->strMusicBrainzTrackID == song->strMusicBrainzTrackID)
132 song->MergeScrapedSong(*sourceSong, override);
135 infoSongs = source.infoSongs;
138 CStdString CAlbum::GetArtistString() const
140 return StringUtils::Join(artist, g_advancedSettings.m_musicItemSeparator);
143 CStdString CAlbum::GetGenreString() const
145 return StringUtils::Join(genre, g_advancedSettings.m_musicItemSeparator);
148 bool CAlbum::operator<(const CAlbum &a) const
150 if (strMusicBrainzAlbumID.empty() && a.strMusicBrainzAlbumID.empty())
152 if (strAlbum < a.strAlbum) return true;
153 if (strAlbum > a.strAlbum) return false;
155 // This will do an std::vector compare (i.e. item by item)
156 if (artist < a.artist) return true;
157 if (artist > a.artist) return false;
161 if (strMusicBrainzAlbumID < a.strMusicBrainzAlbumID) return true;
162 if (strMusicBrainzAlbumID > a.strMusicBrainzAlbumID) return false;
166 bool CAlbum::Load(const TiXmlElement *album, bool append, bool prioritise)
168 if (!album) return false;
172 XMLUtils::GetString(album, "title", strAlbum);
173 XMLUtils::GetString(album, "musicBrainzAlbumID", strMusicBrainzAlbumID);
175 XMLUtils::GetStringArray(album, "artist", artist, prioritise, g_advancedSettings.m_musicItemSeparator);
176 XMLUtils::GetStringArray(album, "genre", genre, prioritise, g_advancedSettings.m_musicItemSeparator);
177 XMLUtils::GetStringArray(album, "style", styles, prioritise, g_advancedSettings.m_musicItemSeparator);
178 XMLUtils::GetStringArray(album, "mood", moods, prioritise, g_advancedSettings.m_musicItemSeparator);
179 XMLUtils::GetStringArray(album, "theme", themes, prioritise, g_advancedSettings.m_musicItemSeparator);
180 XMLUtils::GetBoolean(album, "compilation", bCompilation);
182 XMLUtils::GetString(album,"review",strReview);
183 XMLUtils::GetString(album,"releasedate",m_strDateOfRelease);
184 XMLUtils::GetString(album,"label",strLabel);
185 XMLUtils::GetString(album,"type",strType);
187 XMLUtils::GetInt(album,"year",iYear);
188 const TiXmlElement* rElement = album->FirstChildElement("rating");
192 float max_rating = 5;
193 XMLUtils::GetFloat(album, "rating", rating);
194 if (rElement->QueryFloatAttribute("max", &max_rating) == TIXML_SUCCESS && max_rating>=1)
195 rating *= (5.f / max_rating); // Normalise the Rating to between 0 and 5
198 iRating = MathUtils::round_int(rating);
201 size_t iThumbCount = thumbURL.m_url.size();
202 CStdString xmlAdd = thumbURL.m_xml;
203 const TiXmlElement* thumb = album->FirstChildElement("thumb");
206 thumbURL.ParseElement(thumb);
211 xmlAdd = temp+xmlAdd;
213 thumb = thumb->NextSiblingElement("thumb");
215 // prioritise thumbs from nfos
216 if (prioritise && iThumbCount && iThumbCount != thumbURL.m_url.size())
218 rotate(thumbURL.m_url.begin(),
219 thumbURL.m_url.begin()+iThumbCount,
220 thumbURL.m_url.end());
221 thumbURL.m_xml = xmlAdd;
224 const TiXmlElement* albumArtistCreditsNode = album->FirstChildElement("albumArtistCredits");
225 if (albumArtistCreditsNode)
226 artistCredits.clear();
228 while (albumArtistCreditsNode)
230 if (albumArtistCreditsNode->FirstChild())
232 CArtistCredit artistCredit;
233 XMLUtils::GetString(albumArtistCreditsNode, "artist", artistCredit.m_strArtist);
234 XMLUtils::GetString(albumArtistCreditsNode, "musicBrainzArtistID", artistCredit.m_strMusicBrainzArtistID);
235 XMLUtils::GetString(albumArtistCreditsNode, "joinphrase", artistCredit.m_strJoinPhrase);
236 XMLUtils::GetBoolean(albumArtistCreditsNode, "featuring", artistCredit.m_boolFeatured);
237 artistCredits.push_back(artistCredit);
240 albumArtistCreditsNode = albumArtistCreditsNode->NextSiblingElement("albumArtistCredits");
243 // Support old style <artist></artist> for backwards compatibility
244 // .nfo files should ideally be updated to use the artist credits structure above
245 // or removed entirely in preference for better tags (MusicBrainz?)
246 if (artistCredits.empty() && !artist.empty())
248 for (vector<string>::const_iterator it = artist.begin(); it != artist.end(); ++it)
250 CArtistCredit artistCredit(*it, StringUtils::EmptyString,
251 it == --artist.end() ? StringUtils::EmptyString : g_advancedSettings.m_musicItemSeparator);
252 artistCredits.push_back(artistCredit);
256 const TiXmlElement* node = album->FirstChildElement("track");
258 infoSongs.clear(); // this means that the tracks can't be spread over separate pages
259 // but this is probably a reasonable limitation
260 bool bIncrement = false;
263 if (node->FirstChild())
267 const TiXmlElement* songArtistCreditsNode = node->FirstChildElement("songArtistCredits");
268 if (songArtistCreditsNode)
269 song.artistCredits.clear();
271 while (songArtistCreditsNode)
273 if (songArtistCreditsNode->FirstChild())
275 CArtistCredit artistCredit;
276 XMLUtils::GetString(songArtistCreditsNode, "artist", artistCredit.m_strArtist);
277 XMLUtils::GetString(songArtistCreditsNode, "musicBrainzArtistID", artistCredit.m_strMusicBrainzArtistID);
278 XMLUtils::GetString(songArtistCreditsNode, "joinphrase", artistCredit.m_strJoinPhrase);
279 XMLUtils::GetBoolean(songArtistCreditsNode, "featuring", artistCredit.m_boolFeatured);
280 song.artistCredits.push_back(artistCredit);
283 songArtistCreditsNode = songArtistCreditsNode->NextSiblingElement("songArtistCredits");
286 XMLUtils::GetString(node, "musicBrainzTrackID", song.strMusicBrainzTrackID);
287 XMLUtils::GetInt(node, "position", song.iTrack);
289 if (song.iTrack == 0)
292 XMLUtils::GetString(node,"title",song.strTitle);
294 XMLUtils::GetString(node,"duration",strDur);
295 song.iDuration = StringUtils::TimeStringToSeconds(strDur);
298 song.iTrack = song.iTrack + 1;
300 infoSongs.push_back(song);
302 node = node->NextSiblingElement("track");
308 bool CAlbum::Save(TiXmlNode *node, const CStdString &tag, const CStdString& strPath)
310 if (!node) return false;
312 // we start with a <tag> tag
313 TiXmlElement albumElement(tag.c_str());
314 TiXmlNode *album = node->InsertEndChild(albumElement);
316 if (!album) return false;
318 XMLUtils::SetString(album, "title", strAlbum);
319 XMLUtils::SetString(album, "musicBrainzAlbumID", strMusicBrainzAlbumID);
320 XMLUtils::SetStringArray(album, "artist", artist);
321 XMLUtils::SetStringArray(album, "genre", genre);
322 XMLUtils::SetStringArray(album, "style", styles);
323 XMLUtils::SetStringArray(album, "mood", moods);
324 XMLUtils::SetStringArray(album, "theme", themes);
325 XMLUtils::SetBoolean(album, "compilation", bCompilation);
327 XMLUtils::SetString(album, "review", strReview);
328 XMLUtils::SetString(album, "type", strType);
329 XMLUtils::SetString(album, "releasedate", m_strDateOfRelease);
330 XMLUtils::SetString(album, "label", strLabel);
331 XMLUtils::SetString(album, "type", strType);
332 if (!thumbURL.m_xml.empty())
335 doc.Parse(thumbURL.m_xml);
336 const TiXmlNode* thumb = doc.FirstChild("thumb");
339 album->InsertEndChild(*thumb);
340 thumb = thumb->NextSibling("thumb");
343 XMLUtils::SetString(album, "path", strPath);
345 XMLUtils::SetInt(album, "rating", iRating);
346 XMLUtils::SetInt(album, "year", iYear);
348 for( VECARTISTCREDITS::const_iterator artistCredit = artistCredits.begin();artistCredit != artistCredits.end();++artistCredit)
350 // add an <albumArtistCredits> tag
351 TiXmlElement albumArtistCreditsElement("albumArtistCredits");
352 TiXmlNode *albumArtistCreditsNode = album->InsertEndChild(albumArtistCreditsElement);
353 XMLUtils::SetString(albumArtistCreditsNode, "artist", artistCredit->m_strArtist);
354 XMLUtils::SetString(albumArtistCreditsNode, "musicBrainzArtistID", artistCredit->m_strMusicBrainzArtistID);
355 XMLUtils::SetString(albumArtistCreditsNode, "joinphrase", artistCredit->m_strJoinPhrase);
356 XMLUtils::SetString(albumArtistCreditsNode, "featuring", artistCredit->GetArtist());
359 for( VECSONGS::const_iterator song = infoSongs.begin(); song != infoSongs.end(); ++song)
362 TiXmlElement cast("track");
363 TiXmlNode *node = album->InsertEndChild(cast);
364 for( VECARTISTCREDITS::const_iterator artistCredit = song->artistCredits.begin(); artistCredit != song->artistCredits.end(); ++artistCredit)
366 // add an <albumArtistCredits> tag
367 TiXmlElement songArtistCreditsElement("songArtistCredits");
368 TiXmlNode *songArtistCreditsNode = node->InsertEndChild(songArtistCreditsElement);
369 XMLUtils::SetString(songArtistCreditsNode, "artist", artistCredit->m_strArtist);
370 XMLUtils::SetString(songArtistCreditsNode, "musicBrainzArtistID", artistCredit->m_strMusicBrainzArtistID);
371 XMLUtils::SetString(songArtistCreditsNode, "joinphrase", artistCredit->m_strJoinPhrase);
372 XMLUtils::SetString(songArtistCreditsNode, "featuring", artistCredit->GetArtist());
374 XMLUtils::SetString(node, "musicBrainzTrackID", song->strMusicBrainzTrackID);
375 XMLUtils::SetString(node, "title", song->strTitle);
376 XMLUtils::SetInt(node, "position", song->iTrack);
377 XMLUtils::SetString(node, "duration", StringUtils::SecondsToTimeString(song->iDuration));