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 ***************************************************************************/
34 Connection::Connection() : m_connection(nullptr),
35 m_command_list_active(false),
43 Connection::~Connection()
46 mpd_connection_free(m_connection
);
49 void Connection::Connect()
51 assert(!m_connection
);
54 m_connection
= mpd_connection_new(m_host
.c_str(), m_port
, m_timeout
* 1000);
56 if (!m_password
.empty())
58 m_fd
= mpd_connection_get_fd(m_connection
);
61 catch (MPD::ClientError
&e
)
68 bool Connection::Connected() const
73 void Connection::Disconnect()
76 mpd_connection_free(m_connection
);
77 m_connection
= nullptr;
78 m_command_list_active
= false;
82 unsigned Connection::Version() const
84 return m_connection
? mpd_connection_get_server_version(m_connection
)[1] : 0;
87 void Connection::SetHostname(const std::string
&host
)
89 size_t at
= host
.find("@");
90 if (at
!= std::string::npos
)
92 m_password
= host
.substr(0, at
);
93 m_host
= host
.substr(at
+1);
99 void Connection::SendPassword()
101 assert(m_connection
);
103 assert(!m_command_list_active
);
104 mpd_run_password(m_connection
, m_password
.c_str());
108 void Connection::idle()
113 mpd_send_idle(m_connection
);
119 int Connection::noidle()
123 if (m_idle
&& mpd_send_noidle(m_connection
))
126 flags
= mpd_recv_idle(m_connection
, true);
127 mpd_response_finish(m_connection
);
133 Statistics
Connection::getStatistics()
136 mpd_stats
*stats
= mpd_run_stats(m_connection
);
138 return Statistics(stats
);
141 Status
Connection::getStatus()
144 mpd_status
*status
= mpd_run_status(m_connection
);
146 return Status(status
);
149 void Connection::UpdateDirectory(const std::string
&path
)
151 prechecksNoCommandsList();
152 mpd_run_update(m_connection
, path
.c_str());
156 void Connection::Play()
158 prechecksNoCommandsList();
159 mpd_run_play(m_connection
);
163 void Connection::Play(int pos
)
165 prechecksNoCommandsList();
166 mpd_run_play_pos(m_connection
, pos
);
170 void Connection::PlayID(int id
)
172 prechecksNoCommandsList();
173 mpd_run_play_id(m_connection
, id
);
177 void Connection::Pause(bool state
)
179 prechecksNoCommandsList();
180 mpd_run_pause(m_connection
, state
);
184 void Connection::Toggle()
186 prechecksNoCommandsList();
187 mpd_run_toggle_pause(m_connection
);
191 void Connection::Stop()
193 prechecksNoCommandsList();
194 mpd_run_stop(m_connection
);
198 void Connection::Next()
200 prechecksNoCommandsList();
201 mpd_run_next(m_connection
);
205 void Connection::Prev()
207 prechecksNoCommandsList();
208 mpd_run_previous(m_connection
);
212 void Connection::Move(unsigned from
, unsigned to
)
215 if (m_command_list_active
)
216 mpd_send_move(m_connection
, from
, to
);
219 mpd_run_move(m_connection
, from
, to
);
224 void Connection::Swap(unsigned from
, unsigned to
)
227 if (m_command_list_active
)
228 mpd_send_swap(m_connection
, from
, to
);
231 mpd_run_swap(m_connection
, from
, to
);
236 void Connection::Seek(unsigned pos
, unsigned where
)
238 prechecksNoCommandsList();
239 mpd_run_seek_pos(m_connection
, pos
, where
);
243 void Connection::Shuffle()
245 prechecksNoCommandsList();
246 mpd_run_shuffle(m_connection
);
250 void Connection::ClearMainPlaylist()
252 prechecksNoCommandsList();
253 mpd_run_clear(m_connection
);
257 void Connection::ClearPlaylist(const std::string
&playlist
)
259 prechecksNoCommandsList();
260 mpd_run_playlist_clear(m_connection
, playlist
.c_str());
264 void Connection::AddToPlaylist(const std::string
&path
, const Song
&s
)
266 AddToPlaylist(path
, s
.getURI());
269 void Connection::AddToPlaylist(const std::string
&path
, const std::string
&file
)
272 if (m_command_list_active
)
273 mpd_send_playlist_add(m_connection
, path
.c_str(), file
.c_str());
276 mpd_run_playlist_add(m_connection
, path
.c_str(), file
.c_str());
281 void Connection::PlaylistMove(const std::string
&path
, int from
, int to
)
284 if (m_command_list_active
)
285 mpd_send_playlist_move(m_connection
, path
.c_str(), from
, to
);
288 mpd_send_playlist_move(m_connection
, path
.c_str(), from
, to
);
289 mpd_response_finish(m_connection
);
294 void Connection::Rename(const std::string
&from
, const std::string
&to
)
296 prechecksNoCommandsList();
297 mpd_run_rename(m_connection
, from
.c_str(), to
.c_str());
301 void Connection::GetPlaylistChanges(unsigned version
, SongConsumer f
)
303 prechecksNoCommandsList();
304 mpd_send_queue_changes_meta(m_connection
, version
);
305 while (mpd_song
*s
= mpd_recv_song(m_connection
))
307 mpd_response_finish(m_connection
);
311 Song
Connection::GetCurrentSong()
313 prechecksNoCommandsList();
314 mpd_send_current_song(m_connection
);
315 mpd_song
*s
= mpd_recv_song(m_connection
);
316 mpd_response_finish(m_connection
);
321 Song
Connection::GetSong(const std::string
&path
)
323 prechecksNoCommandsList();
324 mpd_send_list_all_meta(m_connection
, path
.c_str());
325 mpd_song
*s
= mpd_recv_song(m_connection
);
326 mpd_response_finish(m_connection
);
331 void Connection::GetPlaylistContent(const std::string
&path
, SongConsumer f
)
333 prechecksNoCommandsList();
334 mpd_send_list_playlist_meta(m_connection
, path
.c_str());
335 while (mpd_song
*s
= mpd_recv_song(m_connection
))
337 mpd_response_finish(m_connection
);
341 void Connection::GetPlaylistContentNoInfo(const std::string
&path
, SongConsumer f
)
343 prechecksNoCommandsList();
344 mpd_send_list_playlist(m_connection
, path
.c_str());
345 while (mpd_song
*s
= mpd_recv_song(m_connection
))
347 mpd_response_finish(m_connection
);
351 void Connection::GetSupportedExtensions(std::set
<std::string
> &acc
)
353 prechecksNoCommandsList();
354 mpd_send_command(m_connection
, "decoders", NULL
);
355 while (mpd_pair
*pair
= mpd_recv_pair_named(m_connection
, "suffix"))
357 acc
.insert(pair
->value
);
358 mpd_return_pair(m_connection
, pair
);
360 mpd_response_finish(m_connection
);
364 void Connection::SetRepeat(bool mode
)
366 prechecksNoCommandsList();
367 mpd_run_repeat(m_connection
, mode
);
371 void Connection::SetRandom(bool mode
)
373 prechecksNoCommandsList();
374 mpd_run_random(m_connection
, mode
);
378 void Connection::SetSingle(bool mode
)
380 prechecksNoCommandsList();
381 mpd_run_single(m_connection
, mode
);
385 void Connection::SetConsume(bool mode
)
387 prechecksNoCommandsList();
388 mpd_run_consume(m_connection
, mode
);
392 void Connection::SetVolume(unsigned vol
)
394 prechecksNoCommandsList();
395 mpd_run_set_volume(m_connection
, vol
);
399 std::string
Connection::GetReplayGainMode()
401 prechecksNoCommandsList();
402 mpd_send_command(m_connection
, "replay_gain_status", NULL
);
404 if (mpd_pair
*pair
= mpd_recv_pair_named(m_connection
, "replay_gain_mode"))
406 result
= pair
->value
;
407 mpd_return_pair(m_connection
, pair
);
409 mpd_response_finish(m_connection
);
414 void Connection::SetReplayGainMode(ReplayGainMode mode
)
416 prechecksNoCommandsList();
433 mpd_send_command(m_connection
, "replay_gain_mode", rg_mode
, NULL
);
434 mpd_response_finish(m_connection
);
438 void Connection::SetCrossfade(unsigned crossfade
)
440 prechecksNoCommandsList();
441 mpd_run_crossfade(m_connection
, crossfade
);
445 void Connection::SetPriority(const Song
&s
, int prio
)
448 if (m_command_list_active
)
449 mpd_send_prio_id(m_connection
, prio
, s
.getID());
452 mpd_run_prio_id(m_connection
, prio
, s
.getID());
457 int Connection::AddSong(const std::string
&path
, int pos
)
462 mpd_send_add_id(m_connection
, path
.c_str());
464 mpd_send_add_id_to(m_connection
, path
.c_str(), pos
);
465 if (!m_command_list_active
)
467 id
= mpd_recv_song_id(m_connection
);
468 mpd_response_finish(m_connection
);
476 int Connection::AddSong(const Song
&s
, int pos
)
478 return AddSong((!s
.isFromDatabase() ? "file://" : "") + s
.getURI(), pos
);
481 void Connection::Add(const std::string
&path
)
484 if (m_command_list_active
)
485 mpd_send_add(m_connection
, path
.c_str());
488 mpd_run_add(m_connection
, path
.c_str());
493 bool Connection::AddRandomTag(mpd_tag_type tag
, size_t number
)
496 GetList(tag
, [&tags
](std::string tag_name
) {
497 tags
.push_back(tag_name
);
499 if (number
> tags
.size())
501 //if (itsErrorHandler)
502 // itsErrorHandler(this, 0, "Requested number is out of range", itsErrorHandlerUserdata);
507 std::random_shuffle(tags
.begin(), tags
.end());
508 auto it
= tags
.begin();
509 for (size_t i
= 0; i
< number
&& it
!= tags
.end(); ++i
)
512 AddSearch(tag
, *it
++);
514 CommitSearchSongs([&songs
](MPD::Song s
) {
518 for (auto s
= songs
.begin(); s
!= songs
.end(); ++s
)
520 CommitCommandsList();
526 bool Connection::AddRandomSongs(size_t number
)
528 prechecksNoCommandsList();
530 mpd_send_list_all(m_connection
, "/");
531 while (mpd_pair
*item
= mpd_recv_pair_named(m_connection
, "file"))
533 files
.push_back(item
->value
);
534 mpd_return_pair(m_connection
, item
);
536 mpd_response_finish(m_connection
);
539 if (number
> files
.size())
541 //if (itsErrorHandler)
542 // itsErrorHandler(this, 0, "Requested number of random songs is bigger than size of your library", itsErrorHandlerUserdata);
547 std::random_shuffle(files
.begin(), files
.end());
549 auto it
= files
.begin();
550 for (size_t i
= 0; i
< number
&& it
!= files
.end(); ++i
, ++it
)
552 CommitCommandsList();
557 void Connection::Delete(unsigned pos
)
560 mpd_send_delete(m_connection
, pos
);
561 if (!m_command_list_active
)
563 mpd_response_finish(m_connection
);
568 void Connection::PlaylistDelete(const std::string
&playlist
, unsigned pos
)
571 mpd_send_playlist_delete(m_connection
, playlist
.c_str(), pos
);
572 if (!m_command_list_active
)
574 mpd_response_finish(m_connection
);
579 void Connection::StartCommandsList()
581 prechecksNoCommandsList();
582 mpd_command_list_begin(m_connection
, true);
583 m_command_list_active
= true;
587 void Connection::CommitCommandsList()
590 assert(m_command_list_active
);
591 mpd_command_list_end(m_connection
);
592 mpd_response_finish(m_connection
);
593 m_command_list_active
= false;
597 void Connection::DeletePlaylist(const std::string
&name
)
599 prechecksNoCommandsList();
600 mpd_run_rm(m_connection
, name
.c_str());
604 void Connection::LoadPlaylist(const std::string
&name
)
606 prechecksNoCommandsList();
607 mpd_run_load(m_connection
, name
.c_str());
611 void Connection::SavePlaylist(const std::string
&name
)
613 prechecksNoCommandsList();
614 mpd_send_save(m_connection
, name
.c_str());
615 mpd_response_finish(m_connection
);
619 void Connection::GetPlaylists(StringConsumer f
)
621 GetDirectory("/", [&f
](Item
&&item
) {
622 if (item
.type
== itPlaylist
)
623 f(std::move(item
.name
));
627 void Connection::GetList(mpd_tag_type type
, StringConsumer f
)
629 prechecksNoCommandsList();
630 mpd_search_db_tags(m_connection
, type
);
631 mpd_search_commit(m_connection
);
632 while (mpd_pair
*item
= mpd_recv_pair_tag(m_connection
, type
))
634 f(std::string(item
->value
));
635 mpd_return_pair(m_connection
, item
);
637 mpd_response_finish(m_connection
);
641 void Connection::StartSearch(bool exact_match
)
643 prechecksNoCommandsList();
644 mpd_search_db_songs(m_connection
, exact_match
);
647 void Connection::StartFieldSearch(mpd_tag_type item
)
649 prechecksNoCommandsList();
650 mpd_search_db_tags(m_connection
, item
);
653 void Connection::AddSearch(mpd_tag_type item
, const std::string
&str
) const
656 mpd_search_add_tag_constraint(m_connection
, MPD_OPERATOR_DEFAULT
, item
, str
.c_str());
659 void Connection::AddSearchAny(const std::string
&str
) const
662 mpd_search_add_any_tag_constraint(m_connection
, MPD_OPERATOR_DEFAULT
, str
.c_str());
665 void Connection::AddSearchURI(const std::string
&str
) const
668 mpd_search_add_uri_constraint(m_connection
, MPD_OPERATOR_DEFAULT
, str
.c_str());
671 void Connection::CommitSearchSongs(SongConsumer f
)
673 prechecksNoCommandsList();
674 mpd_search_commit(m_connection
);
675 while (mpd_song
*s
= mpd_recv_song(m_connection
))
677 mpd_response_finish(m_connection
);
681 void Connection::CommitSearchTags(StringConsumer f
)
683 prechecksNoCommandsList();
684 mpd_search_commit(m_connection
);
685 while (mpd_pair
*tag
= mpd_recv_pair_tag(m_connection
, m_searched_field
))
687 f(std::string(tag
->value
));
688 mpd_return_pair(m_connection
, tag
);
690 mpd_response_finish(m_connection
);
694 void Connection::GetDirectory(const std::string
&directory
, ItemConsumer f
)
696 prechecksNoCommandsList();
697 mpd_send_list_meta(m_connection
, directory
.c_str());
698 while (mpd_entity
*item
= mpd_recv_entity(m_connection
))
701 switch (mpd_entity_get_type(item
))
703 case MPD_ENTITY_TYPE_DIRECTORY
:
704 it
.name
= mpd_directory_get_path(mpd_entity_get_directory(item
));
705 it
.type
= itDirectory
;
707 case MPD_ENTITY_TYPE_SONG
:
708 it
.song
= std::make_shared
<Song
>(Song(mpd_song_dup(mpd_entity_get_song(item
))));
711 case MPD_ENTITY_TYPE_PLAYLIST
:
712 it
.name
= mpd_playlist_get_path(mpd_entity_get_playlist(item
));
713 it
.type
= itPlaylist
;
718 mpd_entity_free(item
);
721 mpd_response_finish(m_connection
);
725 void Connection::GetDirectoryRecursive(const std::string
&directory
, SongConsumer f
)
727 prechecksNoCommandsList();
728 mpd_send_list_all_meta(m_connection
, directory
.c_str());
729 while (mpd_entity
*e
= mpd_recv_entity(m_connection
)) {
730 if (mpd_entity_get_type(e
) == MPD_ENTITY_TYPE_SONG
)
731 f(Song(mpd_song_dup(mpd_entity_get_song(e
))));
734 mpd_response_finish(m_connection
);
738 void Connection::GetDirectories(const std::string
&directory
, StringConsumer f
)
740 prechecksNoCommandsList();
741 mpd_send_list_meta(m_connection
, directory
.c_str());
742 while (mpd_directory
*dir
= mpd_recv_directory(m_connection
))
744 f(std::string(mpd_directory_get_path(dir
)));
745 mpd_directory_free(dir
);
747 mpd_response_finish(m_connection
);
751 void Connection::GetSongs(const std::string
&directory
, SongConsumer f
)
753 prechecksNoCommandsList();
754 mpd_send_list_meta(m_connection
, directory
.c_str());
755 while (mpd_song
*s
= mpd_recv_song(m_connection
))
757 mpd_response_finish(m_connection
);
761 void Connection::GetOutputs(OutputConsumer f
)
763 prechecksNoCommandsList();
764 mpd_send_outputs(m_connection
);
765 while (mpd_output
*output
= mpd_recv_output(m_connection
))
767 f(Output(mpd_output_get_name(output
), mpd_output_get_enabled(output
)));
768 mpd_output_free(output
);
770 mpd_response_finish(m_connection
);
774 void Connection::EnableOutput(int id
)
776 prechecksNoCommandsList();
777 mpd_run_enable_output(m_connection
, id
);
781 void Connection::DisableOutput(int id
)
783 prechecksNoCommandsList();
784 mpd_run_disable_output(m_connection
, id
);
788 void Connection::GetURLHandlers(StringConsumer f
)
790 prechecksNoCommandsList();
791 mpd_send_list_url_schemes(m_connection
);
792 while (mpd_pair
*handler
= mpd_recv_pair_named(m_connection
, "handler"))
794 f(std::string(handler
->value
));
795 mpd_return_pair(m_connection
, handler
);
797 mpd_response_finish(m_connection
);
801 void Connection::GetTagTypes(StringConsumer f
)
804 prechecksNoCommandsList();
805 mpd_send_list_tag_types(m_connection
);
806 while (mpd_pair
*tag_type
= mpd_recv_pair_named(m_connection
, "tagtype"))
808 f(std::string(tag_type
->value
));
809 mpd_return_pair(m_connection
, tag_type
);
811 mpd_response_finish(m_connection
);
815 void Connection::checkConnection() const
818 throw ClientError(MPD_ERROR_STATE
, "No active MPD connection", false);
821 void Connection::prechecks()
827 void Connection::prechecksNoCommandsList()
829 assert(!m_command_list_active
);
833 void Connection::checkErrors() const
835 mpd_error code
= mpd_connection_get_error(m_connection
);
836 if (code
!= MPD_ERROR_SUCCESS
)
838 std::string msg
= mpd_connection_get_error_message(m_connection
);
839 if (code
== MPD_ERROR_SERVER
)
841 mpd_server_error server_code
= mpd_connection_get_server_error(m_connection
);
842 bool clearable
= mpd_connection_clear_error(m_connection
);
843 throw ServerError(server_code
, msg
, clearable
);
847 bool clearable
= mpd_connection_clear_error(m_connection
);
848 throw ClientError(code
, msg
, clearable
);