1 /***************************************************************************
2 * Copyright (C) 2008-2014 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
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 of the License, or *
8 * (at your option) any later version. *
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 this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
23 #include <boost/format.hpp>
24 #include <boost/lexical_cast.hpp>
29 #include "utility/type_conversions.h"
30 #include "utility/wide_string.h"
35 size_t calc_hash(const char* s
, unsigned seed
= 0)
39 hash
= hash
* 101 + *s
++;
47 std::string
Song::get(mpd_tag_type type
, unsigned idx
) const
50 const char *tag
= mpd_song_get_tag(m_song
.get(), type
, idx
);
56 Song::Song(mpd_song
*s
)
59 m_song
= std::shared_ptr
<mpd_song
>(s
, mpd_song_free
);
60 m_hash
= calc_hash(mpd_song_get_uri(s
));
63 std::string
Song::getURI(unsigned idx
) const
69 return mpd_song_get_uri(m_song
.get());
72 std::string
Song::getName(unsigned idx
) const
75 mpd_song
*s
= m_song
.get();
76 const char *res
= mpd_song_get_tag(s
, MPD_TAG_NAME
, idx
);
79 const char *uri
= mpd_song_get_uri(s
);
80 const char *name
= strrchr(uri
, '/');
87 std::string
Song::getDirectory(unsigned idx
) const
90 if (idx
> 0 || isStream())
92 const char *uri
= mpd_song_get_uri(m_song
.get());
93 const char *name
= strrchr(uri
, '/');
95 return std::string(uri
, name
-uri
);
100 std::string
Song::getArtist(unsigned idx
) const
103 return get(MPD_TAG_ARTIST
, idx
);
106 std::string
Song::getTitle(unsigned idx
) const
109 return get(MPD_TAG_TITLE
, idx
);
112 std::string
Song::getAlbum(unsigned idx
) const
115 return get(MPD_TAG_ALBUM
, idx
);
118 std::string
Song::getAlbumArtist(unsigned idx
) const
121 return get(MPD_TAG_ALBUM_ARTIST
, idx
);
124 std::string
Song::getTrack(unsigned idx
) const
127 std::string track
= get(MPD_TAG_TRACK
, idx
);
128 if ((track
.length() == 1 && track
[0] != '0')
129 || (track
.length() > 3 && track
[1] == '/'))
134 std::string
Song::getTrackNumber(unsigned idx
) const
137 std::string track
= getTrack(idx
);
138 size_t slash
= track
.find('/');
139 if (slash
!= std::string::npos
)
144 std::string
Song::getDate(unsigned idx
) const
147 return get(MPD_TAG_DATE
, idx
);
150 std::string
Song::getGenre(unsigned idx
) const
153 return get(MPD_TAG_GENRE
, idx
);
156 std::string
Song::getComposer(unsigned idx
) const
159 return get(MPD_TAG_COMPOSER
, idx
);
162 std::string
Song::getPerformer(unsigned idx
) const
165 return get(MPD_TAG_PERFORMER
, idx
);
168 std::string
Song::getDisc(unsigned idx
) const
171 return get(MPD_TAG_DISC
, idx
);
174 std::string
Song::getComment(unsigned idx
) const
177 return get(MPD_TAG_COMMENT
, idx
);
180 std::string
Song::getLength(unsigned idx
) const
185 unsigned len
= getDuration();
187 return ShowTime(len
);
192 std::string
Song::getPriority(unsigned idx
) const
197 return boost::lexical_cast
<std::string
>(getPrio());
200 std::string
MPD::Song::getTags(GetFunction f
, const std::string
&tags_separator
) const
205 for (std::string tag
; !(tag
= (this->*f
)(idx
)).empty(); ++idx
)
208 result
+= tags_separator
;
214 unsigned Song::getDuration() const
217 return mpd_song_get_duration(m_song
.get());
220 unsigned Song::getPosition() const
223 return mpd_song_get_pos(m_song
.get());
226 unsigned Song::getID() const
229 return mpd_song_get_id(m_song
.get());
232 unsigned Song::getPrio() const
235 return mpd_song_get_prio(m_song
.get());
238 time_t Song::getMTime() const
241 return mpd_song_get_last_modified(m_song
.get());
244 bool Song::isFromDatabase() const
247 const char *uri
= mpd_song_get_uri(m_song
.get());
248 return uri
[0] != '/' || !strrchr(uri
, '/');
251 bool Song::isStream() const
254 return !strncmp(mpd_song_get_uri(m_song
.get()), "http://", 7);
257 bool Song::empty() const
259 return m_song
.get() == 0;
262 std::string
Song::toString(const std::string
&fmt
, const std::string
&tags_separator
, const std::string
&escape_chars
) const
265 std::string::const_iterator it
= fmt
.begin();
266 return ParseFormat(it
, tags_separator
, escape_chars
);
269 std::string
Song::ShowTime(unsigned length
)
271 int hours
= length
/3600;
272 length
-= hours
*3600;
273 int minutes
= length
/60;
274 length
-= minutes
*60;
275 int seconds
= length
;
279 result
= (boost::format("%d:%02d:%02d") % hours
% minutes
% seconds
).str();
281 result
= (boost::format("%d:%02d") % minutes
% seconds
).str();
285 void MPD::Song::validateFormat(const std::string
&fmt
)
288 for (std::string::const_iterator it
= fmt
.begin(); it
!= fmt
.end(); ++it
)
296 throw std::runtime_error("number of opening and closing braces is not equal");
298 for (size_t i
= fmt
.find('%'); i
!= std::string::npos
; i
= fmt
.find('%', i
))
300 if (isdigit(fmt
[++i
]))
301 while (isdigit(fmt
[++i
])) { }
302 if (!charToGetFunction(fmt
[i
]))
303 throw std::runtime_error(
304 (boost::format("invalid character at position %1%: %2%") % (i
+1) % fmt
[i
]).str()
309 std::string
Song::ParseFormat(std::string::const_iterator
&it
, const std::string
&tags_separator
,
310 const std::string
&escape_chars
) const
313 bool has_some_tags
= 0;
314 MPD::Song::GetFunction get_fun
= 0;
319 std::string tags
= ParseFormat(it
, tags_separator
, escape_chars
);
331 size_t delimiter
= 0;
334 delimiter
= atol(&*it
);
335 while (isdigit(*++it
)) { }
344 get_fun
= charToGetFunction(*it
);
348 std::string tag
= getTags(get_fun
, tags_separator
);
349 if (!escape_chars
.empty()) // prepend format escape character to all given chars to escape
351 for (size_t i
= 0; i
< escape_chars
.length(); ++i
)
352 for (size_t j
= 0; (j
= tag
.find(escape_chars
[i
], j
)) != std::string::npos
; j
+= 2)
353 tag
.replace(j
, 1, std::string(1, FormatEscapeCharacter
) + escape_chars
[i
]);
355 if (!tag
.empty() && (get_fun
!= &MPD::Song::getLength
|| getDuration() > 0))
357 if (delimiter
&& tag
.size() > delimiter
)
359 // Shorten length string by just chopping off the tail
360 if (get_fun
== &MPD::Song::getLength
)
361 tag
= tag
.substr(0, delimiter
);
363 tag
= ToString(wideShorten(ToWString(tag
), delimiter
));
375 int brace_counter
= 0;
376 if (*it
!= '}' || !has_some_tags
)
378 for (; *it
!= '}' || brace_counter
; ++it
)
386 return ParseFormat(++it
, tags_separator
, escape_chars
);
394 for (; *it
!= '}' || *(it
+1) == '|' || brace_counter
; ++it
)