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 bool hasMusicBrainzAlbumArtist = !tag.GetMusicBrainzAlbumArtistID().empty();
42 const vector<string>& artists = hasMusicBrainzAlbumArtist ? tag.GetMusicBrainzAlbumArtistID() : tag.GetAlbumArtist();
43 for (vector<string>::const_iterator it = artists.begin(); it != artists.end(); ++it)
45 CStdString artistName = hasMusicBrainzAlbumArtist && !artist.empty() ? artist[0] : *it;
46 CStdString artistId = hasMusicBrainzAlbumArtist ? *it : StringUtils::EmptyString;
47 CStdString strJoinPhrase = (it == --artists.end() ? "" : g_advancedSettings.m_musicItemSeparator);
48 CArtistCredit artistCredit(artistName, artistId, strJoinPhrase);
49 artistCredits.push_back(artistCredit);
52 bCompilation = tag.GetCompilation();
56 void CAlbum::MergeScrapedAlbum(const CAlbum& source, bool override /* = true */)
59 We don't merge musicbrainz album ID so that a refresh of album information
60 allows a lookup based on name rather than directly (re)using musicbrainz.
61 In future, we may wish to be able to override lookup by musicbrainz so
62 this might be dropped.
64 // strMusicBrainzAlbumID = source.strMusicBrainzAlbumID;
65 if ((override && !source.genre.empty()) || genre.empty())
67 if ((override && !source.strAlbum.empty()) || strAlbum.empty())
68 strAlbum = source.strAlbum;
69 if ((override && source.iYear > 0) || iYear == 0)
72 bCompilation = source.bCompilation;
73 // iTimesPlayed = source.iTimesPlayed; // times played is derived from songs
74 for (std::map<std::string, std::string>::const_iterator i = source.art.begin(); i != source.art.end(); ++i)
76 if (override || art.find(i->first) == art.end())
77 art[i->first] = i->second;
79 strLabel = source.strLabel;
80 thumbURL = source.thumbURL;
82 styles = source.styles;
83 themes = source.themes;
84 strReview = source.strReview;
85 strType = source.strType;
86 // strPath = source.strPath; // don't merge the path
87 m_strDateOfRelease = source.m_strDateOfRelease;
88 iRating = source.iRating;
91 artistCredits = source.artistCredits;
92 artist = source.artist; // artist information is read-only from the database. artistCredits is what counts on scan
94 else if (source.artistCredits.size() > artistCredits.size())
95 artistCredits.insert(artistCredits.end(), source.artistCredits.begin()+artistCredits.size(), source.artistCredits.end());
96 if (!strMusicBrainzAlbumID.empty())
98 /* update local songs with MB information */
99 for (VECSONGS::iterator song = songs.begin(); song != songs.end(); ++song)
101 if (!song->strMusicBrainzTrackID.empty())
102 for (VECSONGS::const_iterator sourceSong = source.infoSongs.begin(); sourceSong != source.infoSongs.end(); ++sourceSong)
103 if (sourceSong->strMusicBrainzTrackID == song->strMusicBrainzTrackID)
104 song->MergeScrapedSong(*sourceSong, override);
107 infoSongs = source.infoSongs;
110 CStdString CAlbum::GetArtistString() const
112 return StringUtils::Join(artist, g_advancedSettings.m_musicItemSeparator);
115 CStdString CAlbum::GetGenreString() const
117 return StringUtils::Join(genre, g_advancedSettings.m_musicItemSeparator);
120 bool CAlbum::operator<(const CAlbum &a) const
122 if (strMusicBrainzAlbumID.empty() && a.strMusicBrainzAlbumID.empty())
124 if (strAlbum < a.strAlbum) return true;
125 if (strAlbum > a.strAlbum) return false;
127 // This will do an std::vector compare (i.e. item by item)
128 if (artist < a.artist) return true;
129 if (artist > a.artist) return false;
133 if (strMusicBrainzAlbumID < a.strMusicBrainzAlbumID) return true;
134 if (strMusicBrainzAlbumID > a.strMusicBrainzAlbumID) return false;
138 bool CAlbum::Load(const TiXmlElement *album, bool append, bool prioritise)
140 if (!album) return false;
144 XMLUtils::GetString(album, "title", strAlbum);
145 XMLUtils::GetString(album, "musicBrainzAlbumID", strMusicBrainzAlbumID);
147 XMLUtils::GetStringArray(album, "artist", artist, prioritise, g_advancedSettings.m_musicItemSeparator);
148 XMLUtils::GetStringArray(album, "genre", genre, prioritise, g_advancedSettings.m_musicItemSeparator);
149 XMLUtils::GetStringArray(album, "style", styles, prioritise, g_advancedSettings.m_musicItemSeparator);
150 XMLUtils::GetStringArray(album, "mood", moods, prioritise, g_advancedSettings.m_musicItemSeparator);
151 XMLUtils::GetStringArray(album, "theme", themes, prioritise, g_advancedSettings.m_musicItemSeparator);
152 XMLUtils::GetBoolean(album, "compilation", bCompilation);
154 XMLUtils::GetString(album,"review",strReview);
155 XMLUtils::GetString(album,"releasedate",m_strDateOfRelease);
156 XMLUtils::GetString(album,"label",strLabel);
157 XMLUtils::GetString(album,"type",strType);
159 XMLUtils::GetInt(album,"year",iYear);
160 const TiXmlElement* rElement = album->FirstChildElement("rating");
164 float max_rating = 5;
165 XMLUtils::GetFloat(album, "rating", rating);
166 if (rElement->QueryFloatAttribute("max", &max_rating) == TIXML_SUCCESS && max_rating>=1)
167 rating *= (5.f / max_rating); // Normalise the Rating to between 0 and 5
170 iRating = MathUtils::round_int(rating);
173 size_t iThumbCount = thumbURL.m_url.size();
174 CStdString xmlAdd = thumbURL.m_xml;
175 const TiXmlElement* thumb = album->FirstChildElement("thumb");
178 thumbURL.ParseElement(thumb);
183 xmlAdd = temp+xmlAdd;
185 thumb = thumb->NextSiblingElement("thumb");
187 // prioritise thumbs from nfos
188 if (prioritise && iThumbCount && iThumbCount != thumbURL.m_url.size())
190 rotate(thumbURL.m_url.begin(),
191 thumbURL.m_url.begin()+iThumbCount,
192 thumbURL.m_url.end());
193 thumbURL.m_xml = xmlAdd;
196 const TiXmlElement* albumArtistCreditsNode = album->FirstChildElement("albumArtistCredits");
197 if (albumArtistCreditsNode)
198 artistCredits.clear();
200 while (albumArtistCreditsNode)
202 if (albumArtistCreditsNode->FirstChild())
204 CArtistCredit artistCredit;
205 XMLUtils::GetString(albumArtistCreditsNode, "artist", artistCredit.m_strArtist);
206 XMLUtils::GetString(albumArtistCreditsNode, "musicBrainzArtistID", artistCredit.m_strMusicBrainzArtistID);
207 XMLUtils::GetString(albumArtistCreditsNode, "joinphrase", artistCredit.m_strJoinPhrase);
208 XMLUtils::GetBoolean(albumArtistCreditsNode, "featuring", artistCredit.m_boolFeatured);
209 artistCredits.push_back(artistCredit);
212 albumArtistCreditsNode = albumArtistCreditsNode->NextSiblingElement("albumArtistCredits");
215 // Support old style <artist></artist> for backwards compatibility
216 // .nfo files should ideally be updated to use the artist credits structure above
217 // or removed entirely in preference for better tags (MusicBrainz?)
218 if (artistCredits.empty() && !artist.empty())
220 for (vector<string>::const_iterator it = artist.begin(); it != artist.end(); ++it)
222 CArtistCredit artistCredit(*it, StringUtils::EmptyString,
223 it == --artist.end() ? StringUtils::EmptyString : g_advancedSettings.m_musicItemSeparator);
224 artistCredits.push_back(artistCredit);
228 const TiXmlElement* node = album->FirstChildElement("track");
230 infoSongs.clear(); // this means that the tracks can't be spread over separate pages
231 // but this is probably a reasonable limitation
232 bool bIncrement = false;
235 if (node->FirstChild())
239 const TiXmlElement* songArtistCreditsNode = node->FirstChildElement("songArtistCredits");
240 if (songArtistCreditsNode)
241 song.artistCredits.clear();
243 while (songArtistCreditsNode)
245 if (songArtistCreditsNode->FirstChild())
247 CArtistCredit artistCredit;
248 XMLUtils::GetString(songArtistCreditsNode, "artist", artistCredit.m_strArtist);
249 XMLUtils::GetString(songArtistCreditsNode, "musicBrainzArtistID", artistCredit.m_strMusicBrainzArtistID);
250 XMLUtils::GetString(songArtistCreditsNode, "joinphrase", artistCredit.m_strJoinPhrase);
251 XMLUtils::GetBoolean(songArtistCreditsNode, "featuring", artistCredit.m_boolFeatured);
252 song.artistCredits.push_back(artistCredit);
255 songArtistCreditsNode = songArtistCreditsNode->NextSiblingElement("songArtistCredits");
258 XMLUtils::GetString(node, "musicBrainzTrackID", song.strMusicBrainzTrackID);
259 XMLUtils::GetInt(node, "position", song.iTrack);
261 if (song.iTrack == 0)
264 XMLUtils::GetString(node,"title",song.strTitle);
266 XMLUtils::GetString(node,"duration",strDur);
267 song.iDuration = StringUtils::TimeStringToSeconds(strDur);
270 song.iTrack = song.iTrack + 1;
272 infoSongs.push_back(song);
274 node = node->NextSiblingElement("track");
280 bool CAlbum::Save(TiXmlNode *node, const CStdString &tag, const CStdString& strPath)
282 if (!node) return false;
284 // we start with a <tag> tag
285 TiXmlElement albumElement(tag.c_str());
286 TiXmlNode *album = node->InsertEndChild(albumElement);
288 if (!album) return false;
290 XMLUtils::SetString(album, "title", strAlbum);
291 XMLUtils::SetString(album, "musicBrainzAlbumID", strMusicBrainzAlbumID);
292 XMLUtils::SetStringArray(album, "artist", artist);
293 XMLUtils::SetStringArray(album, "genre", genre);
294 XMLUtils::SetStringArray(album, "style", styles);
295 XMLUtils::SetStringArray(album, "mood", moods);
296 XMLUtils::SetStringArray(album, "theme", themes);
297 XMLUtils::SetBoolean(album, "compilation", bCompilation);
299 XMLUtils::SetString(album, "review", strReview);
300 XMLUtils::SetString(album, "type", strType);
301 XMLUtils::SetString(album, "releasedate", m_strDateOfRelease);
302 XMLUtils::SetString(album, "label", strLabel);
303 XMLUtils::SetString(album, "type", strType);
304 if (!thumbURL.m_xml.empty())
307 doc.Parse(thumbURL.m_xml);
308 const TiXmlNode* thumb = doc.FirstChild("thumb");
311 album->InsertEndChild(*thumb);
312 thumb = thumb->NextSibling("thumb");
315 XMLUtils::SetString(album, "path", strPath);
317 XMLUtils::SetInt(album, "rating", iRating);
318 XMLUtils::SetInt(album, "year", iYear);
320 for( VECARTISTCREDITS::const_iterator artistCredit = artistCredits.begin();artistCredit != artistCredits.end();++artistCredit)
322 // add an <albumArtistCredits> tag
323 TiXmlElement albumArtistCreditsElement("albumArtistCredits");
324 TiXmlNode *albumArtistCreditsNode = album->InsertEndChild(albumArtistCreditsElement);
325 XMLUtils::SetString(albumArtistCreditsNode, "artist", artistCredit->m_strArtist);
326 XMLUtils::SetString(albumArtistCreditsNode, "musicBrainzArtistID", artistCredit->m_strMusicBrainzArtistID);
327 XMLUtils::SetString(albumArtistCreditsNode, "joinphrase", artistCredit->m_strJoinPhrase);
328 XMLUtils::SetString(albumArtistCreditsNode, "featuring", artistCredit->GetArtist());
331 for( VECSONGS::const_iterator song = infoSongs.begin(); song != infoSongs.end(); ++song)
334 TiXmlElement cast("track");
335 TiXmlNode *node = album->InsertEndChild(cast);
336 for( VECARTISTCREDITS::const_iterator artistCredit = song->artistCredits.begin(); artistCredit != song->artistCredits.end(); ++artistCredit)
338 // add an <albumArtistCredits> tag
339 TiXmlElement songArtistCreditsElement("songArtistCredits");
340 TiXmlNode *songArtistCreditsNode = node->InsertEndChild(songArtistCreditsElement);
341 XMLUtils::SetString(songArtistCreditsNode, "artist", artistCredit->m_strArtist);
342 XMLUtils::SetString(songArtistCreditsNode, "musicBrainzArtistID", artistCredit->m_strMusicBrainzArtistID);
343 XMLUtils::SetString(songArtistCreditsNode, "joinphrase", artistCredit->m_strJoinPhrase);
344 XMLUtils::SetString(songArtistCreditsNode, "featuring", artistCredit->GetArtist());
346 XMLUtils::SetString(node, "musicBrainzTrackID", song->strMusicBrainzTrackID);
347 XMLUtils::SetString(node, "title", song->strTitle);
348 XMLUtils::SetInt(node, "position", song->iTrack);
349 XMLUtils::SetString(node, "duration", StringUtils::SecondsToTimeString(song->iDuration));