make MPD::Connection::Toggle() run playback if player is stopped
[ncmpcpp.git] / src / mpdpp.cpp
blobdaa062e3a3adfbfe2e9e82325da36b810662a01a
1 /***************************************************************************
2 * Copyright (C) 2008-2009 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 <cstdlib>
23 #include <algorithm>
25 #include "charset.h"
26 #include "error.h"
27 #include "mpdpp.h"
29 using namespace MPD;
31 MPD::Connection Mpd;
33 const char *MPD::Message::PartOfSongsAdded = "Only part of requested songs' list added to playlist!";
34 const char *MPD::Message::FullPlaylist = "Playlist is full!";
35 const char *MPD::Message::FunctionDisabledFilteringEnabled = "Function disabled due to enabled filtering in playlist";
37 Connection::Connection() : itsConnection(0),
38 isCommandsListEnabled(0),
39 itsMaxPlaylistLength(-1),
40 isIdle(0),
41 supportsIdle(0),
42 itsHost("localhost"),
43 itsPort(6600),
44 itsTimeout(15),
45 itsCurrentStatus(0),
46 itsOldStatus(0),
47 itsStats(0),
48 itsUpdater(0),
49 itsErrorHandler(0)
53 Connection::~Connection()
55 if (itsConnection)
56 mpd_connection_free(itsConnection);
57 if (itsStats)
58 mpd_stats_free(itsStats);
59 if (itsOldStatus)
60 mpd_status_free(itsOldStatus);
61 if (itsCurrentStatus)
62 mpd_status_free(itsCurrentStatus);
65 bool Connection::Connect()
67 if (itsConnection)
68 return true;
69 itsConnection = mpd_connection_new(itsHost.c_str(), itsPort, itsTimeout*1000 /* timeout is in ms now */);
70 if (CheckForErrors())
71 return false;
72 if (!itsPassword.empty())
73 SendPassword();
74 itsFD = mpd_connection_get_fd(itsConnection);
75 supportsIdle = isIdleEnabled && Version() > 13;
76 // in UpdateStatus() we compare it to itsElapsedTimer[0],
77 // and for the first time it has always evaluate to true
78 // so we need it to be zero at this point
79 itsElapsedTimer[1] = 0;
80 return !CheckForErrors();
83 bool Connection::Connected() const
85 return itsConnection;
88 void Connection::Disconnect()
90 if (itsConnection)
91 mpd_connection_free(itsConnection);
92 if (itsStats)
93 mpd_stats_free(itsStats);
94 if (itsOldStatus)
95 mpd_status_free(itsOldStatus);
96 if (itsCurrentStatus)
97 mpd_status_free(itsCurrentStatus);
98 itsConnection = 0;
99 isIdle = 0;
100 itsCurrentStatus = 0;
101 itsOldStatus = 0;
102 itsStats = 0;
103 isCommandsListEnabled = 0;
104 itsMaxPlaylistLength = -1;
107 float Connection::Version() const
109 if (!itsConnection)
110 return 0;
111 const unsigned *version = mpd_connection_get_server_version(itsConnection);
112 return version[1] + version[2]*0.1;
115 void Connection::SetHostname(const std::string &host)
117 size_t at = host.find("@");
118 if (at != std::string::npos)
120 itsPassword = host.substr(0, at);
121 itsHost = host.substr(at+1);
123 else
124 itsHost = host;
127 bool Connection::SendPassword()
129 GoBusy();
130 assert(!isCommandsListEnabled);
131 mpd_run_password(itsConnection, itsPassword.c_str());
132 return !CheckForErrors();
135 void Connection::SetStatusUpdater(StatusUpdater updater, void *data)
137 itsUpdater = updater;
138 itsStatusUpdaterUserdata = data;
141 void Connection::SetErrorHandler(ErrorHandler handler, void *data)
143 itsErrorHandler = handler;
144 itsErrorHandlerUserdata = data;
147 void Connection::GoIdle()
149 if (supportsIdle && !isIdle && mpd_send_idle(itsConnection))
150 isIdle = 1;
153 int Connection::GoBusy()
155 if (isIdle && mpd_send_noidle(itsConnection))
157 isIdle = 0;
158 return mpd_recv_idle(itsConnection, 1);
160 return 0;
163 void Connection::UpdateStatus()
165 if (!itsConnection)
166 return;
168 int idle_mask = 0;
169 if (isIdle)
171 if (hasData)
173 idle_mask = GoBusy();
174 hasData = 0;
176 else
178 // count local elapsed time as we don't receive
179 // this from mpd while being in idle mode
180 time(&itsElapsedTimer[1]);
181 double diff = difftime(itsElapsedTimer[1], itsElapsedTimer[0]);
182 if (diff >= 1.0 && Mpd.GetState() == psPlay)
184 time(&itsElapsedTimer[0]);
185 itsElapsed += diff;
186 StatusChanges changes;
187 changes.ElapsedTime = 1;
188 if (itsUpdater)
189 itsUpdater(this, changes, itsErrorHandlerUserdata);
191 return;
195 CheckForErrors();
197 if (!itsConnection)
198 return;
200 if (itsOldStatus)
201 mpd_status_free(itsOldStatus);
203 itsOldStatus = itsCurrentStatus;
204 itsCurrentStatus = 0;
206 itsCurrentStatus = mpd_run_status(itsConnection);
208 if (!itsMaxPlaylistLength)
209 itsMaxPlaylistLength = GetPlaylistLength();
211 if (CheckForErrors())
212 return;
214 if (itsCurrentStatus && itsUpdater)
216 if (supportsIdle)
218 // sync local elapsed time counter with mpd
219 unsigned old_elapsed = itsElapsed;
220 itsElapsed = mpd_status_get_elapsed_time(itsCurrentStatus);
221 itsChanges.ElapsedTime = itsElapsed != old_elapsed;
222 time(&itsElapsedTimer[0]);
224 else
225 itsElapsed = mpd_status_get_elapsed_time(itsCurrentStatus);
227 if (!itsOldStatus)
229 itsChanges.Playlist = 1;
230 itsChanges.SongID = 1;
231 itsChanges.Database = 1;
232 itsChanges.DBUpdating = 1;
233 itsChanges.Volume = 1;
234 itsChanges.ElapsedTime = 1;
235 itsChanges.Crossfade = 1;
236 itsChanges.Random = 1;
237 itsChanges.Repeat = 1;
238 itsChanges.Single = 1;
239 itsChanges.Consume = 1;
240 itsChanges.PlayerState = 1;
241 itsChanges.StatusFlags = 1;
242 itsChanges.Outputs = 1;
244 else
246 if (idle_mask != 0)
248 itsChanges.Playlist = idle_mask & MPD_IDLE_QUEUE;
249 itsChanges.Database = idle_mask & MPD_IDLE_DATABASE;
250 itsChanges.DBUpdating = idle_mask & MPD_IDLE_UPDATE;
251 itsChanges.Volume = idle_mask & MPD_IDLE_MIXER;
252 itsChanges.StatusFlags = idle_mask & (MPD_IDLE_OPTIONS | MPD_IDLE_UPDATE);
253 itsChanges.Outputs = idle_mask & MPD_IDLE_OUTPUT;
255 else
257 itsChanges.Playlist = mpd_status_get_queue_version(itsOldStatus)
258 != mpd_status_get_queue_version(itsCurrentStatus);
260 itsChanges.ElapsedTime = mpd_status_get_elapsed_time(itsOldStatus)
261 != mpd_status_get_elapsed_time(itsCurrentStatus);
263 itsChanges.Database = mpd_status_get_update_id(itsOldStatus)
264 && !mpd_status_get_update_id(itsCurrentStatus);
266 itsChanges.DBUpdating = mpd_status_get_update_id(itsOldStatus)
267 != mpd_status_get_update_id(itsCurrentStatus);
269 itsChanges.Volume = mpd_status_get_volume(itsOldStatus)
270 != mpd_status_get_volume(itsCurrentStatus);
272 itsChanges.StatusFlags = itsChanges.Repeat
273 || itsChanges.Random
274 || itsChanges.Single
275 || itsChanges.Consume
276 || itsChanges.Crossfade
277 || itsChanges.DBUpdating;
279 // there is no way to determine if the output has changed or not
280 // from mpd status, it's possible only with idle notifications
281 itsChanges.Outputs = 0;
284 itsChanges.SongID = mpd_status_get_song_id(itsOldStatus)
285 != mpd_status_get_song_id(itsCurrentStatus);
287 itsChanges.Crossfade = mpd_status_get_crossfade(itsOldStatus)
288 != mpd_status_get_crossfade(itsCurrentStatus);
290 itsChanges.Random = mpd_status_get_random(itsOldStatus)
291 != mpd_status_get_random(itsCurrentStatus);
293 itsChanges.Repeat = mpd_status_get_repeat(itsOldStatus)
294 != mpd_status_get_repeat(itsCurrentStatus);
296 itsChanges.Single = mpd_status_get_single(itsOldStatus)
297 != mpd_status_get_single(itsCurrentStatus);
299 itsChanges.Consume = mpd_status_get_consume(itsOldStatus)
300 != mpd_status_get_consume(itsCurrentStatus);
302 itsChanges.PlayerState = mpd_status_get_state(itsOldStatus)
303 != mpd_status_get_state(itsCurrentStatus);
305 itsUpdater(this, itsChanges, itsErrorHandlerUserdata);
306 // status updater could invoke mpd commands that
307 // could fail se we need to check for errors
308 CheckForErrors();
309 GoIdle();
313 void Connection::UpdateStats()
315 if (!itsConnection)
316 return;
317 assert(!isCommandsListEnabled);
318 GoBusy();
319 if (itsStats)
320 mpd_stats_free(itsStats);
321 itsStats = mpd_run_stats(itsConnection);
322 GoIdle();
325 bool Connection::UpdateDirectory(const std::string &path)
327 if (!itsConnection)
328 return false;
329 if (!isCommandsListEnabled)
331 GoBusy();
332 bool success = mpd_run_update(itsConnection, path.c_str());
333 if (!supportsIdle && success)
334 UpdateStatus();
335 return success;
337 else
339 assert(!isIdle);
340 return mpd_send_update(itsConnection, path.c_str());
345 void Connection::Play()
347 if (!itsConnection)
348 return;
349 if (!isCommandsListEnabled)
351 GoBusy();
352 mpd_run_play(itsConnection);
354 else
356 assert(!isIdle);
357 mpd_send_play(itsConnection);
361 void Connection::Play(int pos)
363 if (!itsConnection)
364 return;
365 if (!isCommandsListEnabled)
367 GoBusy();
368 mpd_run_play_pos(itsConnection, pos);
370 else
372 assert(!isIdle);
373 mpd_send_play_pos(itsConnection, pos);
377 void Connection::PlayID(int id)
379 if (!itsConnection)
380 return;
381 if (!isCommandsListEnabled)
383 GoBusy();
384 mpd_run_play_id(itsConnection, id);
386 else
388 assert(!isIdle);
389 mpd_send_play_id(itsConnection, id);
393 void Connection::Pause(bool state)
395 if (!itsConnection)
396 return;
397 if (!isCommandsListEnabled)
399 GoBusy();
400 mpd_run_pause(itsConnection, state);
402 else
404 assert(!isIdle);
405 mpd_send_pause(itsConnection, state);
409 void Connection::Toggle()
411 if (!itsConnection)
412 return;
413 if (!isCommandsListEnabled)
415 GoBusy();
416 if (isPlaying())
417 mpd_run_toggle_pause(itsConnection);
418 else
419 mpd_run_play(itsConnection);
421 else
423 assert(!isIdle);
424 if (isPlaying())
425 mpd_send_toggle_pause(itsConnection);
426 else
427 mpd_send_toggle_pause(itsConnection);
431 void Connection::Stop()
433 if (!itsConnection)
434 return;
435 if (!isCommandsListEnabled)
437 GoBusy();
438 mpd_run_stop(itsConnection);
440 else
442 assert(!isIdle);
443 mpd_send_stop(itsConnection);
447 void Connection::Next()
449 if (!itsConnection)
450 return;
451 if (!isCommandsListEnabled)
453 GoBusy();
454 mpd_run_next(itsConnection);
456 else
458 assert(!isIdle);
459 mpd_send_next(itsConnection);
463 void Connection::Prev()
465 if (!itsConnection)
466 return;
467 if (!isCommandsListEnabled)
469 GoBusy();
470 mpd_run_previous(itsConnection);
472 else
474 assert(!isIdle);
475 mpd_send_previous(itsConnection);
479 void Connection::Move(unsigned from, unsigned to)
481 if (!itsConnection)
482 return;
483 if (!isCommandsListEnabled)
485 GoBusy();
486 mpd_run_move(itsConnection, from, to);
488 else
490 assert(!isIdle);
491 mpd_send_move(itsConnection, from, to);
495 void Connection::Swap(unsigned from, unsigned to)
497 if (!itsConnection)
498 return;
499 if (!isCommandsListEnabled)
501 GoBusy();
502 mpd_run_swap(itsConnection, from, to);
504 else
506 assert(!isIdle);
507 mpd_send_swap(itsConnection, from, to);
511 void Connection::Seek(unsigned where)
513 if (!itsConnection)
514 return;
515 if (!isCommandsListEnabled)
517 GoBusy();
518 mpd_run_seek_pos(itsConnection, Mpd.GetCurrentSongPos(), where);
520 else
522 assert(!isIdle);
523 mpd_send_seek_pos(itsConnection, Mpd.GetCurrentSongPos(), where);
527 void Connection::Shuffle()
529 if (!itsConnection)
530 return;
531 if (!isCommandsListEnabled)
533 GoBusy();
534 mpd_run_shuffle(itsConnection);
536 else
538 assert(!isIdle);
539 mpd_send_shuffle(itsConnection);
543 void Connection::ClearPlaylist()
545 if (!itsConnection)
546 return;
547 if (!isCommandsListEnabled)
549 GoBusy();
550 mpd_run_clear(itsConnection);
552 else
554 assert(!isIdle);
555 mpd_send_clear(itsConnection);
559 void Connection::ClearPlaylist(const std::string &playlist)
561 if (!itsConnection)
562 return;
563 if (!isCommandsListEnabled)
565 GoBusy();
566 mpd_run_playlist_clear(itsConnection, playlist.c_str());
568 else
570 mpd_send_playlist_clear(itsConnection, playlist.c_str());
571 assert(!isIdle);
575 void Connection::AddToPlaylist(const std::string &path, const Song &s)
577 if (!s.Empty())
578 AddToPlaylist(path, s.GetFile());
581 void Connection::AddToPlaylist(const std::string &path, const std::string &file)
583 if (!itsConnection)
584 return;
585 if (!isCommandsListEnabled)
587 GoBusy();
588 mpd_run_playlist_add(itsConnection, path.c_str(), file.c_str());
590 else
592 assert(!isIdle);
593 mpd_send_playlist_add(itsConnection, path.c_str(), file.c_str());
597 void Connection::Move(const std::string &path, int from, int to)
599 if (!itsConnection)
600 return;
601 if (!isCommandsListEnabled)
602 GoBusy();
603 else
604 assert(!isIdle);
605 mpd_send_playlist_move(itsConnection, path.c_str(), from, to);
606 if (!isCommandsListEnabled)
607 mpd_response_finish(itsConnection);
610 bool Connection::Rename(const std::string &from, const std::string &to)
612 if (!itsConnection)
613 return false;
614 if (!isCommandsListEnabled)
616 GoBusy();
617 return mpd_run_rename(itsConnection, from.c_str(), to.c_str());
619 else
621 assert(!isIdle);
622 return mpd_send_rename(itsConnection, from.c_str(), to.c_str());
626 void Connection::GetPlaylistChanges(unsigned version, SongList &v)
628 if (!itsConnection)
629 return;
630 assert(!isCommandsListEnabled);
631 if (!version)
632 v.reserve(GetPlaylistLength());
633 GoBusy();
634 mpd_send_queue_changes_meta(itsConnection, version);
635 while (mpd_song *s = mpd_recv_song(itsConnection))
636 v.push_back(new Song(s, 1));
637 mpd_response_finish(itsConnection);
638 GoIdle();
641 Song Connection::GetSong(const std::string &path)
643 if (!itsConnection)
644 return Song();
645 assert(!isCommandsListEnabled);
646 GoBusy();
647 mpd_send_list_all_meta(itsConnection, path.c_str());
648 mpd_song *s = mpd_recv_song(itsConnection);
649 mpd_response_finish(itsConnection);
650 GoIdle();
651 return Song(s);
654 int Connection::GetCurrentSongPos() const
656 return itsCurrentStatus && isPlaying() ? mpd_status_get_song_pos(itsCurrentStatus) : -1;
659 Song Connection::GetCurrentSong()
661 assert(!isCommandsListEnabled);
662 GoBusy();
663 Song result = Song(itsConnection && isPlaying() ? mpd_run_current_song(itsConnection) : 0);
664 GoIdle();
665 return result;
668 void Connection::GetPlaylistContent(const std::string &path, SongList &v)
670 if (!itsConnection)
671 return;
672 assert(!isCommandsListEnabled);
673 GoBusy();
674 mpd_send_list_playlist_meta(itsConnection, path.c_str());
675 while (mpd_song *s = mpd_recv_song(itsConnection))
676 v.push_back(new Song(s));
677 mpd_response_finish(itsConnection);
678 GoIdle();
681 void Connection::SetRepeat(bool mode)
683 if (!itsConnection)
684 return;
685 if (!isCommandsListEnabled)
687 GoBusy();
688 mpd_run_repeat(itsConnection, mode);
690 else
692 assert(!isIdle);
693 mpd_send_repeat(itsConnection, mode);
697 void Connection::SetRandom(bool mode)
699 if (!itsConnection)
700 return;
701 if (!isCommandsListEnabled)
703 GoBusy();
704 mpd_run_random(itsConnection, mode);
706 else
708 assert(!isIdle);
709 mpd_send_random(itsConnection, mode);
713 void Connection::SetSingle(bool mode)
715 if (!itsConnection)
716 return;
717 if (!isCommandsListEnabled)
719 GoBusy();
720 mpd_run_single(itsConnection, mode);
722 else
724 assert(!isIdle);
725 mpd_send_single(itsConnection, mode);
729 void Connection::SetConsume(bool mode)
731 if (!itsConnection)
732 return;
733 if (!isCommandsListEnabled)
735 GoBusy();
736 mpd_run_consume(itsConnection, mode);
738 else
740 assert(!isIdle);
741 mpd_send_consume(itsConnection, mode);
745 void Connection::SetVolume(unsigned vol)
747 if (!itsConnection || vol > 100)
748 return;
749 assert(!isCommandsListEnabled);
750 GoBusy();
751 if (mpd_run_set_volume(itsConnection, vol) && !supportsIdle)
752 UpdateStatus();
755 std::string Connection::GetReplayGainMode()
757 if (!itsConnection)
758 return "Unknown";
759 assert(!isCommandsListEnabled);
760 GoBusy();
761 if (!mpd_send_command(itsConnection, "replay_gain_status", NULL))
762 return "Unknown";
763 std::string result;
764 if (mpd_pair *pair = mpd_recv_pair_named(itsConnection, "replay_gain_mode"))
766 result = pair->value;
767 if (!result.empty())
768 result[0] = toupper(result[0]);
769 mpd_return_pair(itsConnection, pair);
771 mpd_response_finish(itsConnection);
772 return result;
775 void Connection::SetReplayGainMode(ReplayGainMode mode)
777 if (!itsConnection)
778 return;
779 const char *rg_mode;
780 switch (mode)
782 case rgmOff:
783 rg_mode = "off";
784 break;
785 case rgmTrack:
786 rg_mode = "track";
787 break;
788 case rgmAlbum:
789 rg_mode = "album";
790 break;
791 default:
792 FatalError("undefined value of ReplayGainMode!");
794 if (!isCommandsListEnabled)
795 GoBusy();
796 else
797 assert(!isIdle);
798 if (!mpd_send_command(itsConnection, "replay_gain_mode", rg_mode, NULL))
799 return;
800 if (!isCommandsListEnabled)
801 mpd_response_finish(itsConnection);
804 void Connection::SetCrossfade(unsigned crossfade)
806 if (!itsConnection)
807 return;
808 if (!isCommandsListEnabled)
810 GoBusy();
811 mpd_run_crossfade(itsConnection, crossfade);
813 else
815 assert(!isIdle);
816 mpd_send_crossfade(itsConnection, crossfade);
820 int Connection::AddSong(const std::string &path, int pos)
822 if (!itsConnection)
823 return -1;
824 int id = -1;
825 if (GetPlaylistLength() < itsMaxPlaylistLength)
827 if (!isCommandsListEnabled)
828 GoBusy();
829 else
830 assert(!isIdle);
831 if (pos < 0)
832 mpd_send_add_id(itsConnection, path.c_str());
833 else
834 mpd_send_add_id_to(itsConnection, path.c_str(), pos);
835 if (!isCommandsListEnabled)
837 id = mpd_recv_song_id(itsConnection);
838 mpd_response_finish(itsConnection);
840 else
841 id = 0;
843 else if (itsErrorHandler)
844 itsErrorHandler(this, MPD_SERVER_ERROR_PLAYLIST_MAX, Message::FullPlaylist, itsErrorHandlerUserdata);
845 return id;
848 int Connection::AddSong(const Song &s, int pos)
850 return !s.Empty() ? (AddSong((!s.isFromDB() ? "file://" : "") + (s.Localized() ? locale_to_utf_cpy(s.GetFile()) : s.GetFile()), pos)) : -1;
853 void Connection::Add(const std::string &path)
855 if (!itsConnection)
856 return;
857 if (!isCommandsListEnabled)
859 GoBusy();
860 mpd_run_add(itsConnection, path.c_str());
862 else
864 assert(!isIdle);
865 mpd_send_add(itsConnection, path.c_str());
869 bool Connection::AddRandomSongs(size_t number)
871 if (!itsConnection && !number)
872 return false;
873 assert(!isCommandsListEnabled);
875 TagList files;
877 GoBusy();
878 mpd_send_list_all(itsConnection, "/");
879 while (mpd_pair *item = mpd_recv_pair_named(itsConnection, "file"))
881 files.push_back(item->value);
882 mpd_return_pair(itsConnection, item);
884 mpd_response_finish(itsConnection);
886 if (number > files.size())
888 if (itsErrorHandler)
889 itsErrorHandler(this, 0, "Requested number of random songs is bigger than size of your library!", itsErrorHandlerUserdata);
890 return false;
892 else
894 srand(time(0));
895 std::random_shuffle(files.begin(), files.end());
896 StartCommandsList();
897 TagList::const_iterator it = files.begin()+rand()%(files.size()-number);
898 for (size_t i = 0; i < number && it != files.end(); ++i)
899 AddSong(*it++);
900 CommitCommandsList();
902 return true;
905 bool Connection::Delete(unsigned pos)
907 if (!itsConnection)
908 return false;
909 if (!isCommandsListEnabled)
910 GoBusy();
911 else
912 assert(!isIdle);
913 bool result = mpd_send_delete(itsConnection, pos);
914 if (!isCommandsListEnabled)
915 result = mpd_response_finish(itsConnection);
916 return result;
919 bool Connection::DeleteID(unsigned id)
921 if (!itsConnection)
922 return false;
923 if (!isCommandsListEnabled)
924 GoBusy();
925 else
926 assert(!isIdle);
927 bool result = mpd_send_delete_id(itsConnection, id);
928 if (!isCommandsListEnabled)
929 result = mpd_response_finish(itsConnection);
930 return result;
933 bool Connection::Delete(const std::string &playlist, unsigned pos)
935 if (!itsConnection)
936 return false;
937 if (!isCommandsListEnabled)
939 GoBusy();
940 return mpd_run_playlist_delete(itsConnection, playlist.c_str(), pos);
942 else
944 assert(!isIdle);
945 return mpd_send_playlist_delete(itsConnection, playlist.c_str(), pos);
949 void Connection::StartCommandsList()
951 if (!itsConnection)
952 return;
953 assert(!isCommandsListEnabled);
954 GoBusy();
955 mpd_command_list_begin(itsConnection, 1);
956 isCommandsListEnabled = 1;
959 bool Connection::CommitCommandsList()
961 if (!itsConnection)
962 return false;
963 assert(isCommandsListEnabled);
964 assert(!isIdle);
965 mpd_command_list_end(itsConnection);
966 mpd_response_finish(itsConnection);
967 if (GetPlaylistLength() == itsMaxPlaylistLength && itsErrorHandler)
968 itsErrorHandler(this, MPD_SERVER_ERROR_PLAYLIST_MAX, Message::FullPlaylist, itsErrorHandlerUserdata);
969 isCommandsListEnabled = 0;
970 bool result = !CheckForErrors();
971 UpdateStatus();
972 return result;
975 bool Connection::DeletePlaylist(const std::string &name)
977 if (!itsConnection)
978 return false;
979 if (!isCommandsListEnabled)
981 GoBusy();
982 return mpd_run_rm(itsConnection, name.c_str());
984 else
986 assert(!isIdle);
987 return mpd_send_rm(itsConnection, name.c_str());
991 bool Connection::SavePlaylist(const std::string &name)
993 if (!itsConnection)
994 return false;
995 assert(!isCommandsListEnabled);
996 GoBusy();
997 mpd_send_save(itsConnection, name.c_str());
998 mpd_response_finish(itsConnection);
999 return !(mpd_connection_get_error(itsConnection) == MPD_ERROR_SERVER
1000 && mpd_connection_get_server_error(itsConnection) == MPD_SERVER_ERROR_EXIST);
1003 void Connection::GetPlaylists(TagList &v)
1005 if (!itsConnection)
1006 return;
1007 ItemList list;
1008 GetDirectory("/", list);
1009 for (ItemList::const_iterator it = list.begin(); it != list.end(); ++it)
1010 if (it->type == itPlaylist)
1011 v.push_back(it->name);
1012 FreeItemList(list);
1015 void Connection::GetList(TagList &v, mpd_tag_type type)
1017 if (!itsConnection)
1018 return;
1019 assert(!isCommandsListEnabled);
1020 GoBusy();
1021 mpd_search_db_tags(itsConnection, type);
1022 mpd_search_commit(itsConnection);
1023 while (mpd_pair *item = mpd_recv_pair_tag(itsConnection, type))
1025 if (item->value[0] != 0) // do not push empty item
1026 v.push_back(item->value);
1027 mpd_return_pair(itsConnection, item);
1029 mpd_response_finish(itsConnection);
1030 GoIdle();
1033 void Connection::GetAlbums(const std::string &artist, TagList &v)
1035 if (!itsConnection)
1036 return;
1037 assert(!isCommandsListEnabled);
1038 GoBusy();
1039 mpd_search_db_tags(itsConnection, MPD_TAG_ALBUM);
1040 if (!artist.empty())
1041 mpd_search_add_tag_constraint(itsConnection, MPD_OPERATOR_DEFAULT, MPD_TAG_ARTIST, artist.c_str());
1042 mpd_search_commit(itsConnection);
1043 while (mpd_pair *item = mpd_recv_pair_tag(itsConnection, MPD_TAG_ALBUM))
1045 if (item->value[0] != 0) // do not push empty item
1046 v.push_back(item->value);
1047 mpd_return_pair(itsConnection, item);
1049 mpd_response_finish(itsConnection);
1050 GoIdle();
1053 void Connection::StartSearch(bool exact_match)
1055 if (itsConnection)
1056 mpd_search_db_songs(itsConnection, exact_match);
1059 void Connection::StartFieldSearch(mpd_tag_type item)
1061 if (itsConnection)
1063 itsSearchedField = item;
1064 mpd_search_db_tags(itsConnection, item);
1068 void Connection::AddSearch(mpd_tag_type item, const std::string &str)
1070 // mpd version < 0.14.* doesn't support empty search constraints
1071 if (Version() < 14 && str.empty())
1072 return;
1073 if (itsConnection)
1074 mpd_search_add_tag_constraint(itsConnection, MPD_OPERATOR_DEFAULT, item, str.c_str());
1077 void Connection::CommitSearch(SongList &v)
1079 if (!itsConnection)
1080 return;
1081 assert(!isCommandsListEnabled);
1082 GoBusy();
1083 mpd_search_commit(itsConnection);
1084 while (mpd_song *s = mpd_recv_song(itsConnection))
1085 v.push_back(new Song(s));
1086 mpd_response_finish(itsConnection);
1087 GoIdle();
1090 void Connection::CommitSearch(TagList &v)
1092 if (!itsConnection)
1093 return;
1094 assert(!isCommandsListEnabled);
1095 GoBusy();
1096 mpd_search_commit(itsConnection);
1097 while (mpd_pair *tag = mpd_recv_pair_tag(itsConnection, itsSearchedField))
1099 if (tag->value[0] != 0) // do not push empty item
1100 v.push_back(tag->value);
1101 mpd_return_pair(itsConnection, tag);
1103 mpd_response_finish(itsConnection);
1104 GoIdle();
1107 void Connection::GetDirectory(const std::string &path, ItemList &v)
1109 if (!itsConnection)
1110 return;
1111 assert(!isCommandsListEnabled);
1112 GoBusy();
1113 mpd_send_list_meta(itsConnection, path.c_str());
1114 while (mpd_entity *item = mpd_recv_entity(itsConnection))
1116 Item it;
1117 switch (mpd_entity_get_type(item))
1119 case MPD_ENTITY_TYPE_DIRECTORY:
1120 it.name = mpd_directory_get_path(mpd_entity_get_directory(item));
1121 it.type = itDirectory;
1122 goto WRITE;
1123 case MPD_ENTITY_TYPE_SONG:
1124 it.song = new Song(mpd_song_dup(mpd_entity_get_song(item)));
1125 it.type = itSong;
1126 goto WRITE;
1127 case MPD_ENTITY_TYPE_PLAYLIST:
1128 it.name = mpd_playlist_get_path(mpd_entity_get_playlist(item));
1129 it.type = itPlaylist;
1130 goto WRITE;
1131 WRITE:
1132 v.push_back(it);
1133 break;
1134 default:
1135 break;
1137 mpd_entity_free(item);
1139 mpd_response_finish(itsConnection);
1140 GoIdle();
1143 void Connection::GetDirectoryRecursive(const std::string &path, SongList &v)
1145 if (!itsConnection)
1146 return;
1147 assert(!isCommandsListEnabled);
1148 GoBusy();
1149 mpd_send_list_all_meta(itsConnection, path.c_str());
1150 while (mpd_song *s = mpd_recv_song(itsConnection))
1151 v.push_back(new Song(s));
1152 mpd_response_finish(itsConnection);
1153 GoIdle();
1156 void Connection::GetSongs(const std::string &path, SongList &v)
1158 if (!itsConnection)
1159 return;
1160 assert(!isCommandsListEnabled);
1161 GoBusy();
1162 mpd_send_list_meta(itsConnection, path.c_str());
1163 while (mpd_song *s = mpd_recv_song(itsConnection))
1164 v.push_back(new Song(s));
1165 mpd_response_finish(itsConnection);
1166 GoIdle();
1169 void Connection::GetDirectories(const std::string &path, TagList &v)
1171 if (!itsConnection)
1172 return;
1173 assert(!isCommandsListEnabled);
1174 GoBusy();
1175 mpd_send_list_meta(itsConnection, path.c_str());
1176 while (mpd_directory *dir = mpd_recv_directory(itsConnection))
1178 v.push_back(mpd_directory_get_path(dir));
1179 mpd_directory_free(dir);
1181 mpd_response_finish(itsConnection);
1182 GoIdle();
1185 void Connection::GetOutputs(OutputList &v)
1187 if (!itsConnection)
1188 return;
1189 assert(!isCommandsListEnabled);
1190 GoBusy();
1191 mpd_send_outputs(itsConnection);
1192 while (mpd_output *output = mpd_recv_output(itsConnection))
1194 v.push_back(std::make_pair(mpd_output_get_name(output), mpd_output_get_enabled(output)));
1195 mpd_output_free(output);
1197 mpd_response_finish(itsConnection);
1198 GoIdle();
1201 bool Connection::EnableOutput(int id)
1203 if (!itsConnection)
1204 return false;
1205 if (!isCommandsListEnabled)
1207 GoBusy();
1208 return mpd_run_enable_output(itsConnection, id);
1210 else
1212 assert(!isIdle);
1213 return mpd_send_enable_output(itsConnection, id);
1217 bool Connection::DisableOutput(int id)
1219 if (!itsConnection)
1220 return false;
1221 if (!isCommandsListEnabled)
1223 GoBusy();
1224 return mpd_run_disable_output(itsConnection, id);
1226 else
1228 assert(!isIdle);
1229 return mpd_send_disable_output(itsConnection, id);
1233 void Connection::GetURLHandlers(TagList &v)
1235 if (!itsConnection)
1236 return;
1237 assert(!isCommandsListEnabled);
1238 GoBusy();
1239 mpd_send_list_url_schemes(itsConnection);
1240 while (mpd_pair *handler = mpd_recv_pair_named(itsConnection, "handler"))
1242 v.push_back(handler->value);
1243 mpd_return_pair(itsConnection, handler);
1245 mpd_response_finish(itsConnection);
1246 GoIdle();
1249 void Connection::GetTagTypes(TagList &v)
1251 if (!itsConnection)
1252 return;
1253 assert(!isCommandsListEnabled);
1254 GoBusy();
1255 mpd_send_list_tag_types(itsConnection);
1256 while (mpd_pair *tag_type = mpd_recv_pair_named(itsConnection, "tagtype"))
1258 v.push_back(tag_type->value);
1259 mpd_return_pair(itsConnection, tag_type);
1261 mpd_response_finish(itsConnection);
1262 GoIdle();
1265 int Connection::CheckForErrors()
1267 int error_code = 0;
1268 if ((error_code = mpd_connection_get_error(itsConnection)) != MPD_ERROR_SUCCESS)
1270 itsErrorMessage = mpd_connection_get_error_message(itsConnection);
1271 if (error_code == MPD_ERROR_SERVER)
1273 // this is to avoid setting too small max size as we check it before fetching current status
1274 // setting real max playlist length is in UpdateStatus()
1275 error_code = mpd_connection_get_server_error(itsConnection);
1276 if (error_code == MPD_SERVER_ERROR_PLAYLIST_MAX && itsMaxPlaylistLength == size_t(-1))
1277 itsMaxPlaylistLength = 0;
1279 if (!mpd_connection_clear_error(itsConnection))
1280 Disconnect();
1281 if (itsErrorHandler)
1282 itsErrorHandler(this, error_code, itsErrorMessage.c_str(), itsErrorHandlerUserdata);
1284 return error_code;
1287 void MPD::FreeSongList(SongList &l)
1289 for (SongList::iterator i = l.begin(); i != l.end(); ++i)
1290 delete *i;
1291 l.clear();
1294 void MPD::FreeItemList(ItemList &l)
1296 for (ItemList::iterator i = l.begin(); i != l.end(); ++i)
1297 delete i->song;
1298 l.clear();