2 * Copyright (C) 2005-2008 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, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
22 #include "LabelFormatter.h"
23 #include "GUISettings.h"
26 #include "VideoInfoTag.h"
27 #include "MusicInfoTag.h"
29 #include "StringUtils.h"
30 #include "LocalizeStrings.h"
32 using namespace MUSIC_INFO
;
37 * The purpose of this class is to parse a mask string of the form
39 * [%N. ][%T] - [%A][ (%Y)]
41 * and provide methods to format up a CFileItem's label(s).
43 * The %N/%A/%B masks are replaced with the corresponding metadata (if available).
45 * Square brackets are treated as a metadata block. Anything inside the block other
46 * than the metadata mask is treated as either a prefix or postfix to the metadata. This
47 * information is only included in the formatted string when the metadata is non-empty.
49 * Any metadata tags not enclosed with square brackets are treated as if it were immediately
50 * enclosed - i.e. with no prefix or postfix.
52 * The special characters %, [, and ] can be produced using %%, %[, and %] respectively.
54 * Any static text outside of the metadata blocks is only shown if the blocks on either side
55 * (or just one side in the case of an end) are both non-empty.
57 * Examples (using the above expression):
59 * Track Title Artist Year Resulting Label
60 * ----- ----- ------ ---- ---------------
61 * 10 "40" U2 1983 10. "40" - U2 (1983)
62 * "40" U2 1983 "40" - U2 (1983)
63 * 10 U2 1983 10. U2 (1983)
64 * 10 "40" 1983 "40" (1983)
65 * 10 "40" U2 10. "40" - U2
68 * Available metadata masks:
84 * %K - Movie/Game title
85 * %M - number of episodes
87 * %P - production code
88 * %H - season*100+episode
96 #define MASK_CHARS "NSATBGYFLDIJRCKMEPHZOQUX"
98 CLabelFormatter::CLabelFormatter(const CStdString
&mask
, const CStdString
&mask2
)
100 // assemble our label masks
101 AssembleMask(0, mask
);
102 AssembleMask(1, mask2
);
103 // save a bool for faster lookups
104 m_hideFileExtensions
= !g_guiSettings
.GetBool("filelists.showextensions");
107 CStdString
CLabelFormatter::GetContent(unsigned int label
, const CFileItem
*item
) const
110 assert(m_staticContent
[label
].size() == m_dynamicContent
[label
].size() + 1);
112 if (!item
) return "";
114 CStdString strLabel
, dynamicLeft
, dynamicRight
;
115 for (unsigned int i
= 0; i
< m_dynamicContent
[label
].size(); i
++)
117 dynamicRight
= GetMaskContent(m_dynamicContent
[label
][i
], item
);
118 if ((i
== 0 || !dynamicLeft
.IsEmpty()) && !dynamicRight
.IsEmpty())
119 strLabel
+= m_staticContent
[label
][i
];
120 strLabel
+= dynamicRight
;
121 dynamicLeft
= dynamicRight
;
123 if (!dynamicLeft
.IsEmpty())
124 strLabel
+= m_staticContent
[label
][m_dynamicContent
[label
].size()];
129 void CLabelFormatter::FormatLabel(CFileItem
*item
) const
131 CStdString maskedLabel
= GetContent(0, item
);
132 if (!maskedLabel
.IsEmpty())
133 item
->SetLabel(maskedLabel
);
134 else if (!item
->m_bIsFolder
&& m_hideFileExtensions
)
135 item
->RemoveExtension();
138 void CLabelFormatter::FormatLabel2(CFileItem
*item
) const
140 item
->SetLabel2(GetContent(1, item
));
143 CStdString
CLabelFormatter::GetMaskContent(const CMaskString
&mask
, const CFileItem
*item
) const
145 if (!item
) return "";
146 const CMusicInfoTag
*music
= item
->GetMusicInfoTag();
147 const CVideoInfoTag
*movie
= item
->GetVideoInfoTag();
149 switch (mask
.m_content
)
152 if (music
&& music
->GetTrackNumber() > 0)
153 value
.Format("%02.2i", music
->GetTrackNumber());
154 if (movie
&& movie
->m_iTrack
> 0)
155 value
.Format("%02.2i", movie
->m_iTrack
);
158 if (music
&& music
->GetDiscNumber() > 0)
159 value
.Format("%02.2i", music
->GetDiscNumber());
162 if (music
&& music
->GetArtist().size())
163 value
= music
->GetArtist();
164 if (movie
&& movie
->m_strArtist
.size())
165 value
= movie
->m_strArtist
;
168 if (music
&& music
->GetTitle().size())
169 value
= music
->GetTitle();
170 if (movie
&& movie
->m_strTitle
.size())
171 value
= movie
->m_strTitle
;
174 if (movie
&& !movie
->m_strShowTitle
.IsEmpty())
175 value
= movie
->m_strShowTitle
;
178 if (music
&& music
->GetAlbum().size())
179 value
= music
->GetAlbum();
181 value
= movie
->m_strAlbum
;
184 if (music
&& music
->GetGenre().size())
185 value
= music
->GetGenre();
186 if (movie
&& movie
->m_strGenre
.size())
187 value
= movie
->m_strGenre
;
191 value
= music
->GetYearString();
194 if (!movie
->m_strFirstAired
.IsEmpty())
195 value
= movie
->m_strFirstAired
;
196 else if (!movie
->m_strPremiered
.IsEmpty())
197 value
= movie
->m_strPremiered
;
198 else if (movie
->m_iYear
> 0)
199 value
.Format("%i",movie
->m_iYear
);
202 case 'F': // filename
203 value
= CUtil::GetTitleFromPath(item
->m_strPath
, item
->m_bIsFolder
&& !item
->IsFileFolder());
206 value
= item
->GetLabel();
207 // is the label the actual file or folder name?
208 if (value
== CUtil::GetFileName(item
->m_strPath
))
209 { // label is the same as filename, clean it up as appropriate
210 value
= CUtil::GetTitleFromPath(item
->m_strPath
, item
->m_bIsFolder
&& !item
->IsFileFolder());
217 nDuration
= music
->GetDuration();
220 if (movie
->m_streamDetails
.GetVideoDuration() > 0)
221 nDuration
= movie
->m_streamDetails
.GetVideoDuration();
222 else if (!movie
->m_strRuntime
.IsEmpty())
223 nDuration
= StringUtils::TimeStringToSeconds(movie
->m_strRuntime
);
226 value
= StringUtils::SecondsToTimeString(nDuration
);
227 else if (item
->m_dwSize
> 0)
228 value
= StringUtils::SizeToString(item
->m_dwSize
);
232 if( !item
->m_bIsFolder
|| item
->m_dwSize
!= 0 )
233 value
= StringUtils::SizeToString(item
->m_dwSize
);
236 if (item
->m_dateTime
.IsValid())
237 value
= item
->m_dateTime
.GetAsLocalizedDate();
240 if (item
->m_dateTime
.IsValid())
241 value
= item
->m_dateTime
.GetAsLocalizedTime("", false);
244 if (music
&& music
->GetRating() != '0')
245 value
= music
->GetRating();
246 else if (movie
&& movie
->m_fRating
!= 0.f
)
247 value
.Format("%.1f", movie
->m_fRating
);
249 case 'C': // programs count
250 value
.Format("%i", item
->m_iprogramCount
);
253 value
= item
->m_strTitle
;
256 if (movie
&& movie
->m_iEpisode
> 0)
257 value
.Format("%i %s", movie
->m_iEpisode
,g_localizeStrings
.Get(movie
->m_iEpisode
== 1 ? 20452 : 20453));
260 if (movie
&& movie
->m_iEpisode
> 0)
262 if (movie
->m_iSpecialSortEpisode
> 0)
263 value
.Format("S%02.2i", movie
->m_iEpisode
);
265 value
.Format("%02.2i", movie
->m_iEpisode
);
269 if (movie
) // tvshow production code
270 value
= movie
->m_strProductionCode
;
273 if (movie
&& movie
->m_iEpisode
> 0)
274 { // season*100+episode number
275 if (movie
->m_iSpecialSortSeason
> 0)
276 value
.Format("Sx%02.2i", movie
->m_iEpisode
);
278 value
.Format("%ix%02.2i", movie
->m_iSeason
,movie
->m_iEpisode
);
282 if (movie
&& movie
->m_strMPAARating
)
284 value
= movie
->m_strMPAARating
;
288 if (movie
&& movie
->m_strStudio
)
290 value
= movie
->m_strStudio
;
294 if( !item
->m_bIsFolder
|| item
->m_dwSize
!= 0 )
295 value
.Format("%i kbps", item
->m_dwSize
);
298 if (!value
.IsEmpty())
299 return mask
.m_prefix
+ value
+ mask
.m_postfix
;
303 void CLabelFormatter::SplitMask(unsigned int label
, const CStdString
&mask
)
307 reg
.RegComp("%([" MASK_CHARS
"])");
308 CStdString
work(mask
);
310 while ((findStart
= reg
.RegFind(work
.c_str())) >= 0)
311 { // we've found a match
312 m_staticContent
[label
].push_back(work
.Left(findStart
));
313 char* lp_tmp
= reg
.GetReplaceString("\\1");
314 m_dynamicContent
[label
].push_back(CMaskString("", *lp_tmp
, ""));
316 work
= work
.Mid(findStart
+ reg
.GetFindLen());
318 m_staticContent
[label
].push_back(work
);
321 void CLabelFormatter::AssembleMask(unsigned int label
, const CStdString
& mask
)
324 m_staticContent
[label
].clear();
325 m_dynamicContent
[label
].clear();
327 // we want to match [<prefix>%A<postfix]
328 // but allow %%, %[, %] to be in the prefix and postfix. Anything before the first [
329 // could be a mask that's not surrounded with [], so pass to SplitMask.
331 reg
.RegComp("(^|[^%])\\[(([^%]|%%|%\\]|%\\[)*)%([" MASK_CHARS
"])(([^%]|%%|%\\]|%\\[)*)\\]");
332 CStdString
work(mask
);
334 while ((findStart
= reg
.RegFind(work
.c_str())) >= 0)
335 { // we've found a match for a pre/postfixed string
337 char *s1
= reg
.GetReplaceString("\\1");
338 char *s2
= reg
.GetReplaceString("\\2");
339 char *s4
= reg
.GetReplaceString("\\4");
340 char *s5
= reg
.GetReplaceString("\\5");
341 SplitMask(label
, work
.Left(findStart
) + s1
);
342 m_dynamicContent
[label
].push_back(CMaskString(s2
, *s4
, s5
));
347 work
= work
.Mid(findStart
+ reg
.GetFindLen());
349 SplitMask(label
, work
);
350 assert(m_staticContent
[label
].size() == m_dynamicContent
[label
].size() + 1);
353 bool CLabelFormatter::FillMusicTag(const CStdString
&fileName
, CMusicInfoTag
*tag
) const
355 // run through and find static content to split the string up
356 int pos1
= fileName
.Find(m_staticContent
[0][0], 0);
357 if (pos1
== (int)CStdString::npos
)
359 for (unsigned int i
= 1; i
< m_staticContent
[0].size(); i
++)
361 int pos2
= m_staticContent
[0][i
].size() ? fileName
.Find(m_staticContent
[0][i
], pos1
) : fileName
.size();
362 if (pos2
== (int)CStdString::npos
)
364 // found static content - thus we have the dynamic content surrounded
365 FillMusicMaskContent(m_dynamicContent
[0][i
- 1].m_content
, fileName
.Mid(pos1
, pos2
- pos1
), tag
);
366 pos1
= pos2
+ m_staticContent
[0][i
].size();
371 void CLabelFormatter::FillMusicMaskContent(const char mask
, const CStdString
&value
, CMusicInfoTag
*tag
) const
377 tag
->SetTrackNumber(atol(value
.c_str()));
380 tag
->SetPartOfSet(atol(value
.c_str()));
383 tag
->SetArtist(value
);
386 tag
->SetTitle(value
);
389 tag
->SetAlbum(value
);
392 tag
->SetGenre(value
);
395 tag
->SetYear(atol(value
.c_str()));
398 tag
->SetDuration(StringUtils::TimeStringToSeconds(value
));
401 tag
->SetRating(value
[0]);