1 /***************************************************************************
2 * Copyright (C) 2008-2009 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 ***************************************************************************/
37 unsigned calc_hash(const char *p
)
41 hash
= (hash
<< 5) + hash
+ *p
++;
46 MPD::Song::Song(mpd_song
*s
, bool copy_ptr
) : itsSong(s
),
49 itsSlash(std::string::npos
),
58 MPD::Song::Song(const Song
&s
) : itsSong(s
.copyPtr
? s
.itsSong
: mpd_song_dup(s
.itsSong
)),
59 itsFile(s
.itsFile
? strdup(s
.itsFile
) : 0),
60 itsTags(s
.itsTags
? new TagMap(*s
.itsTags
) : 0),
61 itsNewName(s
.itsNewName
),
65 isLocalised(s
.isLocalised
)
72 mpd_song_free(itsSong
);
77 std::string
MPD::Song::GetLength(unsigned pos
) const
81 unsigned len
= mpd_song_get_duration(itsSong
);
82 return !len
? "-:--" : ShowTime(len
);
85 void MPD::Song::Localize()
90 const char *tag
, *conv_tag
;
91 conv_tag
= tag
= mpd_song_get_uri(itsSong
);
92 utf_to_locale(conv_tag
, 0);
93 if (tag
!= conv_tag
) // file has been converted
98 for (unsigned t
= MPD_TAG_ARTIST
; t
<= MPD_TAG_DISC
; ++t
)
101 for (; (tag
= mpd_song_get_tag(itsSong
, mpd_tag_type(t
), pos
)); ++pos
)
104 utf_to_locale(conv_tag
, 0);
105 if (tag
!= conv_tag
) // tag has been converted
107 SetTag(mpd_tag_type(t
), pos
, conv_tag
);
113 # endif // HAVE_ICONV_H
116 void MPD::Song::Clear()
119 mpd_song_free(itsSong
);
129 itsSlash
= std::string::npos
;
135 bool MPD::Song::Empty() const
140 bool MPD::Song::isFromDB() const
142 return (MyFilename()[0] != '/') || itsSlash
== std::string::npos
;
145 bool MPD::Song::isStream() const
147 return !strncmp(MyFilename(), "http://", 7);
150 std::string
MPD::Song::GetFile(unsigned pos
) const
152 return pos
> 0 ? "" : MyFilename();
155 std::string
MPD::Song::GetName(unsigned pos
) const
159 std::string name
= GetTag(MPD_TAG_NAME
, 0);
162 else if (itsSlash
!= std::string::npos
)
163 return MyFilename()+itsSlash
+1;
168 std::string
MPD::Song::GetDirectory(unsigned pos
) const
170 if (pos
> 0 || isStream())
172 else if (itsSlash
== std::string::npos
)
175 return std::string(MyFilename(), itsSlash
);
178 std::string
MPD::Song::GetArtist(unsigned pos
) const
180 return GetTag(MPD_TAG_ARTIST
, pos
);
183 std::string
MPD::Song::GetTitle(unsigned pos
) const
185 return GetTag(MPD_TAG_TITLE
, pos
);
188 std::string
MPD::Song::GetAlbum(unsigned pos
) const
190 return GetTag(MPD_TAG_ALBUM
, pos
);
193 std::string
MPD::Song::GetTrack(unsigned pos
) const
195 std::string track
= GetTag(MPD_TAG_TRACK
, pos
);
196 return (track
.length() == 1 && track
[0] != '0') || (track
.length() > 3 && track
[1] == '/') ? "0"+track
: track
;
199 std::string
MPD::Song::GetTrackNumber(unsigned pos
) const
201 std::string track
= GetTag(MPD_TAG_TRACK
, pos
);
202 size_t slash
= track
.find('/');
203 if (slash
!= std::string::npos
)
205 return track
.length() == 1 && track
[0] != '0' ? "0"+track
: track
;
208 std::string
MPD::Song::GetDate(unsigned pos
) const
210 return GetTag(MPD_TAG_DATE
, pos
);
213 std::string
MPD::Song::GetGenre(unsigned pos
) const
215 return GetTag(MPD_TAG_GENRE
, pos
);
218 std::string
MPD::Song::GetComposer(unsigned pos
) const
220 return GetTag(MPD_TAG_COMPOSER
, pos
);
223 std::string
MPD::Song::GetPerformer(unsigned pos
) const
225 return GetTag(MPD_TAG_PERFORMER
, pos
);
228 std::string
MPD::Song::GetDisc(unsigned pos
) const
230 return GetTag(MPD_TAG_DISC
, pos
);
233 std::string
MPD::Song::GetComment(unsigned pos
) const
235 return GetTag(MPD_TAG_COMMENT
, pos
);
238 std::string
MPD::Song::GetTags(GetFunction f
) const
242 for (std::string tag
; !(tag
= (this->*f
)(pos
)).empty(); ++pos
)
251 void MPD::Song::SetArtist(const std::string
&str
, unsigned pos
)
253 SetTag(MPD_TAG_ARTIST
, pos
, str
);
256 void MPD::Song::SetTitle(const std::string
&str
, unsigned pos
)
258 SetTag(MPD_TAG_TITLE
, pos
, str
);
261 void MPD::Song::SetAlbum(const std::string
&str
, unsigned pos
)
263 SetTag(MPD_TAG_ALBUM
, pos
, str
);
266 void MPD::Song::SetTrack(const std::string
&str
, unsigned pos
)
268 SetTag(MPD_TAG_TRACK
, pos
, str
);
271 void MPD::Song::SetTrack(unsigned track
, unsigned pos
)
273 SetTag(MPD_TAG_TRACK
, pos
, IntoStr(track
));
276 void MPD::Song::SetDate(const std::string
&str
, unsigned pos
)
278 SetTag(MPD_TAG_DATE
, pos
, str
);
281 void MPD::Song::SetDate(unsigned year
, unsigned pos
)
283 SetTag(MPD_TAG_DATE
, pos
, IntoStr(year
));
286 void MPD::Song::SetGenre(const std::string
&str
, unsigned pos
)
288 SetTag(MPD_TAG_GENRE
, pos
, str
);
291 void MPD::Song::SetComposer(const std::string
&str
, unsigned pos
)
293 SetTag(MPD_TAG_COMPOSER
, pos
, str
);
296 void MPD::Song::SetPerformer(const std::string
&str
, unsigned pos
)
298 SetTag(MPD_TAG_PERFORMER
, pos
, str
);
301 void MPD::Song::SetDisc(const std::string
&str
, unsigned pos
)
303 SetTag(MPD_TAG_DISC
, pos
, str
);
306 void MPD::Song::SetComment(const std::string
&str
, unsigned pos
)
308 SetTag(MPD_TAG_COMMENT
, pos
, str
);
311 void MPD::Song::SetPosition(unsigned pos
)
313 mpd_song_set_pos(itsSong
, pos
);
316 void MPD::Song::SetTags(SetFunction f
, const std::string
&value
)
319 // tag editor can save multiple instances of performer and composer
320 // tag, so we need to split them and allow it to read them separately.
321 if (f
== &Song::SetComposer
|| f
== &Song::SetPerformer
)
323 for (size_t i
= 0; i
!= std::string::npos
; i
= value
.find(",", i
))
327 while (value
[i
] == ' ')
329 size_t j
= value
.find(",", i
);
330 (this->*f
)(value
.substr(i
, j
-i
), pos
++);
334 (this->*f
)(value
, pos
++);
335 // there should be empty tag at the end since if we are
336 // reading them, original tag from mpd_song at the position
337 // after the last one locally set can be non-empty and in this
338 // case GetTags() would read it, which is undesirable.
342 std::string
MPD::Song::ParseFormat(std::string::const_iterator
&it
, const char *escape_chars
) const
345 bool has_some_tags
= 0;
346 MPD::Song::GetFunction get
= 0;
351 std::string tags
= ParseFormat(it
, escape_chars
);
366 get
= &MPD::Song::GetLength
;
369 get
= &MPD::Song::GetDirectory
;
372 get
= &MPD::Song::GetName
;
375 get
= &MPD::Song::GetArtist
;
378 get
= &MPD::Song::GetAlbum
;
381 get
= &MPD::Song::GetDate
;
384 get
= &MPD::Song::GetTrackNumber
;
387 get
= &MPD::Song::GetTrack
;
390 get
= &MPD::Song::GetGenre
;
393 get
= &MPD::Song::GetComposer
;
396 get
= &MPD::Song::GetPerformer
;
399 get
= &MPD::Song::GetDisc
;
402 get
= &MPD::Song::GetComment
;
405 get
= &MPD::Song::GetTitle
;
408 result
+= *it
; // no break here
415 std::string tag
= GetTags(get
);
416 if (escape_chars
) // prepend format escape character to all given chars to escape
417 for (const char *ch
= escape_chars
; *ch
; ++ch
)
418 for (size_t i
= 0; (i
= tag
.find(*ch
, i
)) != std::string::npos
; i
+= 2)
419 tag
.replace(i
, 1, std::string(1, FormatEscapeCharacter
) + ch
);
420 if (!tag
.empty() && (get
!= &MPD::Song::GetLength
|| GetTotalLength()))
432 int brace_counter
= 0;
433 if (*it
!= '}' || !has_some_tags
)
435 for (; *it
!= '}' || brace_counter
; ++it
)
443 return ParseFormat(++it
, escape_chars
);
451 for (; *it
!= '}' || *(it
+1) == '|' || brace_counter
; ++it
)
464 std::string
MPD::Song::toString(const std::string
&format
, const char *escape_chars
) const
466 std::string::const_iterator it
= format
.begin();
467 return ParseFormat(it
, escape_chars
);
470 MPD::Song
&MPD::Song::operator=(const MPD::Song
&s
)
475 mpd_song_free(itsSong
);
478 itsSong
= s
.copyPtr
? s
.itsSong
: (s
.itsSong
? mpd_song_dup(s
.itsSong
) : 0);
479 itsFile
= s
.itsFile
? strdup(s
.itsFile
) : 0;
480 itsTags
= s
.itsTags
? new TagMap(*s
.itsTags
) : 0;
481 itsNewName
= s
.itsNewName
;
482 itsSlash
= s
.itsSlash
;
485 isLocalised
= s
.isLocalised
;
489 std::string
MPD::Song::ShowTime(int length
)
491 std::ostringstream ss
;
493 int hours
= length
/3600;
494 length
-= hours
*3600;
495 int minutes
= length
/60;
496 length
-= minutes
*60;
497 int seconds
= length
;
502 << std::setw(2) << std::setfill('0') << minutes
<< ":"
503 << std::setw(2) << std::setfill('0') << seconds
;
508 << std::setw(2) << std::setfill('0') << seconds
;
513 void MPD::Song::ValidateFormat(const std::string
&type
, const std::string
&s
)
516 for (std::string::const_iterator it
= s
.begin(); it
!= s
.end(); ++it
)
524 FatalError(type
+ ": number of opening and closing braces does not equal!");
527 void MPD::Song::SetHashAndSlash()
529 const char *filename
= MyFilename();
532 const char *tmp
= strrchr(filename
, '/');
533 itsSlash
= tmp
? tmp
-filename
: std::string::npos
;
536 itsHash
= calc_hash(filename
);
539 const char *MPD::Song::MyFilename() const
541 return itsFile
? itsFile
: mpd_song_get_uri(itsSong
);
544 void MPD::Song::SetTag(mpd_tag_type type
, unsigned pos
, const std::string
&value
)
547 itsTags
= new TagMap
;
548 (*itsTags
)[std::make_pair(type
, pos
)] = value
;
551 std::string
MPD::Song::GetTag(mpd_tag_type type
, unsigned pos
) const
555 TagMap::const_iterator it
= itsTags
->find(std::make_pair(type
, pos
));
556 if (it
!= itsTags
->end())
559 const char *tag
= mpd_song_get_tag(itsSong
, type
, pos
);
560 return tag
? tag
: "";