add config options for communication mode with mpd (polling for default)
[ncmpcpp.git] / src / mpdpp.cpp
blobaef39c378522e1c995c28d6fd096e221bf746494
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 # if 1
310 // below conditionals are a hack to workaround mpd bug 2608/2612
311 // by fetching another status with correct values after a while
312 if (!((idle_mask & MPD_IDLE_PLAYER) && !itsChanges.PlayerState))
313 GoIdle();
314 //else if (supportsIdle && !isIdle)
315 // OrderDataFetching();
316 # else
317 GoIdle();
318 # endif
322 void Connection::UpdateStats()
324 if (!itsConnection)
325 return;
326 assert(!isCommandsListEnabled);
327 GoBusy();
328 if (itsStats)
329 mpd_stats_free(itsStats);
330 itsStats = mpd_run_stats(itsConnection);
331 GoIdle();
334 bool Connection::UpdateDirectory(const std::string &path)
336 if (!itsConnection)
337 return false;
338 if (!isCommandsListEnabled)
340 GoBusy();
341 bool success = mpd_run_update(itsConnection, path.c_str());
342 if (!supportsIdle && success)
343 UpdateStatus();
344 return success;
346 else
348 assert(!isIdle);
349 return mpd_send_update(itsConnection, path.c_str());
354 void Connection::Play()
356 if (!itsConnection)
357 return;
358 if (!isCommandsListEnabled)
360 GoBusy();
361 mpd_run_play(itsConnection);
363 else
365 assert(!isIdle);
366 mpd_send_play(itsConnection);
370 void Connection::Play(int pos)
372 if (!itsConnection)
373 return;
374 if (!isCommandsListEnabled)
376 GoBusy();
377 mpd_run_play_pos(itsConnection, pos);
379 else
381 assert(!isIdle);
382 mpd_send_play_pos(itsConnection, pos);
386 void Connection::PlayID(int id)
388 if (!itsConnection)
389 return;
390 if (!isCommandsListEnabled)
392 GoBusy();
393 mpd_run_play_id(itsConnection, id);
395 else
397 assert(!isIdle);
398 mpd_send_play_id(itsConnection, id);
402 void Connection::Pause(bool state)
404 if (!itsConnection)
405 return;
406 if (!isCommandsListEnabled)
408 GoBusy();
409 mpd_run_pause(itsConnection, state);
411 else
413 assert(!isIdle);
414 mpd_send_pause(itsConnection, state);
418 void Connection::Toggle()
420 if (!itsConnection)
421 return;
422 if (!isCommandsListEnabled)
424 GoBusy();
425 mpd_run_toggle_pause(itsConnection);;
427 else
429 assert(!isIdle);
430 mpd_send_toggle_pause(itsConnection);
434 void Connection::Stop()
436 if (!itsConnection)
437 return;
438 if (!isCommandsListEnabled)
440 GoBusy();
441 mpd_run_stop(itsConnection);
443 else
445 assert(!isIdle);
446 mpd_send_stop(itsConnection);
450 void Connection::Next()
452 if (!itsConnection)
453 return;
454 if (!isCommandsListEnabled)
456 GoBusy();
457 mpd_run_next(itsConnection);
459 else
461 assert(!isIdle);
462 mpd_send_next(itsConnection);
466 void Connection::Prev()
468 if (!itsConnection)
469 return;
470 if (!isCommandsListEnabled)
472 GoBusy();
473 mpd_run_previous(itsConnection);
475 else
477 assert(!isIdle);
478 mpd_send_previous(itsConnection);
482 void Connection::Move(unsigned from, unsigned to)
484 if (!itsConnection)
485 return;
486 if (!isCommandsListEnabled)
488 GoBusy();
489 mpd_run_move(itsConnection, from, to);
491 else
493 assert(!isIdle);
494 mpd_send_move(itsConnection, from, to);
498 void Connection::Swap(unsigned from, unsigned to)
500 if (!itsConnection)
501 return;
502 if (!isCommandsListEnabled)
504 GoBusy();
505 mpd_run_swap(itsConnection, from, to);
507 else
509 assert(!isIdle);
510 mpd_send_swap(itsConnection, from, to);
514 void Connection::Seek(unsigned where)
516 if (!itsConnection)
517 return;
518 if (!isCommandsListEnabled)
520 GoBusy();
521 mpd_run_seek_pos(itsConnection, Mpd.GetCurrentSongPos(), where);
523 else
525 assert(!isIdle);
526 mpd_send_seek_pos(itsConnection, Mpd.GetCurrentSongPos(), where);
530 void Connection::Shuffle()
532 if (!itsConnection)
533 return;
534 if (!isCommandsListEnabled)
536 GoBusy();
537 mpd_run_shuffle(itsConnection);
539 else
541 assert(!isIdle);
542 mpd_send_shuffle(itsConnection);
546 void Connection::ClearPlaylist()
548 if (!itsConnection)
549 return;
550 if (!isCommandsListEnabled)
552 GoBusy();
553 mpd_run_clear(itsConnection);
555 else
557 assert(!isIdle);
558 mpd_send_clear(itsConnection);
562 void Connection::ClearPlaylist(const std::string &playlist)
564 if (!itsConnection)
565 return;
566 if (!isCommandsListEnabled)
568 GoBusy();
569 mpd_run_playlist_clear(itsConnection, playlist.c_str());
571 else
573 mpd_send_playlist_clear(itsConnection, playlist.c_str());
574 assert(!isIdle);
578 void Connection::AddToPlaylist(const std::string &path, const Song &s)
580 if (!s.Empty())
581 AddToPlaylist(path, s.GetFile());
584 void Connection::AddToPlaylist(const std::string &path, const std::string &file)
586 if (!itsConnection)
587 return;
588 if (!isCommandsListEnabled)
590 GoBusy();
591 mpd_run_playlist_add(itsConnection, path.c_str(), file.c_str());
593 else
595 assert(!isIdle);
596 mpd_send_playlist_add(itsConnection, path.c_str(), file.c_str());
600 void Connection::Move(const std::string &path, int from, int to)
602 if (!itsConnection)
603 return;
604 if (!isCommandsListEnabled)
605 GoBusy();
606 else
607 assert(!isIdle);
608 mpd_send_playlist_move(itsConnection, path.c_str(), from, to);
609 if (!isCommandsListEnabled)
610 mpd_response_finish(itsConnection);
613 bool Connection::Rename(const std::string &from, const std::string &to)
615 if (!itsConnection)
616 return false;
617 if (!isCommandsListEnabled)
619 GoBusy();
620 return mpd_run_rename(itsConnection, from.c_str(), to.c_str());
622 else
624 assert(!isIdle);
625 return mpd_send_rename(itsConnection, from.c_str(), to.c_str());
629 void Connection::GetPlaylistChanges(unsigned version, SongList &v)
631 if (!itsConnection)
632 return;
633 assert(!isCommandsListEnabled);
634 if (!version)
635 v.reserve(GetPlaylistLength());
636 GoBusy();
637 mpd_send_queue_changes_meta(itsConnection, version);
638 while (mpd_song *s = mpd_recv_song(itsConnection))
639 v.push_back(new Song(s, 1));
640 mpd_response_finish(itsConnection);
641 GoIdle();
644 Song Connection::GetSong(const std::string &path)
646 if (!itsConnection)
647 return Song();
648 assert(!isCommandsListEnabled);
649 GoBusy();
650 mpd_send_list_all_meta(itsConnection, path.c_str());
651 mpd_song *s = mpd_recv_song(itsConnection);
652 mpd_response_finish(itsConnection);
653 GoIdle();
654 return Song(s);
657 int Connection::GetCurrentSongPos() const
659 return itsCurrentStatus && isPlaying() ? mpd_status_get_song_pos(itsCurrentStatus) : -1;
662 Song Connection::GetCurrentSong()
664 assert(!isCommandsListEnabled);
665 GoBusy();
666 Song result = Song(itsConnection && isPlaying() ? mpd_run_current_song(itsConnection) : 0);
667 GoIdle();
668 return result;
671 void Connection::GetPlaylistContent(const std::string &path, SongList &v)
673 if (!itsConnection)
674 return;
675 assert(!isCommandsListEnabled);
676 GoBusy();
677 mpd_send_list_playlist_meta(itsConnection, path.c_str());
678 while (mpd_song *s = mpd_recv_song(itsConnection))
679 v.push_back(new Song(s));
680 mpd_response_finish(itsConnection);
681 GoIdle();
684 void Connection::SetRepeat(bool mode)
686 if (!itsConnection)
687 return;
688 if (!isCommandsListEnabled)
690 GoBusy();
691 mpd_run_repeat(itsConnection, mode);
693 else
695 assert(!isIdle);
696 mpd_send_repeat(itsConnection, mode);
700 void Connection::SetRandom(bool mode)
702 if (!itsConnection)
703 return;
704 if (!isCommandsListEnabled)
706 GoBusy();
707 mpd_run_random(itsConnection, mode);
709 else
711 assert(!isIdle);
712 mpd_send_random(itsConnection, mode);
716 void Connection::SetSingle(bool mode)
718 if (!itsConnection)
719 return;
720 if (!isCommandsListEnabled)
722 GoBusy();
723 mpd_run_single(itsConnection, mode);
725 else
727 assert(!isIdle);
728 mpd_send_single(itsConnection, mode);
732 void Connection::SetConsume(bool mode)
734 if (!itsConnection)
735 return;
736 if (!isCommandsListEnabled)
738 GoBusy();
739 mpd_run_consume(itsConnection, mode);
741 else
743 assert(!isIdle);
744 mpd_send_consume(itsConnection, mode);
748 void Connection::SetVolume(unsigned vol)
750 if (!itsConnection || vol > 100)
751 return;
752 assert(!isCommandsListEnabled);
753 GoBusy();
754 if (mpd_run_set_volume(itsConnection, vol) && !supportsIdle)
755 UpdateStatus();
758 std::string Connection::GetReplayGainMode()
760 if (!itsConnection)
761 return "Unknown";
762 assert(!isCommandsListEnabled);
763 GoBusy();
764 if (!mpd_send_command(itsConnection, "replay_gain_status", NULL))
765 return "Unknown";
766 std::string result;
767 if (mpd_pair *pair = mpd_recv_pair_named(itsConnection, "replay_gain_mode"))
769 result = pair->value;
770 if (!result.empty())
771 result[0] = toupper(result[0]);
772 mpd_return_pair(itsConnection, pair);
774 mpd_response_finish(itsConnection);
775 return result;
778 void Connection::SetReplayGainMode(ReplayGainMode mode)
780 if (!itsConnection)
781 return;
782 const char *rg_mode;
783 switch (mode)
785 case rgmOff:
786 rg_mode = "off";
787 break;
788 case rgmTrack:
789 rg_mode = "track";
790 break;
791 case rgmAlbum:
792 rg_mode = "album";
793 break;
794 default:
795 FatalError("undefined value of ReplayGainMode!");
797 if (!isCommandsListEnabled)
798 GoBusy();
799 else
800 assert(!isIdle);
801 if (!mpd_send_command(itsConnection, "replay_gain_mode", rg_mode, NULL))
802 return;
803 if (!isCommandsListEnabled)
804 mpd_response_finish(itsConnection);
807 void Connection::SetCrossfade(unsigned crossfade)
809 if (!itsConnection)
810 return;
811 if (!isCommandsListEnabled)
813 GoBusy();
814 mpd_run_crossfade(itsConnection, crossfade);
816 else
818 assert(!isIdle);
819 mpd_send_crossfade(itsConnection, crossfade);
823 int Connection::AddSong(const std::string &path, int pos)
825 if (!itsConnection)
826 return -1;
827 int id = -1;
828 if (GetPlaylistLength() < itsMaxPlaylistLength)
830 if (!isCommandsListEnabled)
831 GoBusy();
832 else
833 assert(!isIdle);
834 if (pos < 0)
835 mpd_send_add_id(itsConnection, path.c_str());
836 else
837 mpd_send_add_id_to(itsConnection, path.c_str(), pos);
838 if (!isCommandsListEnabled)
840 id = mpd_recv_song_id(itsConnection);
841 mpd_response_finish(itsConnection);
843 else
844 id = 0;
846 else if (itsErrorHandler)
847 itsErrorHandler(this, MPD_SERVER_ERROR_PLAYLIST_MAX, Message::FullPlaylist, itsErrorHandlerUserdata);
848 return id;
851 int Connection::AddSong(const Song &s, int pos)
853 return !s.Empty() ? (AddSong((!s.isFromDB() ? "file://" : "") + (s.Localized() ? locale_to_utf_cpy(s.GetFile()) : s.GetFile()), pos)) : -1;
856 void Connection::Add(const std::string &path)
858 if (!itsConnection)
859 return;
860 if (!isCommandsListEnabled)
862 GoBusy();
863 mpd_run_add(itsConnection, path.c_str());
865 else
867 assert(!isIdle);
868 mpd_send_add(itsConnection, path.c_str());
872 bool Connection::AddRandomSongs(size_t number)
874 if (!itsConnection && !number)
875 return false;
876 assert(!isCommandsListEnabled);
878 TagList files;
880 GoBusy();
881 mpd_send_list_all(itsConnection, "/");
882 while (mpd_pair *item = mpd_recv_pair_named(itsConnection, "file"))
884 files.push_back(item->value);
885 mpd_return_pair(itsConnection, item);
887 mpd_response_finish(itsConnection);
889 if (number > files.size())
891 if (itsErrorHandler)
892 itsErrorHandler(this, 0, "Requested number of random songs is bigger than size of your library!", itsErrorHandlerUserdata);
893 return false;
895 else
897 srand(time(0));
898 std::random_shuffle(files.begin(), files.end());
899 StartCommandsList();
900 TagList::const_iterator it = files.begin()+rand()%(files.size()-number);
901 for (size_t i = 0; i < number && it != files.end(); ++i)
902 AddSong(*it++);
903 CommitCommandsList();
905 return true;
908 bool Connection::Delete(unsigned pos)
910 if (!itsConnection)
911 return false;
912 if (!isCommandsListEnabled)
913 GoBusy();
914 else
915 assert(!isIdle);
916 bool result = mpd_send_delete(itsConnection, pos);
917 if (!isCommandsListEnabled)
918 result = mpd_response_finish(itsConnection);
919 return result;
922 bool Connection::DeleteID(unsigned id)
924 if (!itsConnection)
925 return false;
926 if (!isCommandsListEnabled)
927 GoBusy();
928 else
929 assert(!isIdle);
930 bool result = mpd_send_delete_id(itsConnection, id);
931 if (!isCommandsListEnabled)
932 result = mpd_response_finish(itsConnection);
933 return result;
936 bool Connection::Delete(const std::string &playlist, unsigned pos)
938 if (!itsConnection)
939 return false;
940 if (!isCommandsListEnabled)
942 GoBusy();
943 return mpd_run_playlist_delete(itsConnection, playlist.c_str(), pos);
945 else
947 assert(!isIdle);
948 return mpd_send_playlist_delete(itsConnection, playlist.c_str(), pos);
952 void Connection::StartCommandsList()
954 if (!itsConnection)
955 return;
956 assert(!isCommandsListEnabled);
957 GoBusy();
958 mpd_command_list_begin(itsConnection, 1);
959 isCommandsListEnabled = 1;
962 bool Connection::CommitCommandsList()
964 if (!itsConnection)
965 return false;
966 assert(isCommandsListEnabled);
967 assert(!isIdle);
968 mpd_command_list_end(itsConnection);
969 mpd_response_finish(itsConnection);
970 if (GetPlaylistLength() == itsMaxPlaylistLength && itsErrorHandler)
971 itsErrorHandler(this, MPD_SERVER_ERROR_PLAYLIST_MAX, Message::FullPlaylist, itsErrorHandlerUserdata);
972 isCommandsListEnabled = 0;
973 bool result = !CheckForErrors();
974 UpdateStatus();
975 return result;
978 bool Connection::DeletePlaylist(const std::string &name)
980 if (!itsConnection)
981 return false;
982 if (!isCommandsListEnabled)
984 GoBusy();
985 return mpd_run_rm(itsConnection, name.c_str());
987 else
989 assert(!isIdle);
990 return mpd_send_rm(itsConnection, name.c_str());
994 bool Connection::SavePlaylist(const std::string &name)
996 if (!itsConnection)
997 return false;
998 assert(!isCommandsListEnabled);
999 GoBusy();
1000 mpd_send_save(itsConnection, name.c_str());
1001 mpd_response_finish(itsConnection);
1002 return !(mpd_connection_get_error(itsConnection) == MPD_ERROR_SERVER
1003 && mpd_connection_get_server_error(itsConnection) == MPD_SERVER_ERROR_EXIST);
1006 void Connection::GetPlaylists(TagList &v)
1008 if (!itsConnection)
1009 return;
1010 ItemList list;
1011 GetDirectory("/", list);
1012 for (ItemList::const_iterator it = list.begin(); it != list.end(); ++it)
1013 if (it->type == itPlaylist)
1014 v.push_back(it->name);
1015 FreeItemList(list);
1018 void Connection::GetList(TagList &v, mpd_tag_type type)
1020 if (!itsConnection)
1021 return;
1022 assert(!isCommandsListEnabled);
1023 GoBusy();
1024 mpd_search_db_tags(itsConnection, type);
1025 mpd_search_commit(itsConnection);
1026 while (mpd_pair *item = mpd_recv_pair_tag(itsConnection, type))
1028 if (item->value[0] != 0) // do not push empty item
1029 v.push_back(item->value);
1030 mpd_return_pair(itsConnection, item);
1032 mpd_response_finish(itsConnection);
1033 GoIdle();
1036 void Connection::GetAlbums(const std::string &artist, TagList &v)
1038 if (!itsConnection)
1039 return;
1040 assert(!isCommandsListEnabled);
1041 GoBusy();
1042 mpd_search_db_tags(itsConnection, MPD_TAG_ALBUM);
1043 if (!artist.empty())
1044 mpd_search_add_tag_constraint(itsConnection, MPD_OPERATOR_DEFAULT, MPD_TAG_ARTIST, artist.c_str());
1045 mpd_search_commit(itsConnection);
1046 while (mpd_pair *item = mpd_recv_pair_tag(itsConnection, MPD_TAG_ALBUM))
1048 if (item->value[0] != 0) // do not push empty item
1049 v.push_back(item->value);
1050 mpd_return_pair(itsConnection, item);
1052 mpd_response_finish(itsConnection);
1053 GoIdle();
1056 void Connection::StartSearch(bool exact_match)
1058 if (itsConnection)
1059 mpd_search_db_songs(itsConnection, exact_match);
1062 void Connection::StartFieldSearch(mpd_tag_type item)
1064 if (itsConnection)
1066 itsSearchedField = item;
1067 mpd_search_db_tags(itsConnection, item);
1071 void Connection::AddSearch(mpd_tag_type item, const std::string &str)
1073 // mpd version < 0.14.* doesn't support empty search constraints
1074 if (Version() < 14 && str.empty())
1075 return;
1076 if (itsConnection)
1077 mpd_search_add_tag_constraint(itsConnection, MPD_OPERATOR_DEFAULT, item, str.c_str());
1080 void Connection::CommitSearch(SongList &v)
1082 if (!itsConnection)
1083 return;
1084 assert(!isCommandsListEnabled);
1085 GoBusy();
1086 mpd_search_commit(itsConnection);
1087 while (mpd_song *s = mpd_recv_song(itsConnection))
1088 v.push_back(new Song(s));
1089 mpd_response_finish(itsConnection);
1090 GoIdle();
1093 void Connection::CommitSearch(TagList &v)
1095 if (!itsConnection)
1096 return;
1097 assert(!isCommandsListEnabled);
1098 GoBusy();
1099 mpd_search_commit(itsConnection);
1100 while (mpd_pair *tag = mpd_recv_pair_tag(itsConnection, itsSearchedField))
1102 if (tag->value[0] != 0) // do not push empty item
1103 v.push_back(tag->value);
1104 mpd_return_pair(itsConnection, tag);
1106 mpd_response_finish(itsConnection);
1107 GoIdle();
1110 void Connection::GetDirectory(const std::string &path, ItemList &v)
1112 if (!itsConnection)
1113 return;
1114 assert(!isCommandsListEnabled);
1115 GoBusy();
1116 mpd_send_list_meta(itsConnection, path.c_str());
1117 while (mpd_entity *item = mpd_recv_entity(itsConnection))
1119 Item it;
1120 switch (mpd_entity_get_type(item))
1122 case MPD_ENTITY_TYPE_DIRECTORY:
1123 it.name = mpd_directory_get_path(mpd_entity_get_directory(item));
1124 it.type = itDirectory;
1125 goto WRITE;
1126 case MPD_ENTITY_TYPE_SONG:
1127 it.song = new Song(mpd_song_dup(mpd_entity_get_song(item)));
1128 it.type = itSong;
1129 goto WRITE;
1130 case MPD_ENTITY_TYPE_PLAYLIST:
1131 it.name = mpd_playlist_get_path(mpd_entity_get_playlist(item));
1132 it.type = itPlaylist;
1133 goto WRITE;
1134 WRITE:
1135 v.push_back(it);
1136 break;
1137 default:
1138 break;
1140 mpd_entity_free(item);
1142 mpd_response_finish(itsConnection);
1143 GoIdle();
1146 void Connection::GetDirectoryRecursive(const std::string &path, SongList &v)
1148 if (!itsConnection)
1149 return;
1150 assert(!isCommandsListEnabled);
1151 GoBusy();
1152 mpd_send_list_all_meta(itsConnection, path.c_str());
1153 while (mpd_song *s = mpd_recv_song(itsConnection))
1154 v.push_back(new Song(s));
1155 mpd_response_finish(itsConnection);
1156 GoIdle();
1159 void Connection::GetSongs(const std::string &path, SongList &v)
1161 if (!itsConnection)
1162 return;
1163 assert(!isCommandsListEnabled);
1164 GoBusy();
1165 mpd_send_list_meta(itsConnection, path.c_str());
1166 while (mpd_song *s = mpd_recv_song(itsConnection))
1167 v.push_back(new Song(s));
1168 mpd_response_finish(itsConnection);
1169 GoIdle();
1172 void Connection::GetDirectories(const std::string &path, TagList &v)
1174 if (!itsConnection)
1175 return;
1176 assert(!isCommandsListEnabled);
1177 GoBusy();
1178 mpd_send_list_meta(itsConnection, path.c_str());
1179 while (mpd_directory *dir = mpd_recv_directory(itsConnection))
1181 v.push_back(mpd_directory_get_path(dir));
1182 mpd_directory_free(dir);
1184 mpd_response_finish(itsConnection);
1185 GoIdle();
1188 void Connection::GetOutputs(OutputList &v)
1190 if (!itsConnection)
1191 return;
1192 assert(!isCommandsListEnabled);
1193 GoBusy();
1194 mpd_send_outputs(itsConnection);
1195 while (mpd_output *output = mpd_recv_output(itsConnection))
1197 v.push_back(std::make_pair(mpd_output_get_name(output), mpd_output_get_enabled(output)));
1198 mpd_output_free(output);
1200 mpd_response_finish(itsConnection);
1201 GoIdle();
1204 bool Connection::EnableOutput(int id)
1206 if (!itsConnection)
1207 return false;
1208 if (!isCommandsListEnabled)
1210 GoBusy();
1211 return mpd_run_enable_output(itsConnection, id);
1213 else
1215 assert(!isIdle);
1216 return mpd_send_enable_output(itsConnection, id);
1220 bool Connection::DisableOutput(int id)
1222 if (!itsConnection)
1223 return false;
1224 if (!isCommandsListEnabled)
1226 GoBusy();
1227 return mpd_run_disable_output(itsConnection, id);
1229 else
1231 assert(!isIdle);
1232 return mpd_send_disable_output(itsConnection, id);
1236 void Connection::GetURLHandlers(TagList &v)
1238 if (!itsConnection)
1239 return;
1240 assert(!isCommandsListEnabled);
1241 GoBusy();
1242 mpd_send_list_url_schemes(itsConnection);
1243 while (mpd_pair *handler = mpd_recv_pair_named(itsConnection, "handler"))
1245 v.push_back(handler->value);
1246 mpd_return_pair(itsConnection, handler);
1248 mpd_response_finish(itsConnection);
1249 GoIdle();
1252 void Connection::GetTagTypes(TagList &v)
1254 if (!itsConnection)
1255 return;
1256 assert(!isCommandsListEnabled);
1257 GoBusy();
1258 mpd_send_list_tag_types(itsConnection);
1259 while (mpd_pair *tag_type = mpd_recv_pair_named(itsConnection, "tagtype"))
1261 v.push_back(tag_type->value);
1262 mpd_return_pair(itsConnection, tag_type);
1264 mpd_response_finish(itsConnection);
1265 GoIdle();
1268 int Connection::CheckForErrors()
1270 int error_code = 0;
1271 if ((error_code = mpd_connection_get_error(itsConnection)) != MPD_ERROR_SUCCESS)
1273 itsErrorMessage = mpd_connection_get_error_message(itsConnection);
1274 if (error_code == MPD_ERROR_SERVER)
1276 // this is to avoid setting too small max size as we check it before fetching current status
1277 // setting real max playlist length is in UpdateStatus()
1278 error_code = mpd_connection_get_server_error(itsConnection);
1279 if (error_code == MPD_SERVER_ERROR_PLAYLIST_MAX && itsMaxPlaylistLength == size_t(-1))
1280 itsMaxPlaylistLength = 0;
1282 if (!mpd_connection_clear_error(itsConnection))
1283 Disconnect();
1284 if (itsErrorHandler)
1285 itsErrorHandler(this, error_code, itsErrorMessage.c_str(), itsErrorHandlerUserdata);
1287 return error_code;
1290 void MPD::FreeSongList(SongList &l)
1292 for (SongList::iterator i = l.begin(); i != l.end(); ++i)
1293 delete *i;
1294 l.clear();
1297 void MPD::FreeItemList(ItemList &l)
1299 for (ItemList::iterator i = l.begin(); i != l.end(); ++i)
1300 delete i->song;
1301 l.clear();