[PVR][Estuary] Timer settings dialog: Show client name in timer type selection dialog...
[xbmc.git] / xbmc / music / Album.cpp
blobefaa7acf682b031b468b0a6a0cad1c2bccd46d49
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "Album.h"
11 #include "FileItem.h"
12 #include "ServiceBroker.h"
13 #include "music/tags/MusicInfoTag.h"
14 #include "settings/AdvancedSettings.h"
15 #include "settings/SettingsComponent.h"
16 #include "utils/MathUtils.h"
17 #include "utils/StringUtils.h"
18 #include "utils/XMLUtils.h"
19 #include "utils/log.h"
21 #include <algorithm>
23 using namespace MUSIC_INFO;
25 typedef struct ReleaseTypeInfo {
26 CAlbum::ReleaseType type;
27 std::string name;
28 } ReleaseTypeInfo;
30 ReleaseTypeInfo releaseTypes[] = {
31 { CAlbum::Album, "album" },
32 { CAlbum::Single, "single" }
35 CAlbum::CAlbum(const CFileItem& item)
37 Reset();
38 const CMusicInfoTag& tag = *item.GetMusicInfoTag();
39 strAlbum = tag.GetAlbum();
40 strMusicBrainzAlbumID = tag.GetMusicBrainzAlbumID();
41 strReleaseGroupMBID = tag.GetMusicBrainzReleaseGroupID();
42 genre = tag.GetGenre();
43 strArtistDesc = tag.GetAlbumArtistString();
44 //Set sort string before processing artist credits
45 strArtistSort = tag.GetAlbumArtistSort();
46 // Determine artist credits from various tag arrays, inc fallback to song artist names
47 SetArtistCredits(tag.GetAlbumArtist(), tag.GetMusicBrainzAlbumArtistHints(), tag.GetMusicBrainzAlbumArtistID(),
48 tag.GetArtist(), tag.GetMusicBrainzArtistHints(), tag.GetMusicBrainzArtistID());
50 strOrigReleaseDate = tag.GetOriginalDate();
51 strReleaseDate = tag.GetReleaseDate();
52 strLabel = tag.GetRecordLabel();
53 strType = tag.GetMusicBrainzReleaseType();
54 bCompilation = tag.GetCompilation();
55 iTimesPlayed = 0;
56 bBoxedSet = tag.GetBoxset();
57 dateAdded.Reset();
58 dateUpdated.Reset();
59 lastPlayed.Reset();
60 releaseType = tag.GetAlbumReleaseType();
61 strReleaseStatus = tag.GetAlbumReleaseStatus();
64 void CAlbum::SetArtistCredits(const std::vector<std::string>& names, const std::vector<std::string>& hints,
65 const std::vector<std::string>& mbids,
66 const std::vector<std::string>& artistnames, const std::vector<std::string>& artisthints,
67 const std::vector<std::string>& artistmbids)
69 std::vector<std::string> albumartistHints = hints;
70 //Split the artist sort string to try and get sort names for individual artists
71 auto artistSort = StringUtils::Split(strArtistSort, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_musicItemSeparator);
72 artistCredits.clear();
74 if (!mbids.empty())
75 { // Have musicbrainz artist info, so use it
77 // Vector of possible separators in the order least likely to be part of artist name
78 const std::vector<std::string> separators{ " feat. ", ";", ":", "|", "#", "/", ",", "&" };
80 // Establish tag consistency
81 // Do the number of musicbrainz ids and *both* number of names and number of hints mismatch?
82 if (albumartistHints.size() != mbids.size() && names.size() != mbids.size())
84 // Tags mismatch - report it and then try to fix
85 CLog::Log(LOGDEBUG, "Mismatch in song file albumartist tags: {} mbid {} name album: {} {}",
86 (int)mbids.size(), (int)names.size(), strAlbum, strArtistDesc);
88 Most likely we have no hints and a single artist name like "Artist1 feat. Artist2"
89 or "Composer; Conductor, Orchestra, Soloist" or "Artist1/Artist2" where the
90 expected single item separator (default = space-slash-space) as not been used.
91 Comma and slash (no spaces) are poor delimiters as could be in name e.g. AC/DC,
92 but here treat them as such in attempt to find artist names.
93 When there are hints they could be poorly formatted using unexpected separators,
94 so attempt to split them. Or we could have more hints or artist names than
95 musicbrainz so ignore them but raise warning.
98 // Do hints exist yet mismatch
99 if (!albumartistHints.empty() && albumartistHints.size() != mbids.size())
101 if (names.size() == mbids.size())
102 // Album artist name count matches, use that as hints
103 albumartistHints = names;
104 else if (albumartistHints.size() < mbids.size())
105 { // Try splitting the hints until have matching number
106 albumartistHints = StringUtils::SplitMulti(albumartistHints, separators, mbids.size());
108 else
109 // Extra hints, discard them.
110 albumartistHints.resize(mbids.size());
112 // Do hints not exist or still mismatch, try album artists
113 if (albumartistHints.size() != mbids.size())
114 albumartistHints = names;
115 // Still mismatch, try splitting the hints (now artists) until have matching number
116 if (albumartistHints.size() < mbids.size())
117 albumartistHints = StringUtils::SplitMulti(albumartistHints, separators, mbids.size());
118 // Try matching on artists or artist hints field, if it is reliable
119 if (albumartistHints.size() != mbids.size())
121 if (!artistmbids.empty() &&
122 (artistmbids.size() == artistnames.size() ||
123 artistmbids.size() == artisthints.size()))
125 for (size_t i = 0; i < mbids.size(); i++)
127 for (size_t j = 0; j < artistmbids.size(); j++)
129 if (mbids[i] == artistmbids[j])
131 if (albumartistHints.size() < i + 1)
132 albumartistHints.resize(i + 1);
133 if (artistmbids.size() == artisthints.size())
134 albumartistHints[i] = artisthints[j];
135 else
136 albumartistHints[i] = artistnames[j];
143 else
144 { // Either hints or album artists (or both) name matches number of musicbrainz id
145 // If hints mismatch, use album artists
146 if (albumartistHints.size() != mbids.size())
147 albumartistHints = names;
150 // Try to get number of artist sort names and musicbrainz ids to match. Split sort names
151 // further using multiple possible delimiters, over single separator applied in Tag loader
152 if (artistSort.size() != mbids.size())
153 artistSort = StringUtils::SplitMulti(artistSort, { ";", ":", "|", "#" });
155 for (size_t i = 0; i < mbids.size(); i++)
157 std::string artistId = mbids[i];
158 std::string artistName;
160 We try and get the musicbrainz id <-> name matching from the hints and match on the same index.
161 Some album artist hints could be blank (if populated from artist or artist hints).
162 If not found, use the musicbrainz id and hope we later on can update that entry.
163 If we have more names than musicbrainz id they are ignored, but raise a warning.
165 if (i < albumartistHints.size())
166 artistName = albumartistHints[i];
167 if (artistName.empty())
168 artistName = artistId;
170 // Use artist sort name providing we have as many as we have mbid,
171 // otherwise something is wrong with them so ignore and leave blank
172 if (artistSort.size() == mbids.size())
173 artistCredits.emplace_back(StringUtils::Trim(artistName), StringUtils::Trim(artistSort[i]), artistId);
174 else
175 artistCredits.emplace_back(StringUtils::Trim(artistName), "", artistId);
178 else
181 No musicbrainz album artist ids so fill artist names directly.
182 This method only called during scanning when there is a musicbrainz album id, so
183 means mbid tags are incomplete. But could also be called by JSON to SetAlbumDetails
184 Try to separate album artist names further, and trim blank space.
186 std::vector<std::string> albumArtists = names;
187 if (albumartistHints.size() > albumArtists.size())
188 // Make use of hints (ALBUMARTISTS tag), when present, to separate artist names
189 albumArtists = albumartistHints;
190 else
191 // Split album artist names further using multiple possible delimiters, over single separator applied in Tag loader
192 albumArtists = StringUtils::SplitMulti(albumArtists, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_musicArtistSeparators);
194 if (artistSort.size() != albumArtists.size())
195 // Split artist sort names further using multiple possible delimiters, over single separator applied in Tag loader
196 artistSort = StringUtils::SplitMulti(artistSort, { ";", ":", "|", "#" });
198 for (size_t i = 0; i < albumArtists.size(); i++)
200 artistCredits.emplace_back(StringUtils::Trim(albumArtists[i]));
201 // Set artist sort name providing we have as many as we have artists,
202 // otherwise something is wrong with them so ignore rather than guess.
203 if (artistSort.size() == albumArtists.size())
204 artistCredits.back().SetSortName(StringUtils::Trim(artistSort[i]));
209 void CAlbum::MergeScrapedAlbum(const CAlbum& source, bool override /* = true */)
212 Initial scraping of album information when there is a Musicbrainz album ID derived from
213 tags is done directly using that ID, otherwise the lookup is based on album and artist names
214 but this can sometimes mis-identify the album (i.e. classical music has many "Symphony No. 5").
215 It is useful to store the scraped mbid, but we need to be able to correct any mistakes. Hence
216 a manual refresh of album information uses either the mbid as derived from tags or the album
217 and artist names, not any previously scraped mbid.
219 When overwriting the data derived from tags, AND the original and scraped album have the same
220 Musicbrainz album ID, then merging is used to keep Kodi up to date with changes in the Musicbrainz
221 database including album artist credits, song artist credits and song titles. However it is only
222 appropriate when the music files are tagged with mbids, these are taken as definative, scraped
223 mbids can not be depended on in this way.
225 When the album is megerd in this deep way it is flagged so that the database album update is aware
226 artist credits and songs need to be updated too.
229 bArtistSongMerge = override && !bScrapedMBID
230 && !source.strMusicBrainzAlbumID.empty() && !strMusicBrainzAlbumID.empty()
231 && (strMusicBrainzAlbumID.compare(source.strMusicBrainzAlbumID) == 0);
234 Musicbrainz album (release) ID and release group ID values derived from music file tags are
235 always taken as accurate and so can not be overwritten by a scraped value. When the album does
236 not already have an mbid or has a previously scraped mbid, merge the new scraped value,
237 flagging it as being from the scraper rather than derived from music file tags.
239 if (!source.strMusicBrainzAlbumID.empty() && (strMusicBrainzAlbumID.empty() || bScrapedMBID))
241 strMusicBrainzAlbumID = source.strMusicBrainzAlbumID;
242 bScrapedMBID = true;
244 if (!source.strReleaseGroupMBID.empty() && (strReleaseGroupMBID.empty() || bScrapedMBID))
246 strReleaseGroupMBID = source.strReleaseGroupMBID;
250 Scraping can return different album artists from the originals derived from tags, even when
251 doing a lookup on artist name.
253 When overwriting the data derived from tags, AND the original and scraped album have the same
254 Musicbrainz album ID, then merging an album replaces both the album artsts and the song artists
255 with those scraped (providing they are not empty).
257 When not doing that kind of merge, for any matching artist names the Musicbrainz artist id
258 returned by the scraper can be used to populate any previously missing Musicbrainz artist id values.
260 if (bArtistSongMerge && !source.artistCredits.empty())
262 artistCredits = source.artistCredits; // Replace artists and store mbid returned by scraper
263 strArtistDesc.clear(); // @todo: set artist display string e.g. "artist1 & artist2" when scraped
265 else
267 // Compare original album artists with those scraped (ignoring order), and set any missing mbid
268 for (auto &artistCredit : artistCredits)
270 if (artistCredit.GetMusicBrainzArtistID().empty())
272 for (const auto& sourceartistCredit : source.artistCredits)
274 if (StringUtils::EqualsNoCase(artistCredit.GetArtist(), sourceartistCredit.GetArtist()))
276 artistCredit.SetMusicBrainzArtistID(sourceartistCredit.GetMusicBrainzArtistID());
277 artistCredit.SetScrapedMBID(true);
278 break;
285 //@todo: scraped album genre needs adding to genre and album_genre tables, this just changes the string
286 if ((override && !source.genre.empty()) || genre.empty())
287 genre = source.genre;
288 if ((override && !source.strAlbum.empty()) || strAlbum.empty())
289 strAlbum = source.strAlbum;
290 //@todo: validate ISO8601 format YYYY, YYYY-MM, or YYYY-MM-DD
291 if ((override && !source.strReleaseDate.empty()) || strReleaseDate.empty())
292 strReleaseDate = source.strReleaseDate;
293 if ((override && !source.strOrigReleaseDate.empty()) || strOrigReleaseDate.empty())
294 strOrigReleaseDate = source.strOrigReleaseDate;
296 if (override)
297 bCompilation = source.bCompilation;
298 // iTimesPlayed = source.iTimesPlayed; // times played is derived from songs
300 if ((override && !source.strArtistSort.empty()) || strArtistSort.empty())
301 strArtistSort = source.strArtistSort;
302 for (const auto& i : source.art)
304 if (override || art.find(i.first) == art.end())
305 art[i.first] = i.second;
307 if((override && !source.strLabel.empty()) || strLabel.empty())
308 strLabel = source.strLabel;
309 thumbURL = source.thumbURL;
310 moods = source.moods;
311 styles = source.styles;
312 themes = source.themes;
313 strReview = source.strReview;
314 if ((override && !source.strType.empty()) || strType.empty())
315 strType = source.strType;
316 // strPath = source.strPath; // don't merge the path
317 if ((override && !source.strReleaseStatus.empty()) || strReleaseStatus.empty())
318 strReleaseStatus = source.strReleaseStatus;
319 fRating = source.fRating;
320 iUserrating = source.iUserrating;
321 iVotes = source.iVotes;
324 When overwriting the data derived from tags, AND the original and scraped album have the same
325 Musicbrainz album ID, update the local songs with scaped Musicbrainz information including the
326 artist credits.
328 if (bArtistSongMerge)
330 for (auto &song : songs)
332 if (!song.strMusicBrainzTrackID.empty())
333 for (const auto& sourceSong : source.songs)
334 if ((sourceSong.strMusicBrainzTrackID == song.strMusicBrainzTrackID) && (sourceSong.iTrack == song.iTrack))
335 song.MergeScrapedSong(sourceSong, override);
340 std::string CAlbum::GetGenreString() const
342 return StringUtils::Join(genre, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_musicItemSeparator);
345 const std::vector<std::string> CAlbum::GetAlbumArtist() const
347 //Get artist names as vector from artist credits
348 std::vector<std::string> albumartists;
349 for (const auto& artistCredit : artistCredits)
351 albumartists.push_back(artistCredit.GetArtist());
353 return albumartists;
356 const std::vector<std::string> CAlbum::GetMusicBrainzAlbumArtistID() const
358 //Get artist MusicBrainz IDs as vector from artist credits
359 std::vector<std::string> musicBrainzID;
360 for (const auto& artistCredit : artistCredits)
362 musicBrainzID.push_back(artistCredit.GetMusicBrainzArtistID());
364 return musicBrainzID;
367 const std::string CAlbum::GetAlbumArtistString() const
369 //Artist description may be different from the artists in artistcredits (see ALBUMARTISTS tag processing)
370 //but is takes precedence as a string because artistcredits is not always filled during processing
371 if (!strArtistDesc.empty())
372 return strArtistDesc;
373 std::vector<std::string> artistvector;
374 for (const auto& i : artistCredits)
375 artistvector.emplace_back(i.GetArtist());
376 std::string artistString;
377 if (!artistvector.empty())
378 artistString = StringUtils::Join(artistvector, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_musicItemSeparator);
379 return artistString;
382 const std::string CAlbum::GetAlbumArtistSort() const
384 //The stored artist sort name string takes precedence but a
385 //value could be created from individual sort names held in artistcredits
386 if (!strArtistSort.empty())
387 return strArtistSort;
388 std::vector<std::string> artistvector;
389 for (const auto& artistcredit : artistCredits)
390 if (!artistcredit.GetSortName().empty())
391 artistvector.emplace_back(artistcredit.GetSortName());
392 std::string artistString;
393 if (!artistvector.empty())
394 artistString = StringUtils::Join(artistvector, "; ");
395 return artistString;
398 const std::vector<int> CAlbum::GetArtistIDArray() const
400 // Get album artist IDs for json rpc
401 std::vector<int> artistids;
402 for (const auto& artistCredit : artistCredits)
403 artistids.push_back(artistCredit.GetArtistId());
404 return artistids;
408 std::string CAlbum::GetReleaseType() const
410 return ReleaseTypeToString(releaseType);
413 void CAlbum::SetReleaseType(const std::string& strReleaseType)
415 releaseType = ReleaseTypeFromString(strReleaseType);
418 void CAlbum::SetDateAdded(const std::string& strDateAdded)
420 dateAdded.SetFromDBDateTime(strDateAdded);
423 void CAlbum::SetDateUpdated(const std::string& strDateUpdated)
425 dateUpdated.SetFromDBDateTime(strDateUpdated);
428 void CAlbum::SetDateNew(const std::string& strDateNew)
430 dateNew.SetFromDBDateTime(strDateNew);
433 void CAlbum::SetLastPlayed(const std::string& strLastPlayed)
435 lastPlayed.SetFromDBDateTime(strLastPlayed);
438 std::string CAlbum::ReleaseTypeToString(CAlbum::ReleaseType releaseType)
440 for (const ReleaseTypeInfo& releaseTypeInfo : releaseTypes)
442 if (releaseTypeInfo.type == releaseType)
443 return releaseTypeInfo.name;
446 return "album";
449 CAlbum::ReleaseType CAlbum::ReleaseTypeFromString(const std::string& strReleaseType)
451 for (const ReleaseTypeInfo& releaseTypeInfo : releaseTypes)
453 if (releaseTypeInfo.name == strReleaseType)
454 return releaseTypeInfo.type;
457 return Album;
460 bool CAlbum::operator<(const CAlbum &a) const
462 if (strMusicBrainzAlbumID.empty() && a.strMusicBrainzAlbumID.empty())
464 if (strAlbum < a.strAlbum) return true;
465 if (strAlbum > a.strAlbum) return false;
467 // This will do an std::vector compare (i.e. item by item)
468 if (GetAlbumArtist() < a.GetAlbumArtist()) return true;
469 if (GetAlbumArtist() > a.GetAlbumArtist()) return false;
470 return false;
473 if (strMusicBrainzAlbumID < a.strMusicBrainzAlbumID) return true;
474 if (strMusicBrainzAlbumID > a.strMusicBrainzAlbumID) return false;
475 return false;
478 bool CAlbum::Load(const TiXmlElement *album, bool append, bool prioritise)
480 if (!album) return false;
481 if (!append)
482 Reset();
484 const std::string itemSeparator = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_musicItemSeparator;
486 XMLUtils::GetString(album, "title", strAlbum);
487 XMLUtils::GetString(album, "musicbrainzalbumid", strMusicBrainzAlbumID);
488 XMLUtils::GetString(album, "musicbrainzreleasegroupid", strReleaseGroupMBID);
489 XMLUtils::GetBoolean(album, "scrapedmbid", bScrapedMBID);
490 XMLUtils::GetString(album, "artistdesc", strArtistDesc);
491 std::vector<std::string> artist; // Support old style <artist></artist> for backwards compatibility
492 XMLUtils::GetStringArray(album, "artist", artist, prioritise, itemSeparator);
493 XMLUtils::GetStringArray(album, "genre", genre, prioritise, itemSeparator);
494 XMLUtils::GetStringArray(album, "style", styles, prioritise, itemSeparator);
495 XMLUtils::GetStringArray(album, "mood", moods, prioritise, itemSeparator);
496 XMLUtils::GetStringArray(album, "theme", themes, prioritise, itemSeparator);
497 XMLUtils::GetBoolean(album, "compilation", bCompilation);
498 XMLUtils::GetBoolean(album, "boxset", bBoxedSet);
500 XMLUtils::GetString(album,"review",strReview);
501 XMLUtils::GetString(album,"label",strLabel);
502 XMLUtils::GetInt(album, "duration", iAlbumDuration);
503 XMLUtils::GetString(album,"type",strType);
504 XMLUtils::GetString(album, "releasestatus", strReleaseStatus);
506 XMLUtils::GetString(album, "releasedate", strReleaseDate);
507 StringUtils::Trim(strReleaseDate); // @todo: validate ISO8601 format
508 // Support old style <year></year> for backwards compatibility
509 if (strReleaseDate.empty())
511 int year;
512 XMLUtils::GetInt(album, "year", year);
513 if (year > 0)
514 strReleaseDate = StringUtils::Format("{:04}", year);
516 XMLUtils::GetString(album, "originalreleasedate", strOrigReleaseDate);
518 const TiXmlElement* rElement = album->FirstChildElement("rating");
519 if (rElement)
521 float rating = 0;
522 float max_rating = 10;
523 XMLUtils::GetFloat(album, "rating", rating);
524 if (rElement->QueryFloatAttribute("max", &max_rating) == TIXML_SUCCESS && max_rating>=1)
525 rating *= (10.f / max_rating); // Normalise the Rating to between 0 and 10
526 if (rating > 10.f)
527 rating = 10.f;
528 fRating = rating;
530 const TiXmlElement* userrating = album->FirstChildElement("userrating");
531 if (userrating)
533 float rating = 0;
534 float max_rating = 10;
535 XMLUtils::GetFloat(album, "userrating", rating);
536 if (userrating->QueryFloatAttribute("max", &max_rating) == TIXML_SUCCESS && max_rating >= 1)
537 rating *= (10.f / max_rating); // Normalise the Rating to between 0 and 10
538 if (rating > 10.f)
539 rating = 10.f;
540 iUserrating = MathUtils::round_int(static_cast<double>(rating));
542 XMLUtils::GetInt(album, "votes", iVotes);
544 size_t iThumbCount = thumbURL.GetUrls().size();
545 std::string xmlAdd = thumbURL.GetData();
546 const TiXmlElement* thumb = album->FirstChildElement("thumb");
547 while (thumb)
549 thumbURL.ParseAndAppendUrl(thumb);
550 if (prioritise)
552 std::string temp;
553 temp << *thumb;
554 xmlAdd = temp+xmlAdd;
556 thumb = thumb->NextSiblingElement("thumb");
558 // prioritise thumbs from nfos
559 if (prioritise && iThumbCount && iThumbCount != thumbURL.GetUrls().size())
561 auto thumbUrls = thumbURL.GetUrls();
562 rotate(thumbUrls.begin(), thumbUrls.begin() + iThumbCount, thumbUrls.end());
563 thumbURL.SetUrls(thumbUrls);
564 thumbURL.SetData(xmlAdd);
567 const TiXmlElement* albumArtistCreditsNode = album->FirstChildElement("albumArtistCredits");
568 if (albumArtistCreditsNode)
569 artistCredits.clear();
571 while (albumArtistCreditsNode)
573 if (albumArtistCreditsNode->FirstChild())
575 CArtistCredit artistCredit;
576 XMLUtils::GetString(albumArtistCreditsNode, "artist", artistCredit.m_strArtist);
577 XMLUtils::GetString(albumArtistCreditsNode, "musicBrainzArtistID", artistCredit.m_strMusicBrainzArtistID);
578 artistCredits.push_back(artistCredit);
581 albumArtistCreditsNode = albumArtistCreditsNode->NextSiblingElement("albumArtistCredits");
584 // Support old style <artist></artist> for backwards compatibility
585 // .nfo files should ideally be updated to use the artist credits structure above
586 // or removed entirely in preference for better tags (MusicBrainz?)
587 if (artistCredits.empty() && !artist.empty())
589 for (const auto& it : artist)
591 CArtistCredit artistCredit(it);
592 artistCredits.push_back(artistCredit);
596 std::string strReleaseType;
597 if (XMLUtils::GetString(album, "releasetype", strReleaseType))
598 SetReleaseType(strReleaseType);
599 else
600 releaseType = Album;
602 return true;
605 bool CAlbum::Save(TiXmlNode *node, const std::string &tag, const std::string& strPath)
607 if (!node) return false;
609 // we start with a <tag> tag
610 TiXmlElement albumElement(tag.c_str());
611 TiXmlNode *album = node->InsertEndChild(albumElement);
613 if (!album) return false;
615 XMLUtils::SetString(album, "title", strAlbum);
616 XMLUtils::SetString(album, "musicbrainzalbumid", strMusicBrainzAlbumID);
617 XMLUtils::SetString(album, "musicbrainzreleasegroupid", strReleaseGroupMBID);
618 XMLUtils::SetBoolean(album, "scrapedmbid", bScrapedMBID);
619 XMLUtils::SetString(album, "artistdesc", strArtistDesc); //Can be different from artist credits
620 XMLUtils::SetStringArray(album, "genre", genre);
621 XMLUtils::SetStringArray(album, "style", styles);
622 XMLUtils::SetStringArray(album, "mood", moods);
623 XMLUtils::SetStringArray(album, "theme", themes);
624 XMLUtils::SetBoolean(album, "compilation", bCompilation);
625 XMLUtils::SetBoolean(album, "boxset", bBoxedSet);
627 XMLUtils::SetString(album, "review", strReview);
628 XMLUtils::SetString(album, "type", strType);
629 XMLUtils::SetString(album, "releasestatus", strReleaseStatus);
630 XMLUtils::SetString(album, "releasedate", strReleaseDate);
631 XMLUtils::SetString(album, "originalreleasedate", strOrigReleaseDate);
632 XMLUtils::SetString(album, "label", strLabel);
633 XMLUtils::SetInt(album, "duration", iAlbumDuration);
634 if (thumbURL.HasData())
636 CXBMCTinyXML doc;
637 doc.Parse(thumbURL.GetData());
638 const TiXmlNode* thumb = doc.FirstChild("thumb");
639 while (thumb)
641 album->InsertEndChild(*thumb);
642 thumb = thumb->NextSibling("thumb");
645 XMLUtils::SetString(album, "path", strPath);
647 auto* rating = XMLUtils::SetFloat(album, "rating", fRating);
648 if (rating)
649 rating->ToElement()->SetAttribute("max", 10);
651 auto* userrating = XMLUtils::SetInt(album, "userrating", iUserrating);
652 if (userrating)
653 userrating->ToElement()->SetAttribute("max", 10);
655 XMLUtils::SetInt(album, "votes", iVotes);
657 for (const auto& artistCredit : artistCredits)
659 // add an <albumArtistCredits> tag
660 TiXmlElement albumArtistCreditsElement("albumArtistCredits");
661 TiXmlNode *albumArtistCreditsNode = album->InsertEndChild(albumArtistCreditsElement);
662 XMLUtils::SetString(albumArtistCreditsNode, "artist", artistCredit.m_strArtist);
663 XMLUtils::SetString(albumArtistCreditsNode, "musicBrainzArtistID",
664 artistCredit.m_strMusicBrainzArtistID);
667 XMLUtils::SetString(album, "releasetype", GetReleaseType());
669 return true;