Improve detection for songs not in playlists
[ncmpcpp.git] / src / song.cpp
blob1935b2d2c35079dcd656f4805ec5af5768fdd1be
1 /***************************************************************************
2 * Copyright (C) 2008-2016 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
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 of the License, or *
8 * (at your option) 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 this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include <cassert>
22 #include <cstring>
23 #include <boost/format.hpp>
24 #include <boost/functional/hash.hpp>
25 #include <boost/lexical_cast.hpp>
26 #include <iostream>
27 #include <memory>
29 #include "curses/window.h"
30 #include "song.h"
31 #include "utility/type_conversions.h"
32 #include "utility/wide_string.h"
34 namespace {
36 size_t calc_hash(const char *s, size_t seed = 0)
38 for (; *s != '\0'; ++s)
39 boost::hash_combine(seed, *s);
40 return seed;
45 namespace MPD {
47 std::string Song::TagsSeparator = " | ";
49 bool Song::ShowDuplicateTags = true;
51 std::string Song::get(mpd_tag_type type, unsigned idx) const
53 std::string result;
54 const char *tag = mpd_song_get_tag(m_song.get(), type, idx);
55 if (tag)
56 result = tag;
57 return result;
60 Song::Song(mpd_song *s)
62 assert(s);
63 m_song = std::shared_ptr<mpd_song>(s, mpd_song_free);
64 m_hash = calc_hash(mpd_song_get_uri(s));
67 std::string Song::getURI(unsigned idx) const
69 assert(m_song);
70 if (idx > 0)
71 return "";
72 else
73 return mpd_song_get_uri(m_song.get());
76 std::string Song::getName(unsigned idx) const
78 assert(m_song);
79 mpd_song *s = m_song.get();
80 const char *res = mpd_song_get_tag(s, MPD_TAG_NAME, idx);
81 if (res)
82 return res;
83 else if (idx > 0)
84 return "";
85 const char *uri = mpd_song_get_uri(s);
86 const char *name = strrchr(uri, '/');
87 if (name)
88 return name+1;
89 else
90 return uri;
93 std::string Song::getDirectory(unsigned idx) const
95 assert(m_song);
96 if (idx > 0 || isStream())
97 return "";
98 const char *uri = mpd_song_get_uri(m_song.get());
99 const char *name = strrchr(uri, '/');
100 if (name)
101 return std::string(uri, name-uri);
102 else
103 return "/";
106 std::string Song::getArtist(unsigned idx) const
108 assert(m_song);
109 return get(MPD_TAG_ARTIST, idx);
112 std::string Song::getTitle(unsigned idx) const
114 assert(m_song);
115 return get(MPD_TAG_TITLE, idx);
118 std::string Song::getAlbum(unsigned idx) const
120 assert(m_song);
121 return get(MPD_TAG_ALBUM, idx);
124 std::string Song::getAlbumArtist(unsigned idx) const
126 assert(m_song);
127 return get(MPD_TAG_ALBUM_ARTIST, idx);
130 std::string Song::getTrack(unsigned idx) const
132 assert(m_song);
133 std::string track = get(MPD_TAG_TRACK, idx);
134 if ((track.length() == 1 && track[0] != '0')
135 || (track.length() > 3 && track[1] == '/'))
136 track = "0"+track;
137 return track;
140 std::string Song::getTrackNumber(unsigned idx) const
142 assert(m_song);
143 std::string track = getTrack(idx);
144 size_t slash = track.find('/');
145 if (slash != std::string::npos)
146 track.resize(slash);
147 return track;
150 std::string Song::getDate(unsigned idx) const
152 assert(m_song);
153 return get(MPD_TAG_DATE, idx);
156 std::string Song::getGenre(unsigned idx) const
158 assert(m_song);
159 return get(MPD_TAG_GENRE, idx);
162 std::string Song::getComposer(unsigned idx) const
164 assert(m_song);
165 return get(MPD_TAG_COMPOSER, idx);
168 std::string Song::getPerformer(unsigned idx) const
170 assert(m_song);
171 return get(MPD_TAG_PERFORMER, idx);
174 std::string Song::getDisc(unsigned idx) const
176 assert(m_song);
177 return get(MPD_TAG_DISC, idx);
180 std::string Song::getComment(unsigned idx) const
182 assert(m_song);
183 return get(MPD_TAG_COMMENT, idx);
186 std::string Song::getLength(unsigned idx) const
188 assert(m_song);
189 if (idx > 0)
190 return "";
191 unsigned len = getDuration();
192 if (len > 0)
193 return ShowTime(len);
194 else
195 return "-:--";
198 std::string Song::getPriority(unsigned idx) const
200 assert(m_song);
201 if (idx > 0)
202 return "";
203 return boost::lexical_cast<std::string>(getPrio());
206 std::string MPD::Song::getTags(GetFunction f) const
208 assert(m_song);
209 unsigned idx = 0;
210 std::string result;
211 if (ShowDuplicateTags)
213 for (std::string tag; !(tag = (this->*f)(idx)).empty(); ++idx)
215 if (!result.empty())
216 result += TagsSeparator;
217 result += tag;
220 else
222 bool already_present;
223 // This is O(n^2), but it doesn't really matter as a list of tags will have
224 // at most 2 or 3 items the vast majority of time.
225 for (std::string tag; !(tag = (this->*f)(idx)).empty(); ++idx)
227 already_present = false;
228 for (unsigned i = 0; i < idx; ++i)
230 if ((this->*f)(i) == tag)
232 already_present = true;
233 break;
236 if (!already_present)
238 if (idx > 0)
239 result += TagsSeparator;
240 result += tag;
244 return result;
247 unsigned Song::getDuration() const
249 assert(m_song);
250 return mpd_song_get_duration(m_song.get());
253 unsigned Song::getPosition() const
255 assert(m_song);
256 return mpd_song_get_pos(m_song.get());
259 unsigned Song::getID() const
261 assert(m_song);
262 return mpd_song_get_id(m_song.get());
265 unsigned Song::getPrio() const
267 assert(m_song);
268 return mpd_song_get_prio(m_song.get());
271 time_t Song::getMTime() const
273 assert(m_song);
274 return mpd_song_get_last_modified(m_song.get());
277 bool Song::isFromDatabase() const
279 assert(m_song);
280 const char *uri = mpd_song_get_uri(m_song.get());
281 return uri[0] != '/' || !strrchr(uri, '/');
284 bool Song::isStream() const
286 assert(m_song);
287 return !strncmp(mpd_song_get_uri(m_song.get()), "http://", 7);
290 bool Song::empty() const
292 return m_song.get() == 0;
295 std::string Song::ShowTime(unsigned length)
297 int hours = length/3600;
298 length -= hours*3600;
299 int minutes = length/60;
300 length -= minutes*60;
301 int seconds = length;
303 std::string result;
304 if (hours > 0)
305 result = (boost::format("%d:%02d:%02d") % hours % minutes % seconds).str();
306 else
307 result = (boost::format("%d:%02d") % minutes % seconds).str();
308 return result;