Update NEWS and bump version to 0.8.2_dev
[ncmpcpp.git] / src / mpdpp.h
blob75ce6ec338923ac0b9f21b94efceb0bda3bd305d
1 /***************************************************************************
2 * Copyright (C) 2008-2017 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 #ifndef NCMPCPP_MPDPP_H
22 #define NCMPCPP_MPDPP_H
24 #include <cassert>
25 #include <exception>
26 #include <random>
27 #include <set>
28 #include <vector>
30 #include <mpd/client.h>
31 #include "song.h"
33 namespace MPD {
35 void checkConnectionErrors(mpd_connection *conn);
37 enum PlayerState { psUnknown, psStop, psPlay, psPause };
38 enum ReplayGainMode { rgmOff, rgmTrack, rgmAlbum };
40 struct ClientError: public std::exception
42 ClientError(mpd_error code_, std::string msg, bool clearable_)
43 : m_code(code_), m_msg(msg), m_clearable(clearable_) { }
44 virtual ~ClientError() noexcept { }
46 virtual const char *what() const noexcept { return m_msg.c_str(); }
47 mpd_error code() const { return m_code; }
48 bool clearable() const { return m_clearable; }
50 private:
51 mpd_error m_code;
52 std::string m_msg;
53 bool m_clearable;
56 struct ServerError: public std::exception
58 ServerError(mpd_server_error code_, std::string msg, bool clearable_)
59 : m_code(code_), m_msg(msg), m_clearable(clearable_) { }
60 virtual ~ServerError() noexcept { }
62 virtual const char *what() const noexcept { return m_msg.c_str(); }
63 mpd_server_error code() const { return m_code; }
64 bool clearable() const { return m_clearable; }
66 private:
67 mpd_server_error m_code;
68 std::string m_msg;
69 bool m_clearable;
72 struct Statistics
74 friend struct Connection;
76 bool empty() const { return m_stats.get() == nullptr; }
78 unsigned artists() const { return mpd_stats_get_number_of_artists(m_stats.get()); }
79 unsigned albums() const { return mpd_stats_get_number_of_albums(m_stats.get()); }
80 unsigned songs() const { return mpd_stats_get_number_of_songs(m_stats.get()); }
81 unsigned long playTime() const { return mpd_stats_get_play_time(m_stats.get()); }
82 unsigned long uptime() const { return mpd_stats_get_uptime(m_stats.get()); }
83 unsigned long dbUpdateTime() const { return mpd_stats_get_db_update_time(m_stats.get()); }
84 unsigned long dbPlayTime() const { return mpd_stats_get_db_play_time(m_stats.get()); }
86 private:
87 Statistics(mpd_stats *stats) : m_stats(stats, mpd_stats_free) { }
89 std::shared_ptr<mpd_stats> m_stats;
92 struct Status
94 friend struct Connection;
96 Status() { }
98 void clear() { m_status.reset(); }
99 bool empty() const { return m_status.get() == nullptr; }
101 int volume() const { return mpd_status_get_volume(m_status.get()); }
102 bool repeat() const { return mpd_status_get_repeat(m_status.get()); }
103 bool random() const { return mpd_status_get_random(m_status.get()); }
104 bool single() const { return mpd_status_get_single(m_status.get()); }
105 bool consume() const { return mpd_status_get_consume(m_status.get()); }
106 unsigned playlistLength() const { return mpd_status_get_queue_length(m_status.get()); }
107 unsigned playlistVersion() const { return mpd_status_get_queue_version(m_status.get()); }
108 PlayerState playerState() const { return PlayerState(mpd_status_get_state(m_status.get())); }
109 unsigned crossfade() const { return mpd_status_get_crossfade(m_status.get()); }
110 int currentSongPosition() const { return mpd_status_get_song_pos(m_status.get()); }
111 int currentSongID() const { return mpd_status_get_song_id(m_status.get()); }
112 int nextSongPosition() const { return mpd_status_get_next_song_pos(m_status.get()); }
113 int nextSongID() const { return mpd_status_get_next_song_id(m_status.get()); }
114 unsigned elapsedTime() const { return mpd_status_get_elapsed_time(m_status.get()); }
115 unsigned totalTime() const { return mpd_status_get_total_time(m_status.get()); }
116 unsigned kbps() const { return mpd_status_get_kbit_rate(m_status.get()); }
117 unsigned updateID() const { return mpd_status_get_update_id(m_status.get()); }
118 const char *error() const { return mpd_status_get_error(m_status.get()); }
120 private:
121 Status(mpd_status *status) : m_status(status, mpd_status_free) { }
123 std::shared_ptr<mpd_status> m_status;
126 struct Directory
128 Directory()
129 : m_last_modified(0)
131 Directory(const mpd_directory *directory)
133 assert(directory != nullptr);
134 m_path = mpd_directory_get_path(directory);
135 m_last_modified = mpd_directory_get_last_modified(directory);
137 Directory(std::string path_, time_t last_modified = 0)
138 : m_path(std::move(path_))
139 , m_last_modified(last_modified)
142 bool operator==(const Directory &rhs) const
144 return m_path == rhs.m_path
145 && m_last_modified == rhs.m_last_modified;
147 bool operator!=(const Directory &rhs) const
149 return !(*this == rhs);
152 const std::string &path() const
154 return m_path;
156 time_t lastModified() const
158 return m_last_modified;
161 private:
162 std::string m_path;
163 time_t m_last_modified;
166 struct Playlist
168 Playlist()
169 : m_last_modified(0)
171 Playlist(const mpd_playlist *playlist)
173 assert(playlist != nullptr);
174 m_path = mpd_playlist_get_path(playlist);
175 m_last_modified = mpd_playlist_get_last_modified(playlist);
177 Playlist(std::string path_, time_t last_modified = 0)
178 : m_path(std::move(path_))
179 , m_last_modified(last_modified)
181 if (m_path.empty())
182 throw std::runtime_error("empty path");
185 bool operator==(const Playlist &rhs) const
187 return m_path == rhs.m_path
188 && m_last_modified == rhs.m_last_modified;
190 bool operator!=(const Playlist &rhs) const
192 return !(*this == rhs);
195 const std::string &path() const
197 return m_path;
199 time_t lastModified() const
201 return m_last_modified;
204 private:
205 std::string m_path;
206 time_t m_last_modified;
209 struct Item
211 enum class Type { Directory, Song, Playlist };
213 Item(mpd_entity *entity)
215 assert(entity != nullptr);
216 switch (mpd_entity_get_type(entity))
218 case MPD_ENTITY_TYPE_DIRECTORY:
219 m_type = Type::Directory;
220 m_directory = Directory(mpd_entity_get_directory(entity));
221 break;
222 case MPD_ENTITY_TYPE_SONG:
223 m_type = Type::Song;
224 m_song = Song(mpd_song_dup(mpd_entity_get_song(entity)));
225 break;
226 case MPD_ENTITY_TYPE_PLAYLIST:
227 m_type = Type::Playlist;
228 m_playlist = Playlist(mpd_entity_get_playlist(entity));
229 break;
230 default:
231 throw std::runtime_error("unknown mpd_entity type");
233 mpd_entity_free(entity);
235 Item(Directory directory_)
236 : m_type(Type::Directory)
237 , m_directory(std::move(directory_))
239 Item(Song song_)
240 : m_type(Type::Song)
241 , m_song(std::move(song_))
243 Item(Playlist playlist_)
244 : m_type(Type::Playlist)
245 , m_playlist(std::move(playlist_))
248 bool operator==(const Item &rhs) const
250 return m_directory == rhs.m_directory
251 && m_song == rhs.m_song
252 && m_playlist == rhs.m_playlist;
254 bool operator!=(const Item &rhs) const
256 return !(*this == rhs);
259 Type type() const
261 return m_type;
264 Directory &directory()
266 return const_cast<Directory &>(
267 static_cast<const Item &>(*this).directory());
269 Song &song()
271 return const_cast<Song &>(
272 static_cast<const Item &>(*this).song());
274 Playlist &playlist()
276 return const_cast<Playlist &>(
277 static_cast<const Item &>(*this).playlist());
280 const Directory &directory() const
282 assert(m_type == Type::Directory);
283 return m_directory;
285 const Song &song() const
287 assert(m_type == Type::Song);
288 return m_song;
290 const Playlist &playlist() const
292 assert(m_type == Type::Playlist);
293 return m_playlist;
296 private:
297 Type m_type;
298 Directory m_directory;
299 Song m_song;
300 Playlist m_playlist;
303 struct Output
305 Output() { }
306 Output(mpd_output *output)
307 : m_output(output, mpd_output_free)
310 bool operator==(const Output &rhs) const
312 if (empty() && rhs.empty())
313 return true;
314 else if (!empty() && !rhs.empty())
315 return id() == rhs.id()
316 && strcmp(name(), rhs.name()) == 0
317 && enabled() == rhs.enabled();
318 else
319 return false;
321 bool operator!=(const Output &rhs) const
323 return !(*this == rhs);
326 unsigned id() const
328 assert(m_output.get() != nullptr);
329 return mpd_output_get_id(m_output.get());
331 const char *name() const
333 assert(m_output.get() != nullptr);
334 return mpd_output_get_name(m_output.get());
336 bool enabled() const
338 assert(m_output.get() != nullptr);
339 return mpd_output_get_enabled(m_output.get());
342 bool empty() const { return m_output.get() == nullptr; }
344 private:
345 std::shared_ptr<mpd_output> m_output;
348 template <typename ObjectT>
349 struct Iterator: std::iterator<std::input_iterator_tag, ObjectT>
351 // shared state of the iterator
352 struct State
354 friend Iterator;
356 typedef std::function<bool(State &)> Fetcher;
358 State(mpd_connection *connection_, Fetcher fetcher)
359 : m_connection(connection_)
360 , m_fetcher(fetcher)
362 assert(m_connection != nullptr);
363 assert(m_fetcher != nullptr);
365 ~State()
367 mpd_response_finish(m_connection);
370 mpd_connection *connection() const
372 return m_connection;
375 void setObject(ObjectT object)
377 if (hasObject())
378 *m_object = std::move(object);
379 else
380 m_object.reset(new ObjectT(std::move(object)));
383 private:
384 bool operator==(const State &rhs) const
386 return m_connection == rhs.m_connection
387 && m_object == m_object;
389 bool operator!=(const State &rhs) const
391 return !(*this == rhs);
394 bool fetch()
396 return m_fetcher(*this);
398 ObjectT &getObject() const
400 return *m_object;
402 bool hasObject() const
404 return m_object.get() != nullptr;
407 mpd_connection *m_connection;
408 Fetcher m_fetcher;
409 std::unique_ptr<ObjectT> m_object;
412 Iterator()
413 : m_state(nullptr)
415 Iterator(mpd_connection *connection, typename State::Fetcher fetcher)
416 : m_state(std::make_shared<State>(connection, std::move(fetcher)))
418 // get the first element
419 ++*this;
421 ~Iterator()
423 if (m_state)
424 checkConnectionErrors(m_state->connection());
427 void finish()
429 assert(m_state);
430 // check errors and change the iterator into end iterator
431 checkConnectionErrors(m_state->connection());
432 m_state = nullptr;
435 ObjectT &operator*() const
437 if (!m_state)
438 throw std::runtime_error("no object associated with the iterator");
439 assert(m_state->hasObject());
440 return m_state->getObject();
442 ObjectT *operator->() const
444 return &**this;
447 Iterator &operator++()
449 assert(m_state);
450 if (!m_state->fetch())
451 finish();
452 return *this;
454 Iterator operator++(int)
456 Iterator it(*this);
457 ++*this;
458 return it;
461 bool operator==(const Iterator &rhs) const
463 return m_state == rhs.m_state;
465 bool operator!=(const Iterator &rhs) const
467 return !(*this == rhs);
470 private:
471 std::shared_ptr<State> m_state;
474 typedef Iterator<Directory> DirectoryIterator;
475 typedef Iterator<Item> ItemIterator;
476 typedef Iterator<Output> OutputIterator;
477 typedef Iterator<Playlist> PlaylistIterator;
478 typedef Iterator<Song> SongIterator;
479 typedef Iterator<std::string> StringIterator;
481 struct Connection
483 typedef std::function<void(int)> NoidleCallback;
485 Connection();
487 void Connect();
488 bool Connected() const;
489 void Disconnect();
491 const std::string &GetHostname() { return m_host; }
492 int GetPort() { return m_port; }
494 unsigned Version() const;
496 int GetFD() const { return m_fd; }
498 void SetHostname(const std::string &);
499 void SetPort(int port) { m_port = port; }
500 void SetTimeout(int timeout) { m_timeout = timeout; }
501 void SetPassword(const std::string &password) { m_password = password; }
502 void SendPassword();
504 Statistics getStatistics();
505 Status getStatus();
507 void UpdateDirectory(const std::string &);
509 void Play();
510 void Play(int);
511 void PlayID(int);
512 void Pause(bool);
513 void Toggle();
514 void Stop();
515 void Next();
516 void Prev();
517 void Move(unsigned int from, unsigned int to);
518 void Swap(unsigned, unsigned);
519 void Seek(unsigned int pos, unsigned int where);
520 void Shuffle();
521 void ShuffleRange(unsigned start, unsigned end);
522 void ClearMainPlaylist();
524 SongIterator GetPlaylistChanges(unsigned);
526 Song GetCurrentSong();
527 Song GetSong(const std::string &);
528 SongIterator GetPlaylistContent(const std::string &name);
529 SongIterator GetPlaylistContentNoInfo(const std::string &name);
531 StringIterator GetSupportedExtensions();
533 void SetRepeat(bool);
534 void SetRandom(bool);
535 void SetSingle(bool);
536 void SetConsume(bool);
537 void SetCrossfade(unsigned);
538 void SetVolume(unsigned int vol);
539 void ChangeVolume(int change);
541 std::string GetReplayGainMode();
542 void SetReplayGainMode(ReplayGainMode);
544 void SetPriority(const MPD::Song &s, int prio);
546 int AddSong(const std::string &, int = -1); // returns id of added song
547 int AddSong(const Song &, int = -1); // returns id of added song
548 bool AddRandomTag(mpd_tag_type, size_t, std::mt19937 &rng);
549 bool AddRandomSongs(size_t number, std::mt19937 &rng);
550 void Add(const std::string &path);
551 void Delete(unsigned int pos);
552 void PlaylistDelete(const std::string &playlist, unsigned int pos);
553 void StartCommandsList();
554 void CommitCommandsList();
556 void DeletePlaylist(const std::string &name);
557 void LoadPlaylist(const std::string &name);
558 void SavePlaylist(const std::string &);
559 void ClearPlaylist(const std::string &playlist);
560 void AddToPlaylist(const std::string &, const Song &);
561 void AddToPlaylist(const std::string &, const std::string &);
562 void PlaylistMove(const std::string &path, int from, int to);
563 void Rename(const std::string &from, const std::string &to);
565 void StartSearch(bool);
566 void StartFieldSearch(mpd_tag_type);
567 void AddSearch(mpd_tag_type item, const std::string &str) const;
568 void AddSearchAny(const std::string &str) const;
569 void AddSearchURI(const std::string &str) const;
570 SongIterator CommitSearchSongs();
572 PlaylistIterator GetPlaylists();
573 StringIterator GetList(mpd_tag_type type);
574 ItemIterator GetDirectory(const std::string &directory);
575 SongIterator GetDirectoryRecursive(const std::string &directory);
576 SongIterator GetSongs(const std::string &directory);
577 DirectoryIterator GetDirectories(const std::string &directory);
579 OutputIterator GetOutputs();
580 void EnableOutput(int id);
581 void DisableOutput(int id);
583 StringIterator GetURLHandlers();
584 StringIterator GetTagTypes();
586 void idle();
587 int noidle();
588 void setNoidleCallback(NoidleCallback callback);
590 private:
591 struct ConnectionDeleter {
592 void operator()(mpd_connection *connection) {
593 mpd_connection_free(connection);
597 void checkConnection() const;
598 void prechecks();
599 void prechecksNoCommandsList();
600 void checkErrors() const;
602 NoidleCallback m_noidle_callback;
603 std::unique_ptr<mpd_connection, ConnectionDeleter> m_connection;
604 bool m_command_list_active;
606 int m_fd;
607 bool m_idle;
609 std::string m_host;
610 int m_port;
611 int m_timeout;
612 std::string m_password;
617 extern MPD::Connection Mpd;
619 #endif // NCMPCPP_MPDPP_H