1 /***************************************************************************
2 * Copyright (C) 2008-2017 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/functional/hash.hpp>
25 #include <boost/lexical_cast.hpp>
29 #include "curses/window.h"
31 #include "utility/type_conversions.h"
32 #include "utility/wide_string.h"
36 // Prepend '0' if the tag is a single digit number so that we get "expected"
37 // sort order with regular string comparison.
38 void format_numeric_tag(std::string
&s
)
40 if ((s
.length() == 1 && s
[0] != '0')
41 || (s
.length() > 3 && s
[1] == '/'))
45 size_t calc_hash(const char *s
, size_t seed
= 0)
47 for (; *s
!= '\0'; ++s
)
48 boost::hash_combine(seed
, *s
);
56 std::string
Song::TagsSeparator
= " | ";
58 bool Song::ShowDuplicateTags
= true;
60 std::string
Song::get(mpd_tag_type type
, unsigned idx
) const
63 const char *tag
= mpd_song_get_tag(m_song
.get(), type
, idx
);
69 Song::Song(mpd_song
*s
)
72 m_song
= std::shared_ptr
<mpd_song
>(s
, mpd_song_free
);
73 m_hash
= calc_hash(mpd_song_get_uri(s
));
76 std::string
Song::getURI(unsigned idx
) const
82 return mpd_song_get_uri(m_song
.get());
85 std::string
Song::getName(unsigned idx
) const
88 mpd_song
*s
= m_song
.get();
89 const char *res
= mpd_song_get_tag(s
, MPD_TAG_NAME
, idx
);
94 const char *uri
= mpd_song_get_uri(s
);
95 const char *name
= strrchr(uri
, '/');
102 std::string
Song::getDirectory(unsigned idx
) const
105 if (idx
> 0 || isStream())
107 const char *uri
= mpd_song_get_uri(m_song
.get());
108 const char *name
= strrchr(uri
, '/');
110 return std::string(uri
, name
-uri
);
115 std::string
Song::getArtist(unsigned idx
) const
118 return get(MPD_TAG_ARTIST
, idx
);
121 std::string
Song::getTitle(unsigned idx
) const
124 return get(MPD_TAG_TITLE
, idx
);
127 std::string
Song::getAlbum(unsigned idx
) const
130 return get(MPD_TAG_ALBUM
, idx
);
133 std::string
Song::getAlbumArtist(unsigned idx
) const
136 return get(MPD_TAG_ALBUM_ARTIST
, idx
);
139 std::string
Song::getTrack(unsigned idx
) const
142 std::string track
= get(MPD_TAG_TRACK
, idx
);
143 format_numeric_tag(track
);
147 std::string
Song::getTrackNumber(unsigned idx
) const
150 std::string track
= getTrack(idx
);
151 size_t slash
= track
.find('/');
152 if (slash
!= std::string::npos
)
157 std::string
Song::getDate(unsigned idx
) const
160 return get(MPD_TAG_DATE
, idx
);
163 std::string
Song::getGenre(unsigned idx
) const
166 return get(MPD_TAG_GENRE
, idx
);
169 std::string
Song::getComposer(unsigned idx
) const
172 return get(MPD_TAG_COMPOSER
, idx
);
175 std::string
Song::getPerformer(unsigned idx
) const
178 return get(MPD_TAG_PERFORMER
, idx
);
181 std::string
Song::getDisc(unsigned idx
) const
184 std::string disc
= get(MPD_TAG_DISC
, idx
);
185 format_numeric_tag(disc
);
189 std::string
Song::getComment(unsigned idx
) const
192 return get(MPD_TAG_COMMENT
, idx
);
195 std::string
Song::getLength(unsigned idx
) const
200 unsigned len
= getDuration();
202 return ShowTime(len
);
207 std::string
Song::getPriority(unsigned idx
) const
212 return boost::lexical_cast
<std::string
>(getPrio());
215 std::string
MPD::Song::getTags(GetFunction f
) const
220 if (ShowDuplicateTags
)
222 for (std::string tag
; !(tag
= (this->*f
)(idx
)).empty(); ++idx
)
225 result
+= TagsSeparator
;
231 bool already_present
;
232 // This is O(n^2), but it doesn't really matter as a list of tags will have
233 // at most 2 or 3 items the vast majority of time.
234 for (std::string tag
; !(tag
= (this->*f
)(idx
)).empty(); ++idx
)
236 already_present
= false;
237 for (unsigned i
= 0; i
< idx
; ++i
)
239 if ((this->*f
)(i
) == tag
)
241 already_present
= true;
245 if (!already_present
)
248 result
+= TagsSeparator
;
256 unsigned Song::getDuration() const
259 return mpd_song_get_duration(m_song
.get());
262 unsigned Song::getPosition() const
265 return mpd_song_get_pos(m_song
.get());
268 unsigned Song::getID() const
271 return mpd_song_get_id(m_song
.get());
274 unsigned Song::getPrio() const
277 return mpd_song_get_prio(m_song
.get());
280 time_t Song::getMTime() const
283 return mpd_song_get_last_modified(m_song
.get());
286 bool Song::isFromDatabase() const
289 const char *uri
= mpd_song_get_uri(m_song
.get());
290 return uri
[0] != '/' || !strrchr(uri
, '/');
293 bool Song::isStream() const
296 return !strncmp(mpd_song_get_uri(m_song
.get()), "http://", 7);
299 bool Song::empty() const
301 return m_song
.get() == 0;
304 std::string
Song::ShowTime(unsigned length
)
306 int hours
= length
/3600;
307 length
-= hours
*3600;
308 int minutes
= length
/60;
309 length
-= minutes
*60;
310 int seconds
= length
;
314 result
= (boost::format("%d:%02d:%02d") % hours
% minutes
% seconds
).str();
316 result
= (boost::format("%d:%02d") % minutes
% seconds
).str();