changed: Add logic to properly handle subtitles for stacked files
[vuplus_xbmc] / xbmc / music / Album.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
21 #include "Album.h"
22 #include "settings/AdvancedSettings.h"
23 #include "utils/StringUtils.h"
24 #include "utils/XMLUtils.h"
25 #include "utils/MathUtils.h"
26 #include "FileItem.h"
27
28 using namespace std;
29 using namespace MUSIC_INFO;
30
31 CAlbum::CAlbum(const CFileItem& item)
32 {
33   Reset();
34   const CMusicInfoTag& tag = *item.GetMusicInfoTag();
35   SYSTEMTIME stTime;
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)
44   {
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);
50   }
51   iYear = stTime.wYear;
52   bCompilation = tag.GetCompilation();
53   iTimesPlayed = 0;
54 }
55
56 CStdString CAlbum::GetArtistString() const
57 {
58   return StringUtils::Join(artist, g_advancedSettings.m_musicItemSeparator);
59 }
60
61 CStdString CAlbum::GetGenreString() const
62 {
63   return StringUtils::Join(genre, g_advancedSettings.m_musicItemSeparator);
64 }
65
66 bool CAlbum::operator<(const CAlbum &a) const
67 {
68   if (strMusicBrainzAlbumID.empty() && a.strMusicBrainzAlbumID.empty())
69   {
70     if (strAlbum < a.strAlbum) return true;
71     if (strAlbum > a.strAlbum) return false;
72
73     // This will do an std::vector compare (i.e. item by item)
74     if (artist < a.artist) return true;
75     if (artist > a.artist) return false;
76     return false;
77   }
78
79   if (strMusicBrainzAlbumID < a.strMusicBrainzAlbumID) return true;
80   if (strMusicBrainzAlbumID > a.strMusicBrainzAlbumID) return false;
81   return false;
82 }
83
84 bool CAlbum::Load(const TiXmlElement *album, bool append, bool prioritise)
85 {
86   if (!album) return false;
87   if (!append)
88     Reset();
89
90   XMLUtils::GetString(album,              "title", strAlbum);
91   XMLUtils::GetString(album, "musicBrainzAlbumID", strMusicBrainzAlbumID);
92
93   XMLUtils::GetStringArray(album, "artist", artist, prioritise, g_advancedSettings.m_musicItemSeparator);
94   XMLUtils::GetStringArray(album, "genre", genre, prioritise, g_advancedSettings.m_musicItemSeparator);
95   XMLUtils::GetStringArray(album, "style", styles, prioritise, g_advancedSettings.m_musicItemSeparator);
96   XMLUtils::GetStringArray(album, "mood", moods, prioritise, g_advancedSettings.m_musicItemSeparator);
97   XMLUtils::GetStringArray(album, "theme", themes, prioritise, g_advancedSettings.m_musicItemSeparator);
98   XMLUtils::GetBoolean(album, "compilation", bCompilation);
99
100   XMLUtils::GetString(album,"review",strReview);
101   XMLUtils::GetString(album,"releasedate",m_strDateOfRelease);
102   XMLUtils::GetString(album,"label",strLabel);
103   XMLUtils::GetString(album,"type",strType);
104
105   XMLUtils::GetInt(album,"year",iYear);
106   const TiXmlElement* rElement = album->FirstChildElement("rating");
107   if (rElement)
108   {
109     float rating = 0;
110     float max_rating = 5;
111     XMLUtils::GetFloat(album, "rating", rating);
112     if (rElement->QueryFloatAttribute("max", &max_rating) == TIXML_SUCCESS && max_rating>=1)
113       rating *= (5.f / max_rating); // Normalise the Rating to between 0 and 5 
114     if (rating > 5.f)
115       rating = 5.f;
116     iRating = MathUtils::round_int(rating);
117   }
118
119   size_t iThumbCount = thumbURL.m_url.size();
120   CStdString xmlAdd = thumbURL.m_xml;
121   const TiXmlElement* thumb = album->FirstChildElement("thumb");
122   while (thumb)
123   {
124     thumbURL.ParseElement(thumb);
125     if (prioritise)
126     {
127       CStdString temp;
128       temp << *thumb;
129       xmlAdd = temp+xmlAdd;
130     }
131     thumb = thumb->NextSiblingElement("thumb");
132   }
133   // prioritise thumbs from nfos
134   if (prioritise && iThumbCount && iThumbCount != thumbURL.m_url.size())
135   {
136     rotate(thumbURL.m_url.begin(),
137            thumbURL.m_url.begin()+iThumbCount, 
138            thumbURL.m_url.end());
139     thumbURL.m_xml = xmlAdd;
140   }
141
142   const TiXmlElement* albumArtistCreditsNode = album->FirstChildElement("albumArtistCredits");
143   if (albumArtistCreditsNode)
144     artistCredits.clear();
145
146   while (albumArtistCreditsNode)
147   {
148     if (albumArtistCreditsNode->FirstChild())
149     {
150       CArtistCredit artistCredit;
151       XMLUtils::GetString(albumArtistCreditsNode,  "artist",               artistCredit.m_strArtist);
152       XMLUtils::GetString(albumArtistCreditsNode,  "musicBrainzArtistID",  artistCredit.m_strMusicBrainzArtistID);
153       XMLUtils::GetString(albumArtistCreditsNode,  "joinphrase",           artistCredit.m_strJoinPhrase);
154       XMLUtils::GetBoolean(albumArtistCreditsNode, "featuring",            artistCredit.m_boolFeatured);
155       artistCredits.push_back(artistCredit);
156     }
157
158     albumArtistCreditsNode = albumArtistCreditsNode->NextSiblingElement("albumArtistCredits");
159   }
160
161   // Support old style <artist></artist> for backwards compatibility
162   // .nfo files should ideally be updated to use the artist credits structure above
163   // or removed entirely in preference for better tags (MusicBrainz?)
164   if (artistCredits.empty() && !artist.empty())
165   {
166     for (vector<string>::const_iterator it = artist.begin(); it != artist.end(); ++it)
167     {
168       CArtistCredit artistCredit(*it, StringUtils::EmptyString,
169                                  it == --artist.end() ? StringUtils::EmptyString : g_advancedSettings.m_musicItemSeparator);
170       artistCredits.push_back(artistCredit);
171     }
172   }
173
174   const TiXmlElement* node = album->FirstChildElement("track");
175   if (node)
176     songs.clear();  // this means that the tracks can't be spread over separate pages
177                     // but this is probably a reasonable limitation
178   bool bIncrement = false;
179   while (node)
180   {
181     if (node->FirstChild())
182     {
183
184       CSong song;
185       const TiXmlElement* songArtistCreditsNode = node->FirstChildElement("songArtistCredits");
186       if (songArtistCreditsNode)
187         song.artistCredits.clear();
188       
189       while (songArtistCreditsNode)
190       {
191         if (songArtistCreditsNode->FirstChild())
192         {
193           CArtistCredit artistCredit;
194           XMLUtils::GetString(songArtistCreditsNode,  "artist",               artistCredit.m_strArtist);
195           XMLUtils::GetString(songArtistCreditsNode,  "musicBrainzArtistID",  artistCredit.m_strMusicBrainzArtistID);
196           XMLUtils::GetString(songArtistCreditsNode,  "joinphrase",           artistCredit.m_strJoinPhrase);
197           XMLUtils::GetBoolean(songArtistCreditsNode, "featuring",            artistCredit.m_boolFeatured);
198           song.artistCredits.push_back(artistCredit);
199         }
200         
201         songArtistCreditsNode = songArtistCreditsNode->NextSiblingElement("albumArtistCredits");
202       }
203
204       XMLUtils::GetString(node,   "musicBrainzTrackID",   song.strMusicBrainzTrackID);
205       XMLUtils::GetInt(node, "position", song.iTrack);
206
207       if (song.iTrack == 0)
208         bIncrement = true;
209
210       XMLUtils::GetString(node,"title",song.strTitle);
211       CStdString strDur;
212       XMLUtils::GetString(node,"duration",strDur);
213       song.iDuration = StringUtils::TimeStringToSeconds(strDur);
214
215       if (bIncrement)
216         song.iTrack = song.iTrack + 1;
217
218       songs.push_back(song);
219     }
220     node = node->NextSiblingElement("track");
221   }
222
223   return true;
224 }
225
226 bool CAlbum::Save(TiXmlNode *node, const CStdString &tag, const CStdString& strPath)
227 {
228   if (!node) return false;
229
230   // we start with a <tag> tag
231   TiXmlElement albumElement(tag.c_str());
232   TiXmlNode *album = node->InsertEndChild(albumElement);
233
234   if (!album) return false;
235
236   XMLUtils::SetString(album,                    "title", strAlbum);
237   XMLUtils::SetString(album,       "musicBrainzAlbumID", strMusicBrainzAlbumID);
238   XMLUtils::SetStringArray(album,              "artist", artist);
239   XMLUtils::SetStringArray(album,               "genre", genre);
240   XMLUtils::SetStringArray(album,               "style", styles);
241   XMLUtils::SetStringArray(album,                "mood", moods);
242   XMLUtils::SetStringArray(album,               "theme", themes);
243   XMLUtils::SetBoolean(album,      "compilation", bCompilation);
244
245   XMLUtils::SetString(album,      "review", strReview);
246   XMLUtils::SetString(album,        "type", strType);
247   XMLUtils::SetString(album, "releasedate", m_strDateOfRelease);
248   XMLUtils::SetString(album,       "label", strLabel);
249   XMLUtils::SetString(album,        "type", strType);
250   if (!thumbURL.m_xml.empty())
251   {
252     CXBMCTinyXML doc;
253     doc.Parse(thumbURL.m_xml);
254     const TiXmlNode* thumb = doc.FirstChild("thumb");
255     while (thumb)
256     {
257       album->InsertEndChild(*thumb);
258       thumb = thumb->NextSibling("thumb");
259     }
260   }
261   XMLUtils::SetString(album,        "path", strPath);
262
263   XMLUtils::SetInt(album,         "rating", iRating);
264   XMLUtils::SetInt(album,           "year", iYear);
265
266   for( VECARTISTCREDITS::const_iterator artistCredit = artistCredits.begin();artistCredit != artistCredits.end();++artistCredit)
267   {
268     // add an <albumArtistCredits> tag
269     TiXmlElement albumArtistCreditsElement("albumArtistCredits");
270     TiXmlNode *albumArtistCreditsNode = album->InsertEndChild(albumArtistCreditsElement);
271     XMLUtils::SetString(albumArtistCreditsNode,               "artist", artistCredit->m_strArtist);
272     XMLUtils::SetString(albumArtistCreditsNode,  "musicBrainzArtistID", artistCredit->m_strMusicBrainzArtistID);
273     XMLUtils::SetString(albumArtistCreditsNode,           "joinphrase", artistCredit->m_strJoinPhrase);
274     XMLUtils::SetString(albumArtistCreditsNode,            "featuring", artistCredit->GetArtist());
275   }
276
277   for( VECSONGS::const_iterator song = songs.begin(); song != songs.end(); ++song)
278   {
279     // add a <song> tag
280     TiXmlElement cast("track");
281     TiXmlNode *node = album->InsertEndChild(cast);
282     for( VECARTISTCREDITS::const_iterator artistCredit = song->artistCredits.begin(); artistCredit != song->artistCredits.end(); ++artistCredit)
283     {
284       // add an <albumArtistCredits> tag
285       TiXmlElement songArtistCreditsElement("songArtistCredits");
286       TiXmlNode *songArtistCreditsNode = node->InsertEndChild(songArtistCreditsElement);
287       XMLUtils::SetString(songArtistCreditsNode,               "artist", artistCredit->m_strArtist);
288       XMLUtils::SetString(songArtistCreditsNode,  "musicBrainzArtistID", artistCredit->m_strMusicBrainzArtistID);
289       XMLUtils::SetString(songArtistCreditsNode,           "joinphrase", artistCredit->m_strJoinPhrase);
290       XMLUtils::SetString(songArtistCreditsNode,            "featuring", artistCredit->GetArtist());
291     }
292     XMLUtils::SetString(node,   "musicBrainzTrackID",   song->strMusicBrainzTrackID);
293     XMLUtils::SetString(node,   "title",                song->strTitle);
294     XMLUtils::SetInt(node,      "position",             song->iTrack);
295     XMLUtils::SetString(node,   "duration",             StringUtils::SecondsToTimeString(song->iDuration));
296   }
297
298   return true;
299 }
300