1 /***************************************************************************
2 * Copyright (C) 2008 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
27 const char *playlist_max_message
= "playlist is at the max size";
29 Connection::Connection() : isConnected(0),
31 itsMaxPlaylistLength(-1),
45 Connection::~Connection()
48 mpd_closeConnection(itsConnection
);
50 mpd_freeStats(itsOldStats
);
52 mpd_freeStats(itsCurrentStats
);
54 mpd_freeStatus(itsOldStatus
);
56 mpd_freeStatus(itsCurrentStatus
);
60 bool Connection::Connect()
62 if (!isConnected
&& !itsConnection
)
64 itsConnection
= mpd_newConnection(itsHost
.c_str(), itsPort
, itsTimeout
);
68 if (!itsPassword
.empty())
70 return !CheckForErrors();
76 bool Connection::Connected() const
81 void Connection::Disconnect()
84 mpd_closeConnection(itsConnection
);
86 mpd_freeStats(itsOldStats
);
88 mpd_freeStats(itsCurrentStats
);
90 mpd_freeStatus(itsOldStatus
);
92 mpd_freeStatus(itsCurrentStatus
);
99 itsMaxPlaylistLength
= -1;
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);
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()
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())
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;
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
197 mpd_sendUpdateCommand(itsConnection
, (char *) path
.c_str());
198 mpd_finishCommand(itsConnection
);
202 void Connection::Execute(const string
&command
) const
206 mpd_executeCommand(itsConnection
, command
.c_str());
207 mpd_finishCommand(itsConnection
);
211 void Connection::Play() const
217 void Connection::Play(int pos
) const
221 mpd_sendPlayCommand(itsConnection
, pos
);
222 mpd_finishCommand(itsConnection
);
226 void Connection::PlayID(int id
) const
230 mpd_sendPlayIdCommand(itsConnection
, id
);
231 mpd_finishCommand(itsConnection
);
235 void Connection::Pause() const
239 mpd_sendPauseCommand(itsConnection
, itsCurrentStatus
->state
!= psPause
);
240 mpd_finishCommand(itsConnection
);
244 void Connection::Stop() const
248 mpd_sendStopCommand(itsConnection
);
249 mpd_finishCommand(itsConnection
);
253 void Connection::Next() const
257 mpd_sendNextCommand(itsConnection
);
258 mpd_finishCommand(itsConnection
);
262 void Connection::Prev() const
266 mpd_sendPrevCommand(itsConnection
);
267 mpd_finishCommand(itsConnection
);
271 void Connection::Move(int from
, int to
) const
275 mpd_sendMoveCommand(itsConnection
, from
, to
);
276 mpd_finishCommand(itsConnection
);
280 void Connection::Seek(int where
) const
284 mpd_sendSeekCommand(itsConnection
, itsCurrentStatus
->song
, where
);
285 mpd_finishCommand(itsConnection
);
289 void Connection::Shuffle() const
293 mpd_sendShuffleCommand(itsConnection
);
294 mpd_finishCommand(itsConnection
);
298 void Connection::ClearPlaylist() const
302 mpd_sendClearCommand(itsConnection
);
303 mpd_finishCommand(itsConnection
);
307 void Connection::AddToPlaylist(const string
&path
, const Song
&s
) const
310 AddToPlaylist(path
, s
.GetFile());
313 void Connection::AddToPlaylist(const string
&path
, const string
&file
) const
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
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
335 mpd_sendRenameCommand(itsConnection
, from
.c_str(), to
.c_str());
336 mpd_finishCommand(itsConnection
);
340 void Connection::GetPlaylistChanges(long long id
, SongList
&v
) const
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);
359 mpd_freeInfoEntity(item
);
361 mpd_finishCommand(itsConnection
);
365 Song
Connection::GetSong(const string
&path
) const
369 mpd_sendListallInfoCommand(itsConnection
, path
.c_str());
370 mpd_InfoEntity
*item
= NULL
;
371 item
= mpd_getNextInfoEntity(itsConnection
);
372 Song result
= item
->info
.song
;
374 mpd_freeInfoEntity(item
);
375 mpd_finishCommand(itsConnection
);
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
);
396 Song result
= item
->info
.song
;
398 mpd_freeInfoEntity(item
);
403 mpd_finishCommand(itsConnection
);
409 void Connection::GetPlaylistContent(const string
&path
, SongList
&v
) const
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
);
423 mpd_freeInfoEntity(item
);
425 mpd_finishCommand(itsConnection
);
429 void Connection::SetRepeat(bool mode
) const
433 mpd_sendRepeatCommand(itsConnection
, mode
);
434 mpd_finishCommand(itsConnection
);
438 void Connection::SetRandom(bool mode
) const
442 mpd_sendRandomCommand(itsConnection
, mode
);
443 mpd_finishCommand(itsConnection
);
447 void Connection::SetVolume(int vol
) const
451 mpd_sendSetvolCommand(itsConnection
, vol
);
452 mpd_finishCommand(itsConnection
);
456 void Connection::SetCrossfade(int crossfade
) const
460 mpd_sendCrossfadeCommand(itsConnection
, crossfade
);
461 mpd_finishCommand(itsConnection
);
465 int Connection::AddSong(const string
&path
)
470 if (GetPlaylistLength() < itsMaxPlaylistLength
)
472 id
= mpd_sendAddIdCommand(itsConnection
, path
.c_str());
473 mpd_finishCommand(itsConnection
);
478 itsErrorHandler(this, MPD_ACK_ERROR_PLAYLIST_MAX
, playlist_max_message
, NULL
);
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
;
495 itsQueue
.push_back(q
);
499 void Connection::QueueAddSong(const Song
&s
)
502 QueueAddSong(s
.GetFile());
505 void Connection::QueueAddToPlaylist(const string
&playlist
, const string
&path
)
509 QueueCommand
*q
= new QueueCommand
;
510 q
->type
= qctAddToPlaylist
;
511 q
->playlist_path
= playlist
;
513 itsQueue
.push_back(q
);
517 void Connection::QueueAddToPlaylist(const string
&playlist
, const Song
&s
)
520 QueueAddToPlaylist(playlist
, s
.GetFile());
523 void Connection::QueueDeleteSong(int id
)
527 QueueCommand
*q
= new QueueCommand
;
530 itsQueue
.push_back(q
);
534 void Connection::QueueDeleteSongId(int id
)
538 QueueCommand
*q
= new QueueCommand
;
539 q
->type
= qctDeleteID
;
541 itsQueue
.push_back(q
);
545 void Connection::QueueMove(int from
, int to
)
549 QueueCommand
*q
= new QueueCommand
;
553 itsQueue
.push_back(q
);
557 void Connection::QueueMove(const string
&playlist
, int from
, int to
)
561 QueueCommand
*q
= new QueueCommand
;
562 q
->type
= qctPlaylistMove
;
563 q
->playlist_path
= playlist
;
566 itsQueue
.push_back(q
);
570 void Connection::QueueDeleteFromPlaylist(const string
&playlist
, int pos
)
574 QueueCommand
*q
= new QueueCommand
;
575 q
->type
= qctDeleteFromPlaylist
;
576 q
->playlist_path
= playlist
;
578 itsQueue
.push_back(q
);
582 bool Connection::CommitQueue()
587 mpd_sendCommandListBegin(itsConnection
);
588 for (std::vector
<QueueCommand
*>::const_iterator it
= itsQueue
.begin(); it
!= itsQueue
.end(); it
++)
593 mpd_sendAddCommand(itsConnection
, (*it
)->item_path
.c_str());
595 case qctAddToPlaylist
:
596 mpd_sendPlaylistAddCommand(itsConnection
, (char *) (*it
)->playlist_path
.c_str(), (char *) (*it
)->item_path
.c_str());
599 mpd_sendDeleteCommand(itsConnection
, (*it
)->id
);
602 mpd_sendDeleteIdCommand(itsConnection
, (*it
)->id
);
605 mpd_sendMoveCommand(itsConnection
, (*it
)->id
, (*it
)->id2
);
607 case qctPlaylistMove
:
608 mpd_sendPlaylistMoveCommand(itsConnection
, (char *) (*it
)->playlist_path
.c_str(), (*it
)->id
, (*it
)->id2
);
610 case qctDeleteFromPlaylist
:
611 mpd_sendPlaylistDeleteCommand(itsConnection
, (char *) (*it
)->playlist_path
.c_str(), (*it
)->id
);
615 mpd_sendCommandListEnd(itsConnection
);
616 mpd_finishCommand(itsConnection
);
618 if (GetPlaylistLength() == itsMaxPlaylistLength
&& itsErrorHandler
)
619 itsErrorHandler(this, MPD_ACK_ERROR_PLAYLIST_MAX
, playlist_max_message
, NULL
);
620 retval
= !itsQueue
.empty();
626 void Connection::DeletePlaylist(const string
&name
) const
630 mpd_sendRmCommand(itsConnection
, name
.c_str());
631 mpd_finishCommand(itsConnection
);
635 bool Connection::SavePlaylist(const string
&name
) const
639 mpd_sendSaveCommand(itsConnection
, name
.c_str());
640 mpd_finishCommand(itsConnection
);
641 return !(itsConnection
->error
== MPD_ERROR_ACK
&& itsConnection
->errorCode
== MPD_ACK_ERROR_EXIST
);
647 void Connection::GetPlaylists(TagList
&v
) const
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
);
662 void Connection::GetList(TagList
&v
, mpd_TagItems type
) const
666 mpd_sendListCommand(itsConnection
, type
, NULL
);
668 while ((item
= mpd_getNextTag(itsConnection
, type
)) != NULL
)
673 mpd_finishCommand(itsConnection
);
677 void Connection::GetArtists(TagList
&v
) const
681 mpd_sendListCommand(itsConnection
, MPD_TABLE_ARTIST
, NULL
);
683 while ((item
= mpd_getNextArtist(itsConnection
)) != NULL
)
688 mpd_finishCommand(itsConnection
);
692 void Connection::GetAlbums(string artist
, TagList
&v
) const
696 mpd_sendListCommand(itsConnection
, MPD_TABLE_ALBUM
, artist
.empty() ? NULL
: artist
.c_str());
698 while ((item
= mpd_getNextAlbum(itsConnection
)) != NULL
)
703 mpd_finishCommand(itsConnection
);
707 void Connection::StartSearch(bool exact_match
) const
710 mpd_startSearch(itsConnection
, exact_match
);
713 void Connection::StartFieldSearch(mpd_TagItems item
)
717 itsSearchedField
= item
;
718 mpd_startFieldSearch(itsConnection
, item
);
722 void Connection::AddSearch(mpd_TagItems item
, const string
&str
) const
725 mpd_addConstraintSearch(itsConnection
, item
, str
.c_str());
728 void Connection::CommitSearch(SongList
&v
) const
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
);
742 mpd_freeInfoEntity(item
);
744 mpd_finishCommand(itsConnection
);
748 void Connection::CommitSearch(TagList
&v
) const
752 mpd_commitSearch(itsConnection
);
754 while ((tag
= mpd_getNextTag(itsConnection
, itsSearchedField
)) != NULL
)
757 if (v
.empty() || v
.back() != s_tag
)
761 mpd_finishCommand(itsConnection
);
765 void Connection::GetDirectory(const string
&path
, ItemList
&v
) const
769 mpd_InfoEntity
*item
= NULL
;
770 mpd_sendLsInfoCommand(itsConnection
, path
.c_str());
771 while ((item
= mpd_getNextInfoEntity(itsConnection
)) != NULL
)
776 case MPD_INFO_ENTITY_TYPE_DIRECTORY
:
777 i
.name
= item
->info
.directory
->path
;
778 i
.type
= itDirectory
;
780 case MPD_INFO_ENTITY_TYPE_SONG
:
781 i
.song
= new Song(item
->info
.song
);
785 case MPD_INFO_ENTITY_TYPE_PLAYLISTFILE
:
786 i
.name
= item
->info
.playlistFile
->path
;
791 mpd_freeInfoEntity(item
);
793 mpd_finishCommand(itsConnection
);
797 void Connection::GetDirectoryRecursive(const string
&path
, SongList
&v
) const
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
);
811 mpd_freeInfoEntity(item
);
813 mpd_finishCommand(itsConnection
);
817 void Connection::GetSongs(const string
&path
, SongList
&v
) const
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
);
831 mpd_freeInfoEntity(item
);
833 mpd_finishCommand(itsConnection
);
837 void Connection::GetDirectories(const string
&path
, TagList
&v
) const
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()
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;
867 itsErrorHandler(this, itsConnection
->errorCode
, itsConnection
->errorStr
, itsErrorHandlerUserdata
);
868 itsErrorCode
= itsConnection
->errorCode
;
873 itsErrorHandler(this, itsConnection
->error
, itsConnection
->errorStr
, itsErrorHandlerUserdata
);
874 itsErrorCode
= itsConnection
->error
;
875 Disconnect(); // the rest of errors are fatal to connection
878 mpd_clearError(itsConnection
);
883 void Connection::ClearQueue()
885 for (std::vector
<QueueCommand
*>::iterator it
= itsQueue
.begin(); it
!= itsQueue
.end(); it
++)
890 void MPD::FreeSongList(SongList
&l
)
892 for (SongList::iterator i
= l
.begin(); i
!= l
.end(); i
++)
897 void MPD::FreeItemList(ItemList
&l
)
899 for (ItemList::iterator i
= l
.begin(); i
!= l
.end(); i
++)