some improvements for handling mpd connection
[ncmpcpp/cirrus.git] / src / mpdpp.cpp
blobb2ebfea03aabd44df0052ce55c1114d1fc506a4e
1 /***************************************************************************
2 * Copyright (C) 2008 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 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include "mpdpp.h"
23 using namespace MPD;
25 using std::string;
27 const char *playlist_max_message = "playlist is at the max size";
29 Connection::Connection() : isConnected(0),
30 itsErrorCode(0),
31 itsMaxPlaylistLength(-1),
32 itsHost("localhost"),
33 itsPort(6600),
34 itsTimeout(15),
35 itsUpdater(0),
36 itsErrorHandler(0)
38 itsConnection = 0;
39 itsCurrentStats = 0;
40 itsOldStats = 0;
41 itsCurrentStatus = 0;
42 itsOldStatus = 0;
45 Connection::~Connection()
47 if (itsConnection)
48 mpd_closeConnection(itsConnection);
49 if (itsOldStats)
50 mpd_freeStats(itsOldStats);
51 if (itsCurrentStats)
52 mpd_freeStats(itsCurrentStats);
53 if (itsOldStatus)
54 mpd_freeStatus(itsOldStatus);
55 if (itsCurrentStatus)
56 mpd_freeStatus(itsCurrentStatus);
57 ClearQueue();
60 bool Connection::Connect()
62 if (!isConnected && !itsConnection)
64 itsConnection = mpd_newConnection(itsHost.c_str(), itsPort, itsTimeout);
65 isConnected = 1;
66 if (CheckForErrors())
67 return false;
68 if (!itsPassword.empty())
69 SendPassword();
70 return !CheckForErrors();
72 else
73 return true;
76 bool Connection::Connected() const
78 return isConnected;
81 void Connection::Disconnect()
83 if (itsConnection)
84 mpd_closeConnection(itsConnection);
85 if (itsOldStats)
86 mpd_freeStats(itsOldStats);
87 if (itsCurrentStats)
88 mpd_freeStats(itsCurrentStats);
89 if (itsOldStatus)
90 mpd_freeStatus(itsOldStatus);
91 if (itsCurrentStatus)
92 mpd_freeStatus(itsCurrentStatus);
93 itsConnection = 0;
94 itsCurrentStats = 0;
95 itsOldStats = 0;
96 itsCurrentStatus = 0;
97 itsOldStatus = 0;
98 isConnected = 0;
99 itsMaxPlaylistLength = -1;
100 ClearQueue();
103 void Connection::SetHostname(const string &host)
105 size_t at = host.find("@");
106 if (at != string::npos)
108 itsPassword = host.substr(0, at);
109 itsHost = host.substr(at+1);
111 else
112 itsHost = host;
115 void Connection::SendPassword() const
117 mpd_sendPasswordCommand(itsConnection, itsPassword.c_str());
118 mpd_finishCommand(itsConnection);
121 void Connection::SetStatusUpdater(StatusUpdater updater, void *data)
123 itsUpdater = updater;
124 itsStatusUpdaterUserdata = data;
127 void Connection::SetErrorHandler(ErrorHandler handler, void *data)
129 itsErrorHandler = handler;
130 itsErrorHandlerUserdata = data;
133 void Connection::UpdateStatus()
135 if (!itsConnection)
136 return;
138 CheckForErrors();
140 if (itsOldStatus)
141 mpd_freeStatus(itsOldStatus);
143 itsOldStatus = itsCurrentStatus;
144 itsCurrentStatus = 0;
146 mpd_sendStatusCommand(itsConnection);
147 itsCurrentStatus = mpd_getStatus(itsConnection);
149 if (!itsMaxPlaylistLength)
150 itsMaxPlaylistLength = GetPlaylistLength();
152 if (CheckForErrors())
153 return;
155 if (itsOldStats)
156 mpd_freeStats(itsOldStats);
157 itsOldStats = itsCurrentStats;
158 mpd_sendStatsCommand(itsConnection);
159 itsCurrentStats = mpd_getStats(itsConnection);
161 if (itsCurrentStatus && itsCurrentStats && itsUpdater)
163 if (itsOldStatus == NULL || itsOldStats == NULL)
165 itsChanges.Playlist = 1;
166 itsChanges.SongID = 1;
167 itsChanges.Database = 1;
168 itsChanges.DBUpdating = 1;
169 itsChanges.Volume = 1;
170 itsChanges.ElapsedTime = 1;
171 itsChanges.Crossfade = 1;
172 itsChanges.Random = 1;
173 itsChanges.Repeat = 1;
174 itsChanges.PlayerState = 1;
176 else
178 itsChanges.Playlist = itsOldStatus->playlist != itsCurrentStatus->playlist;
179 itsChanges.SongID = itsOldStatus->songid != itsCurrentStatus->songid;
180 itsChanges.Database = itsOldStats->dbUpdateTime != itsCurrentStats->dbUpdateTime;
181 itsChanges.DBUpdating = itsOldStatus->updatingDb != itsCurrentStatus->updatingDb;
182 itsChanges.Volume = itsOldStatus->volume != itsCurrentStatus->volume;
183 itsChanges.ElapsedTime = itsOldStatus->elapsedTime != itsCurrentStatus->elapsedTime;
184 itsChanges.Crossfade = itsOldStatus->crossfade != itsCurrentStatus->crossfade;
185 itsChanges.Random = itsOldStatus->random != itsCurrentStatus->random;
186 itsChanges.Repeat = itsOldStatus->repeat != itsCurrentStatus->repeat;
187 itsChanges.PlayerState = itsOldStatus->state != itsCurrentStatus->state;
189 itsUpdater(this, itsChanges, itsErrorHandlerUserdata);
193 void Connection::UpdateDirectory(const string &path) const
195 if (isConnected)
197 mpd_sendUpdateCommand(itsConnection, (char *) path.c_str());
198 mpd_finishCommand(itsConnection);
202 void Connection::Execute(const string &command) const
204 if (isConnected)
206 mpd_executeCommand(itsConnection, command.c_str());
207 mpd_finishCommand(itsConnection);
211 void Connection::Play() const
213 if (isConnected)
214 PlayID(-1);
217 void Connection::Play(int pos) const
219 if (isConnected)
221 mpd_sendPlayCommand(itsConnection, pos);
222 mpd_finishCommand(itsConnection);
226 void Connection::PlayID(int id) const
228 if (isConnected)
230 mpd_sendPlayIdCommand(itsConnection, id);
231 mpd_finishCommand(itsConnection);
235 void Connection::Pause() const
237 if (isConnected)
239 mpd_sendPauseCommand(itsConnection, itsCurrentStatus->state != psPause);
240 mpd_finishCommand(itsConnection);
244 void Connection::Stop() const
246 if (isConnected)
248 mpd_sendStopCommand(itsConnection);
249 mpd_finishCommand(itsConnection);
253 void Connection::Next() const
255 if (isConnected)
257 mpd_sendNextCommand(itsConnection);
258 mpd_finishCommand(itsConnection);
262 void Connection::Prev() const
264 if (isConnected)
266 mpd_sendPrevCommand(itsConnection);
267 mpd_finishCommand(itsConnection);
271 void Connection::Move(int from, int to) const
273 if (isConnected)
275 mpd_sendMoveCommand(itsConnection, from, to);
276 mpd_finishCommand(itsConnection);
280 void Connection::Seek(int where) const
282 if (isConnected)
284 mpd_sendSeekCommand(itsConnection, itsCurrentStatus->song, where);
285 mpd_finishCommand(itsConnection);
289 void Connection::Shuffle() const
291 if (isConnected)
293 mpd_sendShuffleCommand(itsConnection);
294 mpd_finishCommand(itsConnection);
298 void Connection::ClearPlaylist() const
300 if (isConnected)
302 mpd_sendClearCommand(itsConnection);
303 mpd_finishCommand(itsConnection);
307 void Connection::AddToPlaylist(const string &path, const Song &s) const
309 if (!s.Empty())
310 AddToPlaylist(path, s.GetFile());
313 void Connection::AddToPlaylist(const string &path, const string &file) const
315 if (isConnected)
317 mpd_sendPlaylistAddCommand(itsConnection, (char *) path.c_str(), (char *) file.c_str());
318 mpd_finishCommand(itsConnection);
322 void Connection::Move(const string &path, int from, int to) const
324 if (isConnected)
326 mpd_sendPlaylistMoveCommand(itsConnection, (char *) path.c_str(), from, to);
327 mpd_finishCommand(itsConnection);
331 void Connection::Rename(const string &from, const string &to) const
333 if (isConnected)
335 mpd_sendRenameCommand(itsConnection, from.c_str(), to.c_str());
336 mpd_finishCommand(itsConnection);
340 void Connection::GetPlaylistChanges(long long id, SongList &v) const
342 if (isConnected)
344 if (id < 0)
346 id = 0;
347 v.reserve(GetPlaylistLength());
349 mpd_sendPlChangesCommand(itsConnection, id);
350 mpd_InfoEntity *item = NULL;
351 while ((item = mpd_getNextInfoEntity(itsConnection)) != NULL)
353 if (item->type == MPD_INFO_ENTITY_TYPE_SONG)
355 Song *s = new Song(item->info.song, 1);
356 item->info.song = 0;
357 v.push_back(s);
359 mpd_freeInfoEntity(item);
361 mpd_finishCommand(itsConnection);
365 Song Connection::GetSong(const string &path) const
367 if (isConnected)
369 mpd_sendListallInfoCommand(itsConnection, path.c_str());
370 mpd_InfoEntity *item = NULL;
371 item = mpd_getNextInfoEntity(itsConnection);
372 Song result = item->info.song;
373 item->info.song = 0;
374 mpd_freeInfoEntity(item);
375 mpd_finishCommand(itsConnection);
376 return result;
378 else
379 return Song();
382 int Connection::GetCurrentSongPos() const
384 return isConnected && itsCurrentStatus ? (itsCurrentStatus->state == psPlay || itsCurrentStatus->state == psPause ? itsCurrentStatus->song : -1) : -1;
387 Song Connection::GetCurrentSong() const
389 if (isConnected && (GetState() == psPlay || GetState() == psPause))
391 mpd_sendCurrentSongCommand(itsConnection);
392 mpd_InfoEntity *item = NULL;
393 item = mpd_getNextInfoEntity(itsConnection);
394 if (item)
396 Song result = item->info.song;
397 item->info.song = 0;
398 mpd_freeInfoEntity(item);
399 return result;
401 else
402 return Song();
403 mpd_finishCommand(itsConnection);
405 else
406 return Song();
409 void Connection::GetPlaylistContent(const string &path, SongList &v) const
411 if (isConnected)
413 mpd_sendListPlaylistInfoCommand(itsConnection, (char *) path.c_str());
414 mpd_InfoEntity *item = NULL;
415 while ((item = mpd_getNextInfoEntity(itsConnection)) != NULL)
417 if (item->type == MPD_INFO_ENTITY_TYPE_SONG)
419 Song *s = new Song(item->info.song);
420 item->info.song = 0;
421 v.push_back(s);
423 mpd_freeInfoEntity(item);
425 mpd_finishCommand(itsConnection);
429 void Connection::SetRepeat(bool mode) const
431 if (isConnected)
433 mpd_sendRepeatCommand(itsConnection, mode);
434 mpd_finishCommand(itsConnection);
438 void Connection::SetRandom(bool mode) const
440 if (isConnected)
442 mpd_sendRandomCommand(itsConnection, mode);
443 mpd_finishCommand(itsConnection);
447 void Connection::SetVolume(int vol) const
449 if (isConnected)
451 mpd_sendSetvolCommand(itsConnection, vol);
452 mpd_finishCommand(itsConnection);
456 void Connection::SetCrossfade(int crossfade) const
458 if (isConnected)
460 mpd_sendCrossfadeCommand(itsConnection, crossfade);
461 mpd_finishCommand(itsConnection);
465 int Connection::AddSong(const string &path)
467 int id = -1;
468 if (isConnected)
470 if (GetPlaylistLength() < itsMaxPlaylistLength)
472 id = mpd_sendAddIdCommand(itsConnection, path.c_str());
473 mpd_finishCommand(itsConnection);
474 UpdateStatus();
476 else
477 if (itsErrorHandler)
478 itsErrorHandler(this, MPD_ACK_ERROR_PLAYLIST_MAX, playlist_max_message, NULL);
480 return id;
483 int Connection::AddSong(const Song &s)
485 return !s.Empty() ? (s.IsFromDB() ? AddSong(s.GetFile()) : AddSong("file://" + string(s.GetFile()))) : -1;
488 void Connection::QueueAddSong(const string &path)
490 if (isConnected && GetPlaylistLength() < itsMaxPlaylistLength)
492 QueueCommand *q = new QueueCommand;
493 q->type = qctAdd;
494 q->item_path = path;
495 itsQueue.push_back(q);
499 void Connection::QueueAddSong(const Song &s)
501 if (!s.Empty())
502 QueueAddSong(s.GetFile());
505 void Connection::QueueAddToPlaylist(const string &playlist, const string &path)
507 if (isConnected)
509 QueueCommand *q = new QueueCommand;
510 q->type = qctAddToPlaylist;
511 q->playlist_path = playlist;
512 q->item_path = path;
513 itsQueue.push_back(q);
517 void Connection::QueueAddToPlaylist(const string &playlist, const Song &s)
519 if (!s.Empty())
520 QueueAddToPlaylist(playlist, s.GetFile());
523 void Connection::QueueDeleteSong(int id)
525 if (isConnected)
527 QueueCommand *q = new QueueCommand;
528 q->type = qctDelete;
529 q->id = id;
530 itsQueue.push_back(q);
534 void Connection::QueueDeleteSongId(int id)
536 if (isConnected)
538 QueueCommand *q = new QueueCommand;
539 q->type = qctDeleteID;
540 q->id = id;
541 itsQueue.push_back(q);
545 void Connection::QueueMove(int from, int to)
547 if (isConnected)
549 QueueCommand *q = new QueueCommand;
550 q->type = qctMove;
551 q->id = from;
552 q->id2 = to;
553 itsQueue.push_back(q);
557 void Connection::QueueMove(const string &playlist, int from, int to)
559 if (isConnected)
561 QueueCommand *q = new QueueCommand;
562 q->type = qctPlaylistMove;
563 q->playlist_path = playlist;
564 q->id = from;
565 q->id2 = to;
566 itsQueue.push_back(q);
570 void Connection::QueueDeleteFromPlaylist(const string &playlist, int pos)
572 if (isConnected)
574 QueueCommand *q = new QueueCommand;
575 q->type = qctDeleteFromPlaylist;
576 q->playlist_path = playlist;
577 q->id = pos;
578 itsQueue.push_back(q);
582 bool Connection::CommitQueue()
584 bool retval = false;
585 if (isConnected)
587 mpd_sendCommandListBegin(itsConnection);
588 for (std::vector<QueueCommand *>::const_iterator it = itsQueue.begin(); it != itsQueue.end(); it++)
590 switch ((*it)->type)
592 case qctAdd:
593 mpd_sendAddCommand(itsConnection, (*it)->item_path.c_str());
594 break;
595 case qctAddToPlaylist:
596 mpd_sendPlaylistAddCommand(itsConnection, (char *) (*it)->playlist_path.c_str(), (char *) (*it)->item_path.c_str());
597 break;
598 case qctDelete:
599 mpd_sendDeleteCommand(itsConnection, (*it)->id);
600 break;
601 case qctDeleteID:
602 mpd_sendDeleteIdCommand(itsConnection, (*it)->id);
603 break;
604 case qctMove:
605 mpd_sendMoveCommand(itsConnection, (*it)->id, (*it)->id2);
606 break;
607 case qctPlaylistMove:
608 mpd_sendPlaylistMoveCommand(itsConnection, (char *) (*it)->playlist_path.c_str(), (*it)->id, (*it)->id2);
609 break;
610 case qctDeleteFromPlaylist:
611 mpd_sendPlaylistDeleteCommand(itsConnection, (char *) (*it)->playlist_path.c_str(), (*it)->id);
612 break;
615 mpd_sendCommandListEnd(itsConnection);
616 mpd_finishCommand(itsConnection);
617 UpdateStatus();
618 if (GetPlaylistLength() == itsMaxPlaylistLength && itsErrorHandler)
619 itsErrorHandler(this, MPD_ACK_ERROR_PLAYLIST_MAX, playlist_max_message, NULL);
620 retval = !itsQueue.empty();
622 ClearQueue();
623 return retval;
626 void Connection::DeletePlaylist(const string &name) const
628 if (isConnected)
630 mpd_sendRmCommand(itsConnection, name.c_str());
631 mpd_finishCommand(itsConnection);
635 bool Connection::SavePlaylist(const string &name) const
637 if (isConnected)
639 mpd_sendSaveCommand(itsConnection, name.c_str());
640 mpd_finishCommand(itsConnection);
641 return !(itsConnection->error == MPD_ERROR_ACK && itsConnection->errorCode == MPD_ACK_ERROR_EXIST);
643 else
644 return false;
647 void Connection::GetPlaylists(TagList &v) const
649 if (isConnected)
651 ItemList list;
652 GetDirectory("/", list);
653 for (ItemList::const_iterator it = list.begin(); it != list.end(); it++)
655 if (it->type == itPlaylist)
656 v.push_back(it->name);
658 FreeItemList(list);
662 void Connection::GetList(TagList &v, mpd_TagItems type) const
664 if (isConnected)
666 mpd_sendListCommand(itsConnection, type, NULL);
667 char *item;
668 while ((item = mpd_getNextTag(itsConnection, type)) != NULL)
670 v.push_back(item);
671 delete [] item;
673 mpd_finishCommand(itsConnection);
677 void Connection::GetArtists(TagList &v) const
679 if (isConnected)
681 mpd_sendListCommand(itsConnection, MPD_TABLE_ARTIST, NULL);
682 char *item;
683 while ((item = mpd_getNextArtist(itsConnection)) != NULL)
685 v.push_back(item);
686 delete [] item;
688 mpd_finishCommand(itsConnection);
692 void Connection::GetAlbums(string artist, TagList &v) const
694 if (isConnected)
696 mpd_sendListCommand(itsConnection, MPD_TABLE_ALBUM, artist.empty() ? NULL : artist.c_str());
697 char *item;
698 while ((item = mpd_getNextAlbum(itsConnection)) != NULL)
700 v.push_back(item);
701 delete [] item;
703 mpd_finishCommand(itsConnection);
707 void Connection::StartSearch(bool exact_match) const
709 if (isConnected)
710 mpd_startSearch(itsConnection, exact_match);
713 void Connection::StartFieldSearch(mpd_TagItems item)
715 if (isConnected)
717 itsSearchedField = item;
718 mpd_startFieldSearch(itsConnection, item);
722 void Connection::AddSearch(mpd_TagItems item, const string &str) const
724 if (isConnected)
725 mpd_addConstraintSearch(itsConnection, item, str.c_str());
728 void Connection::CommitSearch(SongList &v) const
730 if (isConnected)
732 mpd_commitSearch(itsConnection);
733 mpd_InfoEntity *item = NULL;
734 while ((item = mpd_getNextInfoEntity(itsConnection)) != NULL)
736 if (item->type == MPD_INFO_ENTITY_TYPE_SONG)
738 Song *s = new Song(item->info.song);
739 item->info.song = 0;
740 v.push_back(s);
742 mpd_freeInfoEntity(item);
744 mpd_finishCommand(itsConnection);
748 void Connection::CommitSearch(TagList &v) const
750 if (isConnected)
752 mpd_commitSearch(itsConnection);
753 char *tag = NULL;
754 while ((tag = mpd_getNextTag(itsConnection, itsSearchedField)) != NULL)
756 string s_tag = tag;
757 if (v.empty() || v.back() != s_tag)
758 v.push_back(s_tag);
759 delete [] tag;
761 mpd_finishCommand(itsConnection);
765 void Connection::GetDirectory(const string &path, ItemList &v) const
767 if (isConnected)
769 mpd_InfoEntity *item = NULL;
770 mpd_sendLsInfoCommand(itsConnection, path.c_str());
771 while ((item = mpd_getNextInfoEntity(itsConnection)) != NULL)
773 Item i;
774 switch (item->type)
776 case MPD_INFO_ENTITY_TYPE_DIRECTORY:
777 i.name = item->info.directory->path;
778 i.type = itDirectory;
779 break;
780 case MPD_INFO_ENTITY_TYPE_SONG:
781 i.song = new Song(item->info.song);
782 item->info.song = 0;
783 i.type = itSong;
784 break;
785 case MPD_INFO_ENTITY_TYPE_PLAYLISTFILE:
786 i.name = item->info.playlistFile->path;
787 i.type = itPlaylist;
788 break;
790 v.push_back(i);
791 mpd_freeInfoEntity(item);
793 mpd_finishCommand(itsConnection);
797 void Connection::GetDirectoryRecursive(const string &path, SongList &v) const
799 if (isConnected)
801 mpd_InfoEntity *item = NULL;
802 mpd_sendListallInfoCommand(itsConnection, path.c_str());
803 while ((item = mpd_getNextInfoEntity(itsConnection)) != NULL)
805 if (item->type == MPD_INFO_ENTITY_TYPE_SONG)
807 Song *s = new Song(item->info.song);
808 item->info.song = 0;
809 v.push_back(s);
811 mpd_freeInfoEntity(item);
813 mpd_finishCommand(itsConnection);
817 void Connection::GetSongs(const string &path, SongList &v) const
819 if (isConnected)
821 mpd_InfoEntity *item = NULL;
822 mpd_sendLsInfoCommand(itsConnection, path.c_str());
823 while ((item = mpd_getNextInfoEntity(itsConnection)) != NULL)
825 if (item->type == MPD_INFO_ENTITY_TYPE_SONG)
827 Song *s = new Song(item->info.song);
828 item->info.song = 0;
829 v.push_back(s);
831 mpd_freeInfoEntity(item);
833 mpd_finishCommand(itsConnection);
837 void Connection::GetDirectories(const string &path, TagList &v) const
839 if (isConnected)
841 mpd_InfoEntity *item = NULL;
842 mpd_sendLsInfoCommand(itsConnection, path.c_str());
843 while ((item = mpd_getNextInfoEntity(itsConnection)) != NULL)
845 if (item->type == MPD_INFO_ENTITY_TYPE_DIRECTORY)
846 v.push_back(item->info.directory->path);
847 mpd_freeInfoEntity(item);
849 mpd_finishCommand(itsConnection);
853 int Connection::CheckForErrors()
855 itsErrorCode = 0;
856 if (itsConnection->error)
858 itsErrorMessage = itsConnection->errorStr;
859 if (itsConnection->error == MPD_ERROR_ACK)
861 // this is to avoid setting too small max size as we check it before fetching current status
862 // setting real max playlist length is in UpdateStatus()
863 if (itsConnection->errorCode == MPD_ACK_ERROR_PLAYLIST_MAX && itsMaxPlaylistLength == size_t(-1))
864 itsMaxPlaylistLength = 0;
866 if (itsErrorHandler)
867 itsErrorHandler(this, itsConnection->errorCode, itsConnection->errorStr, itsErrorHandlerUserdata);
868 itsErrorCode = itsConnection->errorCode;
870 else
872 if (itsErrorHandler)
873 itsErrorHandler(this, itsConnection->error, itsConnection->errorStr, itsErrorHandlerUserdata);
874 itsErrorCode = itsConnection->error;
875 Disconnect(); // the rest of errors are fatal to connection
877 if (itsConnection)
878 mpd_clearError(itsConnection);
880 return itsErrorCode;
883 void Connection::ClearQueue()
885 for (std::vector<QueueCommand *>::iterator it = itsQueue.begin(); it != itsQueue.end(); it++)
886 delete *it;
887 itsQueue.clear();
890 void MPD::FreeSongList(SongList &l)
892 for (SongList::iterator i = l.begin(); i != l.end(); i++)
893 delete *i;
894 l.clear();
897 void MPD::FreeItemList(ItemList &l)
899 for (ItemList::iterator i = l.begin(); i != l.end(); i++)
900 delete i->song;
901 l.clear();