add support for built-in mpd searching in search engine
[ncmpcpp.git] / src / mpdpp.cpp
blobc2b330c53bcf1b5acf879d3700729e71f367ae38
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 MPD::Connection Mpd;
31 const char *MPD::Message::PartOfSongsAdded = "Only part of requested songs' list added to playlist!";
32 const char *MPD::Message::FullPlaylist = "Playlist is full!";
33 const char *MPD::Message::FunctionDisabledFilteringEnabled = "Function disabled due to enabled filtering in playlist";
35 MPD::Connection::Connection() : itsConnection(0),
36 isCommandsListEnabled(0),
37 itsMaxPlaylistLength(-1),
38 isIdle(0),
39 supportsIdle(0),
40 itsHost("localhost"),
41 itsPort(6600),
42 itsTimeout(15),
43 itsCurrentStatus(0),
44 itsOldStatus(0),
45 itsStats(0),
46 itsUpdater(0),
47 itsErrorHandler(0)
51 MPD::Connection::~Connection()
53 if (itsConnection)
54 mpd_connection_free(itsConnection);
55 if (itsStats)
56 mpd_stats_free(itsStats);
57 if (itsOldStatus)
58 mpd_status_free(itsOldStatus);
59 if (itsCurrentStatus)
60 mpd_status_free(itsCurrentStatus);
63 bool MPD::Connection::Connect()
65 if (itsConnection)
66 return true;
67 itsConnection = mpd_connection_new(itsHost.c_str(), itsPort, itsTimeout*1000 /* timeout is in ms now */);
68 if (CheckForErrors())
69 return false;
70 if (!itsPassword.empty())
71 SendPassword();
72 itsFD = mpd_connection_get_fd(itsConnection);
73 supportsIdle = isIdleEnabled && Version() > 13;
74 // in UpdateStatus() we compare it to itsElapsedTimer[0],
75 // and for the first time it has always evaluate to true
76 // so we need it to be zero at this point
77 itsElapsedTimer[1] = 0;
78 return !CheckForErrors();
81 bool MPD::Connection::Connected() const
83 return itsConnection;
86 void MPD::Connection::Disconnect()
88 if (itsConnection)
89 mpd_connection_free(itsConnection);
90 if (itsStats)
91 mpd_stats_free(itsStats);
92 if (itsOldStatus)
93 mpd_status_free(itsOldStatus);
94 if (itsCurrentStatus)
95 mpd_status_free(itsCurrentStatus);
96 itsConnection = 0;
97 isIdle = 0;
98 itsCurrentStatus = 0;
99 itsOldStatus = 0;
100 itsStats = 0;
101 isCommandsListEnabled = 0;
102 itsMaxPlaylistLength = -1;
105 float MPD::Connection::Version() const
107 if (!itsConnection)
108 return 0;
109 const unsigned *version = mpd_connection_get_server_version(itsConnection);
110 return version[1] + version[2]*0.1;
113 void MPD::Connection::SetHostname(const std::string &host)
115 size_t at = host.find("@");
116 if (at != std::string::npos)
118 itsPassword = host.substr(0, at);
119 itsHost = host.substr(at+1);
121 else
122 itsHost = host;
125 bool MPD::Connection::SendPassword()
127 GoBusy();
128 assert(!isCommandsListEnabled);
129 mpd_run_password(itsConnection, itsPassword.c_str());
130 return !CheckForErrors();
133 void MPD::Connection::SetStatusUpdater(StatusUpdater updater, void *data)
135 itsUpdater = updater;
136 itsStatusUpdaterUserdata = data;
139 void MPD::Connection::SetErrorHandler(ErrorHandler handler, void *data)
141 itsErrorHandler = handler;
142 itsErrorHandlerUserdata = data;
145 void MPD::Connection::GoIdle()
147 if (supportsIdle && !isIdle && mpd_send_idle(itsConnection))
148 isIdle = 1;
151 int MPD::Connection::GoBusy()
153 if (isIdle && mpd_send_noidle(itsConnection))
155 isIdle = 0;
156 return mpd_recv_idle(itsConnection, 1);
158 return 0;
161 void MPD::Connection::UpdateStatus()
163 if (!itsConnection)
164 return;
166 int idle_mask = 0;
167 if (isIdle)
169 if (hasData)
171 idle_mask = GoBusy();
172 hasData = 0;
174 else
176 // count local elapsed time as we don't receive
177 // this from mpd while being in idle mode
178 time(&itsElapsedTimer[1]);
179 double diff = difftime(itsElapsedTimer[1], itsElapsedTimer[0]);
180 if (diff >= 1.0 && Mpd.GetState() == psPlay)
182 time(&itsElapsedTimer[0]);
183 itsElapsed += diff;
184 StatusChanges changes;
185 changes.ElapsedTime = 1;
186 if (itsUpdater)
187 itsUpdater(this, changes, itsErrorHandlerUserdata);
189 return;
193 CheckForErrors();
195 if (!itsConnection)
196 return;
198 if (itsOldStatus)
199 mpd_status_free(itsOldStatus);
201 itsOldStatus = itsCurrentStatus;
202 itsCurrentStatus = 0;
204 itsCurrentStatus = mpd_run_status(itsConnection);
206 if (!itsMaxPlaylistLength)
207 itsMaxPlaylistLength = GetPlaylistLength();
209 if (CheckForErrors())
210 return;
212 if (itsCurrentStatus && itsUpdater)
214 if (supportsIdle)
216 // sync local elapsed time counter with mpd
217 unsigned old_elapsed = itsElapsed;
218 itsElapsed = mpd_status_get_elapsed_time(itsCurrentStatus);
219 itsChanges.ElapsedTime = itsElapsed != old_elapsed;
220 time(&itsElapsedTimer[0]);
222 else
223 itsElapsed = mpd_status_get_elapsed_time(itsCurrentStatus);
225 if (!itsOldStatus)
227 itsChanges.Playlist = 1;
228 itsChanges.SongID = 1;
229 itsChanges.Database = 1;
230 itsChanges.DBUpdating = 1;
231 itsChanges.Volume = 1;
232 itsChanges.ElapsedTime = 1;
233 itsChanges.Crossfade = 1;
234 itsChanges.Random = 1;
235 itsChanges.Repeat = 1;
236 itsChanges.Single = 1;
237 itsChanges.Consume = 1;
238 itsChanges.PlayerState = 1;
239 itsChanges.StatusFlags = 1;
240 itsChanges.Outputs = 1;
242 else
244 if (idle_mask != 0)
246 itsChanges.Playlist = idle_mask & MPD_IDLE_QUEUE;
247 itsChanges.Database = idle_mask & MPD_IDLE_DATABASE;
248 itsChanges.DBUpdating = idle_mask & MPD_IDLE_UPDATE;
249 itsChanges.Volume = idle_mask & MPD_IDLE_MIXER;
250 itsChanges.StatusFlags = idle_mask & (MPD_IDLE_OPTIONS | MPD_IDLE_UPDATE);
251 itsChanges.Outputs = idle_mask & MPD_IDLE_OUTPUT;
253 else
255 itsChanges.Playlist = mpd_status_get_queue_version(itsOldStatus)
256 != mpd_status_get_queue_version(itsCurrentStatus);
258 itsChanges.ElapsedTime = mpd_status_get_elapsed_time(itsOldStatus)
259 != mpd_status_get_elapsed_time(itsCurrentStatus);
261 itsChanges.Database = mpd_status_get_update_id(itsOldStatus)
262 && !mpd_status_get_update_id(itsCurrentStatus);
264 itsChanges.DBUpdating = mpd_status_get_update_id(itsOldStatus)
265 != mpd_status_get_update_id(itsCurrentStatus);
267 itsChanges.Volume = mpd_status_get_volume(itsOldStatus)
268 != mpd_status_get_volume(itsCurrentStatus);
270 itsChanges.StatusFlags = itsChanges.Repeat
271 || itsChanges.Random
272 || itsChanges.Single
273 || itsChanges.Consume
274 || itsChanges.Crossfade
275 || itsChanges.DBUpdating;
277 // there is no way to determine if the output has changed or not
278 // from mpd status, it's possible only with idle notifications
279 itsChanges.Outputs = 0;
282 itsChanges.SongID = mpd_status_get_song_id(itsOldStatus)
283 != mpd_status_get_song_id(itsCurrentStatus);
285 itsChanges.Crossfade = mpd_status_get_crossfade(itsOldStatus)
286 != mpd_status_get_crossfade(itsCurrentStatus);
288 itsChanges.Random = mpd_status_get_random(itsOldStatus)
289 != mpd_status_get_random(itsCurrentStatus);
291 itsChanges.Repeat = mpd_status_get_repeat(itsOldStatus)
292 != mpd_status_get_repeat(itsCurrentStatus);
294 itsChanges.Single = mpd_status_get_single(itsOldStatus)
295 != mpd_status_get_single(itsCurrentStatus);
297 itsChanges.Consume = mpd_status_get_consume(itsOldStatus)
298 != mpd_status_get_consume(itsCurrentStatus);
300 itsChanges.PlayerState = mpd_status_get_state(itsOldStatus)
301 != mpd_status_get_state(itsCurrentStatus);
303 itsUpdater(this, itsChanges, itsErrorHandlerUserdata);
304 // status updater could invoke mpd commands that
305 // could fail se we need to check for errors
306 CheckForErrors();
307 GoIdle();
311 void MPD::Connection::UpdateStats()
313 if (!itsConnection)
314 return;
315 assert(!isCommandsListEnabled);
316 GoBusy();
317 if (itsStats)
318 mpd_stats_free(itsStats);
319 itsStats = mpd_run_stats(itsConnection);
320 GoIdle();
323 bool MPD::Connection::UpdateDirectory(const std::string &path)
325 if (!itsConnection)
326 return false;
327 if (!isCommandsListEnabled)
329 GoBusy();
330 bool success = mpd_run_update(itsConnection, path.c_str());
331 if (!supportsIdle && success)
332 UpdateStatus();
333 return success;
335 else
337 assert(!isIdle);
338 return mpd_send_update(itsConnection, path.c_str());
343 void MPD::Connection::Play()
345 if (!itsConnection)
346 return;
347 if (!isCommandsListEnabled)
349 GoBusy();
350 mpd_run_play(itsConnection);
352 else
354 assert(!isIdle);
355 mpd_send_play(itsConnection);
359 void MPD::Connection::Play(int pos)
361 if (!itsConnection)
362 return;
363 if (!isCommandsListEnabled)
365 GoBusy();
366 mpd_run_play_pos(itsConnection, pos);
368 else
370 assert(!isIdle);
371 mpd_send_play_pos(itsConnection, pos);
375 void MPD::Connection::PlayID(int id)
377 if (!itsConnection)
378 return;
379 if (!isCommandsListEnabled)
381 GoBusy();
382 mpd_run_play_id(itsConnection, id);
384 else
386 assert(!isIdle);
387 mpd_send_play_id(itsConnection, id);
391 void MPD::Connection::Pause(bool state)
393 if (!itsConnection)
394 return;
395 if (!isCommandsListEnabled)
397 GoBusy();
398 mpd_run_pause(itsConnection, state);
400 else
402 assert(!isIdle);
403 mpd_send_pause(itsConnection, state);
407 void MPD::Connection::Toggle()
409 if (!itsConnection)
410 return;
411 if (!isCommandsListEnabled)
413 GoBusy();
414 if (isPlaying())
415 mpd_run_toggle_pause(itsConnection);
416 else
417 mpd_run_play(itsConnection);
419 else
421 assert(!isIdle);
422 if (isPlaying())
423 mpd_send_toggle_pause(itsConnection);
424 else
425 mpd_send_play(itsConnection);
429 void MPD::Connection::Stop()
431 if (!itsConnection)
432 return;
433 if (!isCommandsListEnabled)
435 GoBusy();
436 mpd_run_stop(itsConnection);
438 else
440 assert(!isIdle);
441 mpd_send_stop(itsConnection);
445 void MPD::Connection::Next()
447 if (!itsConnection)
448 return;
449 if (!isCommandsListEnabled)
451 GoBusy();
452 mpd_run_next(itsConnection);
454 else
456 assert(!isIdle);
457 mpd_send_next(itsConnection);
461 void MPD::Connection::Prev()
463 if (!itsConnection)
464 return;
465 if (!isCommandsListEnabled)
467 GoBusy();
468 mpd_run_previous(itsConnection);
470 else
472 assert(!isIdle);
473 mpd_send_previous(itsConnection);
477 void MPD::Connection::Move(unsigned from, unsigned to)
479 if (!itsConnection)
480 return;
481 if (!isCommandsListEnabled)
483 GoBusy();
484 mpd_run_move(itsConnection, from, to);
486 else
488 assert(!isIdle);
489 mpd_send_move(itsConnection, from, to);
493 void MPD::Connection::Swap(unsigned from, unsigned to)
495 if (!itsConnection)
496 return;
497 if (!isCommandsListEnabled)
499 GoBusy();
500 mpd_run_swap(itsConnection, from, to);
502 else
504 assert(!isIdle);
505 mpd_send_swap(itsConnection, from, to);
509 void MPD::Connection::Seek(unsigned where)
511 if (!itsConnection)
512 return;
513 if (!isCommandsListEnabled)
515 GoBusy();
516 mpd_run_seek_pos(itsConnection, Mpd.GetCurrentSongPos(), where);
518 else
520 assert(!isIdle);
521 mpd_send_seek_pos(itsConnection, Mpd.GetCurrentSongPos(), where);
525 void MPD::Connection::Shuffle()
527 if (!itsConnection)
528 return;
529 if (!isCommandsListEnabled)
531 GoBusy();
532 mpd_run_shuffle(itsConnection);
534 else
536 assert(!isIdle);
537 mpd_send_shuffle(itsConnection);
541 void MPD::Connection::ClearPlaylist()
543 if (!itsConnection)
544 return;
545 if (!isCommandsListEnabled)
547 GoBusy();
548 mpd_run_clear(itsConnection);
550 else
552 assert(!isIdle);
553 mpd_send_clear(itsConnection);
557 void MPD::Connection::ClearPlaylist(const std::string &playlist)
559 if (!itsConnection)
560 return;
561 if (!isCommandsListEnabled)
563 GoBusy();
564 mpd_run_playlist_clear(itsConnection, playlist.c_str());
566 else
568 mpd_send_playlist_clear(itsConnection, playlist.c_str());
569 assert(!isIdle);
573 void MPD::Connection::AddToPlaylist(const std::string &path, const Song &s)
575 if (!s.Empty())
576 AddToPlaylist(path, s.GetFile());
579 void MPD::Connection::AddToPlaylist(const std::string &path, const std::string &file)
581 if (!itsConnection)
582 return;
583 if (!isCommandsListEnabled)
585 GoBusy();
586 mpd_run_playlist_add(itsConnection, path.c_str(), file.c_str());
588 else
590 assert(!isIdle);
591 mpd_send_playlist_add(itsConnection, path.c_str(), file.c_str());
595 void MPD::Connection::Move(const std::string &path, int from, int to)
597 if (!itsConnection)
598 return;
599 if (!isCommandsListEnabled)
600 GoBusy();
601 else
602 assert(!isIdle);
603 mpd_send_playlist_move(itsConnection, path.c_str(), from, to);
604 if (!isCommandsListEnabled)
605 mpd_response_finish(itsConnection);
608 bool MPD::Connection::Rename(const std::string &from, const std::string &to)
610 if (!itsConnection)
611 return false;
612 if (!isCommandsListEnabled)
614 GoBusy();
615 return mpd_run_rename(itsConnection, from.c_str(), to.c_str());
617 else
619 assert(!isIdle);
620 return mpd_send_rename(itsConnection, from.c_str(), to.c_str());
624 void MPD::Connection::GetPlaylistChanges(unsigned version, SongList &v)
626 if (!itsConnection)
627 return;
628 assert(!isCommandsListEnabled);
629 if (!version)
630 v.reserve(GetPlaylistLength());
631 GoBusy();
632 mpd_send_queue_changes_meta(itsConnection, version);
633 while (mpd_song *s = mpd_recv_song(itsConnection))
634 v.push_back(new Song(s, 1));
635 mpd_response_finish(itsConnection);
636 GoIdle();
639 MPD::Song MPD::Connection::GetSong(const std::string &path)
641 if (!itsConnection)
642 return Song();
643 assert(!isCommandsListEnabled);
644 GoBusy();
645 mpd_send_list_all_meta(itsConnection, path.c_str());
646 mpd_song *s = mpd_recv_song(itsConnection);
647 mpd_response_finish(itsConnection);
648 GoIdle();
649 return Song(s);
652 int MPD::Connection::GetCurrentSongPos() const
654 return itsCurrentStatus && isPlaying() ? mpd_status_get_song_pos(itsCurrentStatus) : -1;
657 MPD::Song MPD::Connection::GetCurrentSong()
659 assert(!isCommandsListEnabled);
660 GoBusy();
661 Song result = Song(itsConnection && isPlaying() ? mpd_run_current_song(itsConnection) : 0);
662 GoIdle();
663 return result;
666 void MPD::Connection::GetPlaylistContent(const std::string &path, SongList &v)
668 if (!itsConnection)
669 return;
670 assert(!isCommandsListEnabled);
671 GoBusy();
672 mpd_send_list_playlist_meta(itsConnection, path.c_str());
673 while (mpd_song *s = mpd_recv_song(itsConnection))
674 v.push_back(new Song(s));
675 mpd_response_finish(itsConnection);
676 GoIdle();
679 void MPD::Connection::SetRepeat(bool mode)
681 if (!itsConnection)
682 return;
683 if (!isCommandsListEnabled)
685 GoBusy();
686 mpd_run_repeat(itsConnection, mode);
688 else
690 assert(!isIdle);
691 mpd_send_repeat(itsConnection, mode);
695 void MPD::Connection::SetRandom(bool mode)
697 if (!itsConnection)
698 return;
699 if (!isCommandsListEnabled)
701 GoBusy();
702 mpd_run_random(itsConnection, mode);
704 else
706 assert(!isIdle);
707 mpd_send_random(itsConnection, mode);
711 void MPD::Connection::SetSingle(bool mode)
713 if (!itsConnection)
714 return;
715 if (!isCommandsListEnabled)
717 GoBusy();
718 mpd_run_single(itsConnection, mode);
720 else
722 assert(!isIdle);
723 mpd_send_single(itsConnection, mode);
727 void MPD::Connection::SetConsume(bool mode)
729 if (!itsConnection)
730 return;
731 if (!isCommandsListEnabled)
733 GoBusy();
734 mpd_run_consume(itsConnection, mode);
736 else
738 assert(!isIdle);
739 mpd_send_consume(itsConnection, mode);
743 void MPD::Connection::SetVolume(unsigned vol)
745 if (!itsConnection || vol > 100)
746 return;
747 assert(!isCommandsListEnabled);
748 GoBusy();
749 if (mpd_run_set_volume(itsConnection, vol) && !supportsIdle)
750 UpdateStatus();
753 std::string MPD::Connection::GetReplayGainMode()
755 if (!itsConnection)
756 return "Unknown";
757 assert(!isCommandsListEnabled);
758 GoBusy();
759 if (!mpd_send_command(itsConnection, "replay_gain_status", NULL))
760 return "Unknown";
761 std::string result;
762 if (mpd_pair *pair = mpd_recv_pair_named(itsConnection, "replay_gain_mode"))
764 result = pair->value;
765 if (!result.empty())
766 result[0] = toupper(result[0]);
767 mpd_return_pair(itsConnection, pair);
769 mpd_response_finish(itsConnection);
770 return result;
773 void MPD::Connection::SetReplayGainMode(ReplayGainMode mode)
775 if (!itsConnection)
776 return;
777 const char *rg_mode;
778 switch (mode)
780 case rgmOff:
781 rg_mode = "off";
782 break;
783 case rgmTrack:
784 rg_mode = "track";
785 break;
786 case rgmAlbum:
787 rg_mode = "album";
788 break;
789 default:
790 FatalError("undefined value of ReplayGainMode!");
792 if (!isCommandsListEnabled)
793 GoBusy();
794 else
795 assert(!isIdle);
796 if (!mpd_send_command(itsConnection, "replay_gain_mode", rg_mode, NULL))
797 return;
798 if (!isCommandsListEnabled)
799 mpd_response_finish(itsConnection);
802 void MPD::Connection::SetCrossfade(unsigned crossfade)
804 if (!itsConnection)
805 return;
806 if (!isCommandsListEnabled)
808 GoBusy();
809 mpd_run_crossfade(itsConnection, crossfade);
811 else
813 assert(!isIdle);
814 mpd_send_crossfade(itsConnection, crossfade);
818 int MPD::Connection::AddSong(const std::string &path, int pos)
820 if (!itsConnection)
821 return -1;
822 int id = -1;
823 if (GetPlaylistLength() < itsMaxPlaylistLength)
825 if (!isCommandsListEnabled)
826 GoBusy();
827 else
828 assert(!isIdle);
829 if (pos < 0)
830 mpd_send_add_id(itsConnection, path.c_str());
831 else
832 mpd_send_add_id_to(itsConnection, path.c_str(), pos);
833 if (!isCommandsListEnabled)
835 id = mpd_recv_song_id(itsConnection);
836 mpd_response_finish(itsConnection);
838 else
839 id = 0;
841 else if (itsErrorHandler)
842 itsErrorHandler(this, MPD_SERVER_ERROR_PLAYLIST_MAX, Message::FullPlaylist, itsErrorHandlerUserdata);
843 return id;
846 int MPD::Connection::AddSong(const Song &s, int pos)
848 return !s.Empty() ? (AddSong((!s.isFromDB() ? "file://" : "") + (s.Localized() ? locale_to_utf_cpy(s.GetFile()) : s.GetFile()), pos)) : -1;
851 void MPD::Connection::Add(const std::string &path)
853 if (!itsConnection)
854 return;
855 if (!isCommandsListEnabled)
857 GoBusy();
858 mpd_run_add(itsConnection, path.c_str());
860 else
862 assert(!isIdle);
863 mpd_send_add(itsConnection, path.c_str());
867 bool MPD::Connection::AddRandomSongs(size_t number)
869 if (!itsConnection && !number)
870 return false;
871 assert(!isCommandsListEnabled);
873 TagList files;
875 GoBusy();
876 mpd_send_list_all(itsConnection, "/");
877 while (mpd_pair *item = mpd_recv_pair_named(itsConnection, "file"))
879 files.push_back(item->value);
880 mpd_return_pair(itsConnection, item);
882 mpd_response_finish(itsConnection);
884 if (number > files.size())
886 if (itsErrorHandler)
887 itsErrorHandler(this, 0, "Requested number of random songs is bigger than size of your library!", itsErrorHandlerUserdata);
888 return false;
890 else
892 srand(time(0));
893 std::random_shuffle(files.begin(), files.end());
894 StartCommandsList();
895 TagList::const_iterator it = files.begin()+rand()%(files.size()-number);
896 for (size_t i = 0; i < number && it != files.end(); ++i)
897 AddSong(*it++);
898 CommitCommandsList();
900 return true;
903 bool MPD::Connection::Delete(unsigned pos)
905 if (!itsConnection)
906 return false;
907 if (!isCommandsListEnabled)
908 GoBusy();
909 else
910 assert(!isIdle);
911 bool result = mpd_send_delete(itsConnection, pos);
912 if (!isCommandsListEnabled)
913 result = mpd_response_finish(itsConnection);
914 return result;
917 bool MPD::Connection::DeleteID(unsigned id)
919 if (!itsConnection)
920 return false;
921 if (!isCommandsListEnabled)
922 GoBusy();
923 else
924 assert(!isIdle);
925 bool result = mpd_send_delete_id(itsConnection, id);
926 if (!isCommandsListEnabled)
927 result = mpd_response_finish(itsConnection);
928 return result;
931 bool MPD::Connection::Delete(const std::string &playlist, unsigned pos)
933 if (!itsConnection)
934 return false;
935 if (!isCommandsListEnabled)
937 GoBusy();
938 return mpd_run_playlist_delete(itsConnection, playlist.c_str(), pos);
940 else
942 assert(!isIdle);
943 return mpd_send_playlist_delete(itsConnection, playlist.c_str(), pos);
947 void MPD::Connection::StartCommandsList()
949 if (!itsConnection)
950 return;
951 assert(!isCommandsListEnabled);
952 GoBusy();
953 mpd_command_list_begin(itsConnection, 1);
954 isCommandsListEnabled = 1;
957 bool MPD::Connection::CommitCommandsList()
959 if (!itsConnection)
960 return false;
961 assert(isCommandsListEnabled);
962 assert(!isIdle);
963 mpd_command_list_end(itsConnection);
964 mpd_response_finish(itsConnection);
965 if (GetPlaylistLength() == itsMaxPlaylistLength && itsErrorHandler)
966 itsErrorHandler(this, MPD_SERVER_ERROR_PLAYLIST_MAX, Message::FullPlaylist, itsErrorHandlerUserdata);
967 isCommandsListEnabled = 0;
968 bool result = !CheckForErrors();
969 UpdateStatus();
970 return result;
973 bool MPD::Connection::DeletePlaylist(const std::string &name)
975 if (!itsConnection)
976 return false;
977 if (!isCommandsListEnabled)
979 GoBusy();
980 return mpd_run_rm(itsConnection, name.c_str());
982 else
984 assert(!isIdle);
985 return mpd_send_rm(itsConnection, name.c_str());
989 bool MPD::Connection::SavePlaylist(const std::string &name)
991 if (!itsConnection)
992 return false;
993 assert(!isCommandsListEnabled);
994 GoBusy();
995 mpd_send_save(itsConnection, name.c_str());
996 mpd_response_finish(itsConnection);
997 return !(mpd_connection_get_error(itsConnection) == MPD_ERROR_SERVER
998 && mpd_connection_get_server_error(itsConnection) == MPD_SERVER_ERROR_EXIST);
1001 void MPD::Connection::GetPlaylists(TagList &v)
1003 if (!itsConnection)
1004 return;
1005 ItemList list;
1006 GetDirectory("/", list);
1007 for (ItemList::const_iterator it = list.begin(); it != list.end(); ++it)
1008 if (it->type == itPlaylist)
1009 v.push_back(it->name);
1010 FreeItemList(list);
1013 void MPD::Connection::GetList(TagList &v, mpd_tag_type type)
1015 if (!itsConnection)
1016 return;
1017 assert(!isCommandsListEnabled);
1018 GoBusy();
1019 mpd_search_db_tags(itsConnection, type);
1020 mpd_search_commit(itsConnection);
1021 while (mpd_pair *item = mpd_recv_pair_tag(itsConnection, type))
1023 v.push_back(item->value);
1024 mpd_return_pair(itsConnection, item);
1026 mpd_response_finish(itsConnection);
1027 GoIdle();
1030 void MPD::Connection::StartSearch(bool exact_match)
1032 if (itsConnection)
1033 mpd_search_db_songs(itsConnection, exact_match);
1036 void MPD::Connection::StartFieldSearch(mpd_tag_type item)
1038 if (itsConnection)
1040 itsSearchedField = item;
1041 mpd_search_db_tags(itsConnection, item);
1045 void MPD::Connection::AddSearch(mpd_tag_type item, const std::string &str) const
1047 // mpd version < 0.14.* doesn't support empty search constraints
1048 if (Version() < 14 && str.empty())
1049 return;
1050 if (itsConnection)
1051 mpd_search_add_tag_constraint(itsConnection, MPD_OPERATOR_DEFAULT, item, str.c_str());
1054 void MPD::Connection::AddSearchAny(const std::string &str) const
1056 assert(!str.empty());
1057 if (itsConnection)
1058 mpd_search_add_any_tag_constraint(itsConnection, MPD_OPERATOR_DEFAULT, str.c_str());
1061 void MPD::Connection::AddSearchURI(const std::string &str) const
1063 assert(!str.empty());
1064 if (itsConnection)
1065 mpd_search_add_uri_constraint(itsConnection, MPD_OPERATOR_DEFAULT, str.c_str());
1068 void MPD::Connection::CommitSearch(SongList &v)
1070 if (!itsConnection)
1071 return;
1072 assert(!isCommandsListEnabled);
1073 GoBusy();
1074 mpd_search_commit(itsConnection);
1075 while (mpd_song *s = mpd_recv_song(itsConnection))
1076 v.push_back(new Song(s));
1077 mpd_response_finish(itsConnection);
1078 GoIdle();
1081 void MPD::Connection::CommitSearch(TagList &v)
1083 if (!itsConnection)
1084 return;
1085 assert(!isCommandsListEnabled);
1086 GoBusy();
1087 mpd_search_commit(itsConnection);
1088 while (mpd_pair *tag = mpd_recv_pair_tag(itsConnection, itsSearchedField))
1090 v.push_back(tag->value);
1091 mpd_return_pair(itsConnection, tag);
1093 mpd_response_finish(itsConnection);
1094 GoIdle();
1097 void MPD::Connection::GetDirectory(const std::string &path, ItemList &v)
1099 if (!itsConnection)
1100 return;
1101 assert(!isCommandsListEnabled);
1102 GoBusy();
1103 mpd_send_list_meta(itsConnection, path.c_str());
1104 while (mpd_entity *item = mpd_recv_entity(itsConnection))
1106 Item it;
1107 switch (mpd_entity_get_type(item))
1109 case MPD_ENTITY_TYPE_DIRECTORY:
1110 it.name = mpd_directory_get_path(mpd_entity_get_directory(item));
1111 it.type = itDirectory;
1112 goto WRITE;
1113 case MPD_ENTITY_TYPE_SONG:
1114 it.song = new Song(mpd_song_dup(mpd_entity_get_song(item)));
1115 it.type = itSong;
1116 goto WRITE;
1117 case MPD_ENTITY_TYPE_PLAYLIST:
1118 it.name = mpd_playlist_get_path(mpd_entity_get_playlist(item));
1119 it.type = itPlaylist;
1120 goto WRITE;
1121 WRITE:
1122 v.push_back(it);
1123 break;
1124 default:
1125 break;
1127 mpd_entity_free(item);
1129 mpd_response_finish(itsConnection);
1130 GoIdle();
1133 void MPD::Connection::GetDirectoryRecursive(const std::string &path, SongList &v)
1135 if (!itsConnection)
1136 return;
1137 assert(!isCommandsListEnabled);
1138 GoBusy();
1139 mpd_send_list_all_meta(itsConnection, path.c_str());
1140 while (mpd_song *s = mpd_recv_song(itsConnection))
1141 v.push_back(new Song(s));
1142 mpd_response_finish(itsConnection);
1143 GoIdle();
1146 void MPD::Connection::GetSongs(const std::string &path, SongList &v)
1148 if (!itsConnection)
1149 return;
1150 assert(!isCommandsListEnabled);
1151 GoBusy();
1152 mpd_send_list_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 MPD::Connection::GetDirectories(const std::string &path, TagList &v)
1161 if (!itsConnection)
1162 return;
1163 assert(!isCommandsListEnabled);
1164 GoBusy();
1165 mpd_send_list_meta(itsConnection, path.c_str());
1166 while (mpd_directory *dir = mpd_recv_directory(itsConnection))
1168 v.push_back(mpd_directory_get_path(dir));
1169 mpd_directory_free(dir);
1171 mpd_response_finish(itsConnection);
1172 GoIdle();
1175 void MPD::Connection::GetOutputs(OutputList &v)
1177 if (!itsConnection)
1178 return;
1179 assert(!isCommandsListEnabled);
1180 GoBusy();
1181 mpd_send_outputs(itsConnection);
1182 while (mpd_output *output = mpd_recv_output(itsConnection))
1184 v.push_back(std::make_pair(mpd_output_get_name(output), mpd_output_get_enabled(output)));
1185 mpd_output_free(output);
1187 mpd_response_finish(itsConnection);
1188 GoIdle();
1191 bool MPD::Connection::EnableOutput(int id)
1193 if (!itsConnection)
1194 return false;
1195 if (!isCommandsListEnabled)
1197 GoBusy();
1198 return mpd_run_enable_output(itsConnection, id);
1200 else
1202 assert(!isIdle);
1203 return mpd_send_enable_output(itsConnection, id);
1207 bool MPD::Connection::DisableOutput(int id)
1209 if (!itsConnection)
1210 return false;
1211 if (!isCommandsListEnabled)
1213 GoBusy();
1214 return mpd_run_disable_output(itsConnection, id);
1216 else
1218 assert(!isIdle);
1219 return mpd_send_disable_output(itsConnection, id);
1223 void MPD::Connection::GetURLHandlers(TagList &v)
1225 if (!itsConnection)
1226 return;
1227 assert(!isCommandsListEnabled);
1228 GoBusy();
1229 mpd_send_list_url_schemes(itsConnection);
1230 while (mpd_pair *handler = mpd_recv_pair_named(itsConnection, "handler"))
1232 v.push_back(handler->value);
1233 mpd_return_pair(itsConnection, handler);
1235 mpd_response_finish(itsConnection);
1236 GoIdle();
1239 void MPD::Connection::GetTagTypes(TagList &v)
1241 if (!itsConnection)
1242 return;
1243 assert(!isCommandsListEnabled);
1244 GoBusy();
1245 mpd_send_list_tag_types(itsConnection);
1246 while (mpd_pair *tag_type = mpd_recv_pair_named(itsConnection, "tagtype"))
1248 v.push_back(tag_type->value);
1249 mpd_return_pair(itsConnection, tag_type);
1251 mpd_response_finish(itsConnection);
1252 GoIdle();
1255 int MPD::Connection::CheckForErrors()
1257 int error_code = 0;
1258 if ((error_code = mpd_connection_get_error(itsConnection)) != MPD_ERROR_SUCCESS)
1260 itsErrorMessage = mpd_connection_get_error_message(itsConnection);
1261 if (error_code == MPD_ERROR_SERVER)
1263 // this is to avoid setting too small max size as we check it before fetching current status
1264 // setting real max playlist length is in UpdateStatus()
1265 error_code = mpd_connection_get_server_error(itsConnection);
1266 if (error_code == MPD_SERVER_ERROR_PLAYLIST_MAX && itsMaxPlaylistLength == size_t(-1))
1267 itsMaxPlaylistLength = 0;
1269 if (!mpd_connection_clear_error(itsConnection))
1270 Disconnect();
1271 if (itsErrorHandler)
1272 itsErrorHandler(this, error_code, itsErrorMessage.c_str(), itsErrorHandlerUserdata);
1274 return error_code;
1277 void MPD::FreeSongList(SongList &l)
1279 for (SongList::iterator i = l.begin(); i != l.end(); ++i)
1280 delete *i;
1281 l.clear();
1284 void MPD::FreeItemList(ItemList &l)
1286 for (ItemList::iterator i = l.begin(); i != l.end(); ++i)
1287 delete i->song;
1288 l.clear();