Fix InternetLyricsFetcher
[ncmpcpp.git] / src / song.cpp
blobd18ba9df10c58a73fef243ed76413bfcbb1720ef
1 /***************************************************************************
2 * Copyright (C) 2008-2017 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 // 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] == '/'))
42 s = "0"+s;
45 size_t calc_hash(const char *s, size_t seed = 0)
47 for (; *s != '\0'; ++s)
48 boost::hash_combine(seed, *s);
49 return seed;
54 namespace MPD {
56 std::string Song::TagsSeparator = " | ";
58 bool Song::ShowDuplicateTags = true;
60 std::string Song::get(mpd_tag_type type, unsigned idx) const
62 std::string result;
63 const char *tag = mpd_song_get_tag(m_song.get(), type, idx);
64 if (tag)
65 result = tag;
66 return result;
69 Song::Song(mpd_song *s)
71 assert(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
78 assert(m_song);
79 if (idx > 0)
80 return "";
81 else
82 return mpd_song_get_uri(m_song.get());
85 std::string Song::getName(unsigned idx) const
87 assert(m_song);
88 mpd_song *s = m_song.get();
89 const char *res = mpd_song_get_tag(s, MPD_TAG_NAME, idx);
90 if (res)
91 return res;
92 else if (idx > 0)
93 return "";
94 const char *uri = mpd_song_get_uri(s);
95 const char *name = strrchr(uri, '/');
96 if (name)
97 return name+1;
98 else
99 return uri;
102 std::string Song::getDirectory(unsigned idx) const
104 assert(m_song);
105 if (idx > 0 || isStream())
106 return "";
107 const char *uri = mpd_song_get_uri(m_song.get());
108 const char *name = strrchr(uri, '/');
109 if (name)
110 return std::string(uri, name-uri);
111 else
112 return "/";
115 std::string Song::getArtist(unsigned idx) const
117 assert(m_song);
118 return get(MPD_TAG_ARTIST, idx);
121 std::string Song::getTitle(unsigned idx) const
123 assert(m_song);
124 return get(MPD_TAG_TITLE, idx);
127 std::string Song::getAlbum(unsigned idx) const
129 assert(m_song);
130 return get(MPD_TAG_ALBUM, idx);
133 std::string Song::getAlbumArtist(unsigned idx) const
135 assert(m_song);
136 return get(MPD_TAG_ALBUM_ARTIST, idx);
139 std::string Song::getTrack(unsigned idx) const
141 assert(m_song);
142 std::string track = get(MPD_TAG_TRACK, idx);
143 format_numeric_tag(track);
144 return track;
147 std::string Song::getTrackNumber(unsigned idx) const
149 assert(m_song);
150 std::string track = getTrack(idx);
151 size_t slash = track.find('/');
152 if (slash != std::string::npos)
153 track.resize(slash);
154 return track;
157 std::string Song::getDate(unsigned idx) const
159 assert(m_song);
160 return get(MPD_TAG_DATE, idx);
163 std::string Song::getGenre(unsigned idx) const
165 assert(m_song);
166 return get(MPD_TAG_GENRE, idx);
169 std::string Song::getComposer(unsigned idx) const
171 assert(m_song);
172 return get(MPD_TAG_COMPOSER, idx);
175 std::string Song::getPerformer(unsigned idx) const
177 assert(m_song);
178 return get(MPD_TAG_PERFORMER, idx);
181 std::string Song::getDisc(unsigned idx) const
183 assert(m_song);
184 std::string disc = get(MPD_TAG_DISC, idx);
185 format_numeric_tag(disc);
186 return disc;
189 std::string Song::getComment(unsigned idx) const
191 assert(m_song);
192 return get(MPD_TAG_COMMENT, idx);
195 std::string Song::getLength(unsigned idx) const
197 assert(m_song);
198 if (idx > 0)
199 return "";
200 unsigned len = getDuration();
201 if (len > 0)
202 return ShowTime(len);
203 else
204 return "-:--";
207 std::string Song::getPriority(unsigned idx) const
209 assert(m_song);
210 if (idx > 0)
211 return "";
212 return boost::lexical_cast<std::string>(getPrio());
215 std::string MPD::Song::getTags(GetFunction f) const
217 assert(m_song);
218 unsigned idx = 0;
219 std::string result;
220 if (ShowDuplicateTags)
222 for (std::string tag; !(tag = (this->*f)(idx)).empty(); ++idx)
224 if (!result.empty())
225 result += TagsSeparator;
226 result += tag;
229 else
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;
242 break;
245 if (!already_present)
247 if (idx > 0)
248 result += TagsSeparator;
249 result += tag;
253 return result;
256 unsigned Song::getDuration() const
258 assert(m_song);
259 return mpd_song_get_duration(m_song.get());
262 unsigned Song::getPosition() const
264 assert(m_song);
265 return mpd_song_get_pos(m_song.get());
268 unsigned Song::getID() const
270 assert(m_song);
271 return mpd_song_get_id(m_song.get());
274 unsigned Song::getPrio() const
276 assert(m_song);
277 return mpd_song_get_prio(m_song.get());
280 time_t Song::getMTime() const
282 assert(m_song);
283 return mpd_song_get_last_modified(m_song.get());
286 bool Song::isFromDatabase() const
288 assert(m_song);
289 const char *uri = mpd_song_get_uri(m_song.get());
290 return uri[0] != '/' || !strrchr(uri, '/');
293 bool Song::isStream() const
295 assert(m_song);
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;
312 std::string result;
313 if (hours > 0)
314 result = (boost::format("%d:%02d:%02d") % hours % minutes % seconds).str();
315 else
316 result = (boost::format("%d:%02d") % minutes % seconds).str();
317 return result;