1 /*
2 Vimpc
3 Copyright (C) 2010 - 2012 Nathan Sweetman
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 3 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, see <http://www.gnu.org/licenses/>.
17
18 mpdclient.cpp - provides interaction with the music player daemon
19 */
20
21 #include "mpdclient.hpp"
22
23 #include "assert.hpp"
24 #include "events.hpp"
25 #include "screen.hpp"
26 #include "settings.hpp"
27 #include "vimpc.hpp"
28
29 #include "buffer/playlist.hpp"
30 #include "buffer/list.hpp"
31 #include "mode/mode.hpp"
32 #include "window/error.hpp"
33
34 #include <mpd/client.h>
35 #include <sys/time.h>
36 #include <poll.h>
37 #include <unistd.h>
38
39 #include <list>
40 #include <signal.h>
41 #include <sys/types.h>
42
43 using namespace Mpc;
44
45 #define MPDCOMMAND
46 //#define _DEBUG_ASSERT_ON_ERROR
47 //#define _DEBUG_BREAK_ON_ERROR
48
49 static std::list<FUNCTION<void()> > Queue;
50 static Mutex QueueMutex;
51 static Atomic(bool) Running(true);
52 static ConditionVariable Condition;
53
54
55 // Helper functions
SecondsToMinutes(uint32_t duration)56 uint32_t Mpc::SecondsToMinutes(uint32_t duration)
57 {
58 return static_cast<uint32_t>(duration / 60);
59 }
60
RemainingSeconds(uint32_t duration)61 uint32_t Mpc::RemainingSeconds(uint32_t duration)
62 {
63 return (duration - (SecondsToMinutes(duration) * 60));
64 }
65
66
CommandList(Mpc::Client & client,bool condition)67 CommandList::CommandList(Mpc::Client & client, bool condition) :
68 condition_(condition),
69 client_ (client)
70 {
71 Debug("Commandlist object construct");
72
73 if (condition_)
74 {
75 client_.StartCommandList();
76 }
77 }
78
~CommandList()79 CommandList::~CommandList()
80 {
81 Debug("Commandlist object destruct");
82
83 if (condition_)
84 {
85 client_.SendCommandList();
86 }
87 }
88
89
90 // Mpc::Client Implementation
Client(Main::Vimpc * vimpc,Main::Settings & settings,Mpc::Lists & lists,Ui::Screen & screen)91 Client::Client(Main::Vimpc * vimpc, Main::Settings & settings, Mpc::Lists & lists, Ui::Screen & screen) :
92 vimpc_ (vimpc),
93 settings_ (settings),
94 connection_ (NULL),
95 fd_ (-1),
96
97 hostname_ (""),
98 port_ (0),
99 versionMajor_ (-1),
100 versionMinor_ (-1),
101 versionPatch_ (-1),
102 timeSinceUpdate_ (0),
103 retried_ (true),
104 ready_ (false),
105
106 volume_ (100),
107 mVolume_ (100),
108 mute_ (false),
109 updating_ (false),
110 random_ (false),
111 repeat_ (false),
112 single_ (false),
113 consume_ (false),
114 crossfade_ (false),
115 crossfadeTime_ (0),
116 elapsed_ (0),
117 state_ (MPD_STATE_UNKNOWN),
118 mpdstate_ (MPD_STATE_UNKNOWN),
119
120 currentSong_ (NULL),
121 currentStatus_ (NULL),
122 currentSongId_ (-1),
123 totalNumberOfSongs_ (0),
124 currentSongURI_ (""),
125 currentState_ ("Disconnected"),
126
127 lists_ (&lists),
128 loadedList_ (""),
129
130 screen_ (screen),
131 queueVersion_ (-1),
132 oldVersion_ (-1),
133 forceUpdate_ (true),
134 listMode_ (false),
135 idleMode_ (false),
136 queueUpdate_ (false),
137 autoscroll_ (false),
138 clientThread_ (Thread(&Client::ClientQueueExecutor, this, this))
139 {
140 screen_.RegisterProgressCallback([this] (double Value) { SeekToPercent(Value); });
141
142 Main::Vimpc::EventHandler(Event::PlaylistContentsForRemove, [this] (EventData const & Data)
143 {
144 Mpc::CommandList list(*this);
145
146 for (auto uri : Data.uris)
147 {
148 Mpc::Song * song = Main::Library().Song(uri);
149
150 int const PlaylistIndex = Main::Playlist().Index(song);
151
152 if (PlaylistIndex >= 0)
153 {
154 Delete(PlaylistIndex);
155 }
156 }
157 });
158 }
159
~Client()160 Client::~Client()
161 {
162 Running = false;
163
164 clientThread_.join();
165
166 if (currentStatus_ != NULL)
167 {
168 mpd_status_free(currentStatus_);
169 currentStatus_ = NULL;
170 }
171
172 if (currentSong_ != NULL)
173 {
174 mpd_song_free(currentSong_);
175 currentSong_ = NULL;
176 }
177
178 DeleteConnection();
179 }
180
QueueCommand(FUNCTION<void ()> const & function)181 void Client::QueueCommand(FUNCTION<void()> const & function)
182 {
183 UniqueLock<Mutex> Lock(QueueMutex);
184 Queue.push_back(function);
185 Condition.notify_all();
186 }
187
WaitForCompletion()188 void Client::WaitForCompletion()
189 {
190 QueueMutex.lock();
191 int Count = Queue.size();
192 QueueMutex.unlock();
193
194 while (Count != 0)
195 {
196 usleep(20 * 1000);
197 UniqueLock<Mutex> Lock(QueueMutex);
198 Count = Queue.size();
199 }
200 }
201
202
Connect(std::string const & hostname,uint16_t port,uint32_t timeout_ms)203 void Client::Connect(std::string const & hostname, uint16_t port, uint32_t timeout_ms)
204 {
205 QueueCommand([this, hostname, port, timeout_ms] () { ConnectImpl(hostname, port, timeout_ms); });
206 //ConnectImpl(hostname, port, timeout_ms);
207 }
208
ConnectImpl(std::string const & hostname,uint16_t port,uint32_t timeout_ms)209 void Client::ConnectImpl(std::string const & hostname, uint16_t port, uint32_t timeout_ms)
210 {
211 std::string connect_hostname = hostname;
212 uint16_t connect_port = port;
213 uint32_t connect_timeout = timeout_ms;
214 std::string connect_password = "";
215
216 DeleteConnection();
217
218 if (connect_hostname.empty() == true)
219 {
220 char * const host_env = getenv("MPD_HOST");
221
222 if (host_env != NULL)
223 {
224 connect_hostname = host_env;
225
226 size_t const pos = connect_hostname.find_last_of("@");
227
228 if (pos != connect_hostname.npos)
229 {
230 connect_password = connect_hostname.substr(0, pos);
231 connect_hostname = connect_hostname.substr(pos + 1);
232 }
233 }
234 else
235 {
236 connect_hostname = "localhost";
237 }
238 }
239
240 if (port == 0)
241 {
242 char * const port_env = getenv("MPD_PORT");
243
244 if (port_env != NULL)
245 {
246 connect_port = atoi(port_env);
247 }
248 else
249 {
250 connect_port = 0;
251 }
252 }
253
254 if (timeout_ms == 0)
255 {
256 char * const timeout_env = getenv("MPD_TIMEOUT");
257
258 if (settings_.Get(Setting::Timeout) != "0")
259 {
260 Debug("Client::Connect timeout " + settings_.Get(Setting::Timeout));
261 connect_timeout = atoi(settings_.Get(Setting::Timeout).c_str());
262 }
263 else if (timeout_env != NULL)
264 {
265 connect_timeout = atoi(timeout_env);
266 }
267
268 connect_timeout *= 1000;
269 }
270
271 // Connecting may take a long time as this is a single threaded application
272 // and the mpd connect is a blocking call, so be sure to update the screen
273 // first to let the user know that something is happening
274 currentState_ = "Connecting";
275 StateEvent();
276
277 hostname_ = connect_hostname;
278 port_ = connect_port;
279 connection_ = NULL;
280
281 EventData HostData;
282 HostData.hostname = hostname_;
283 HostData.port = port_;
284 Main::Vimpc::CreateEvent(Event::ChangeHost, HostData);
285
286 //! \TODO make the connection async
287 Debug("Client::Connecting to %s:%u - timeout %u", connect_hostname.c_str(), connect_port, connect_timeout);
288 connection_ = mpd_connection_new(connect_hostname.c_str(), connect_port, connect_timeout);
289
290 CheckError();
291
292 if (Connected() == true)
293 {
294 fd_ = mpd_connection_get_fd(connection_);
295
296 Debug("Client::Connected.");
297
298 EventData Data;
299 Main::Vimpc::CreateEvent(Event::Connected, Data);
300 Main::Vimpc::CreateEvent(Event::Repaint, Data);
301
302 GetVersion();
303
304 if (connect_password != "")
305 {
306 Password(connect_password);
307 }
308
309 elapsed_ = 0;
310
311 if (IsPasswordRequired() == false)
312 {
313 Initialise();
314 }
315 else
316 {
317 StateEvent();
318 EventData Data;
319 Main::Vimpc::CreateEvent(Event::RequirePassword, Data);
320 }
321 }
322 else
323 {
324 Error(ErrorNumber::ClientNoConnection, "Failed to connect to server, please ensure it is running and type :connect <server> [port]");
325 }
326 }
327
Initialise()328 void Client::Initialise()
329 {
330 UpdateStatus();
331 StateEvent();
332
333 GetAllMetaInformation();
334 UpdateCurrentSong();
335
336 if (Connected() == true)
337 {
338 ready_ = true;
339 retried_ = false;
340 }
341 }
342
343
Disconnect()344 void Client::Disconnect()
345 {
346 QueueCommand([this] ()
347 {
348 if (Connected() == true)
349 {
350 Debug("Client::Disconnect");
351 DeleteConnection();
352 }
353 });
354 }
355
Reconnect()356 void Client::Reconnect()
357 {
358 QueueCommand([this] ()
359 {
360 Debug("Client::Reconnect");
361 Disconnect();
362 Connect(hostname_, port_);
363 });
364 }
365
Password(std::string const & password)366 void Client::Password(std::string const & password)
367 {
368 QueueCommand([this, password] ()
369 {
370 ClearCommand();
371
372 if (Connected() == true)
373 {
374 Debug("Client::Sending password");
375 mpd_send_password(connection_, password.c_str());
376 }
377 else
378 {
379 ErrorString(ErrorNumber::ClientNoConnection);
380 }
381 });
382 }
383
Connected() const384 bool Client::Connected() const
385 {
386 return (connection_ != NULL);
387 }
388
389
Play(uint32_t const playId)390 void Client::Play(uint32_t const playId)
391 {
392 QueueCommand([this, playId] ()
393 {
394 ClearCommand();
395
396 if (Connected() == true)
397 {
398 Debug("Client::Play position %u", playId);
399
400 if (mpd_run_play_pos(connection_, playId) == true)
401 {
402 currentSongId_ = playId;
403 EventData IdData; IdData.id = currentSongId_;
404 Main::Vimpc::CreateEvent(Event::CurrentSongId, IdData);
405
406 autoscroll_ = false;
407 Main::Vimpc::CreateEvent(Event::Autoscroll, IdData);
408
409 state_ = MPD_STATE_PLAY;
410 elapsed_ = 0;
411 timeSinceUpdate_ = 0;
412 StateEvent();
413 }
414 }
415 else
416 {
417 ErrorString(ErrorNumber::ClientNoConnection);
418 }
419 });
420 }
421
422
AddComplete()423 void Client::AddComplete()
424 {
425 QueueCommand([this] ()
426 {
427 if ((state_ == MPD_STATE_STOP) && (settings_.Get(Setting::PlayOnAdd) == true))
428 {
429 Debug("Client::Playing start of playlist");
430 Play(0);
431 }
432 });
433 }
434
Pause()435 void Client::Pause()
436 {
437 QueueCommand([this] ()
438 {
439 ClearCommand();
440
441 if (Connected() == true)
442 {
443 Debug("Client::Toggling pause state");
444
445 if (mpd_run_toggle_pause(connection_) == true)
446 {
447 if (state_ == MPD_STATE_PLAY)
448 {
449 state_ = MPD_STATE_PAUSE;
450 }
451 else if (state_ == MPD_STATE_PAUSE)
452 {
453 state_ = MPD_STATE_PLAY;
454 }
455
456 timeSinceUpdate_ = 0;
457 elapsed_ = mpdelapsed_;
458 StateEvent();
459 }
460 }
461 else
462 {
463 ErrorString(ErrorNumber::ClientNoConnection);
464 }
465 });
466 }
467
Stop()468 void Client::Stop()
469 {
470 QueueCommand([this] ()
471 {
472 ClearCommand();
473
474 if (Connected() == true)
475 {
476 Debug("Client::Stopping playback");
477
478 if (mpd_run_stop(connection_) == true)
479 {
480 state_ = MPD_STATE_STOP;
481 StateEvent();
482 }
483 }
484 else
485 {
486 ErrorString(ErrorNumber::ClientNoConnection);
487 }
488 });
489 }
490
Next()491 void Client::Next()
492 {
493 QueueCommand([this] ()
494 {
495 ClearCommand();
496
497 if (Connected() == true)
498 {
499 Debug("Client::Next song");
500 mpd_send_next(connection_);
501
502 autoscroll_ = true;
503 }
504 else
505 {
506 ErrorString(ErrorNumber::ClientNoConnection);
507 }
508 });
509 }
510
Previous()511 void Client::Previous()
512 {
513 QueueCommand([this] ()
514 {
515 ClearCommand();
516
517 if (Connected() == true)
518 {
519 Debug("Client::Previous song");
520 mpd_send_previous(connection_);
521
522 autoscroll_ = true;
523 }
524 else
525 {
526 ErrorString(ErrorNumber::ClientNoConnection);
527 }
528 });
529 }
530
Seek(int32_t Offset)531 void Client::Seek(int32_t Offset)
532 {
533 QueueCommand([this, Offset] ()
534 {
535 ClearCommand();
536
537 if (Connected() == true)
538 {
539 if (currentSongId_ >= 0)
540 {
541 Debug("Client::Seek to time %d", elapsed_ + Offset);
542 mpd_send_seek_pos(connection_, currentSongId_, elapsed_ + Offset);
543 }
544 }
545 else
546 {
547 ErrorString(ErrorNumber::ClientNoConnection);
548 }
549 });
550 }
551
SeekTo(uint32_t Time)552 void Client::SeekTo(uint32_t Time)
553 {
554 QueueCommand([this, Time] ()
555 {
556 ClearCommand();
557
558 if (Connected() == true)
559 {
560 if (currentSongId_ >= 0)
561 {
562 Debug("Client::Seek to time %u", Time);
563 mpd_send_seek_pos(connection_, currentSongId_, Time);
564 }
565 }
566 else
567 {
568 ErrorString(ErrorNumber::ClientNoConnection);
569 }
570 });
571 }
572
SeekToPercent(double Percent)573 void Client::SeekToPercent(double Percent)
574 {
575 QueueCommand([this, Percent] ()
576 {
577 if (currentSong_)
578 {
579 Debug("Client::Seek to percent %d%%", static_cast<int32_t>(Percent * 100));
580
581 uint32_t const duration = mpd_song_get_duration(currentSong_);
582 SeekTo(static_cast<uint32_t>(Percent * duration));
583 }
584 });
585 }
586
587
SetRandom(bool const random)588 void Client::SetRandom(bool const random)
589 {
590 QueueCommand([this, random] ()
591 {
592 ClearCommand();
593
594 if (Connected() == true)
595 {
596 Debug("Client::Set random state %d", static_cast<int32_t>(random));
597
598 if (mpd_run_random(connection_, random) == true)
599 {
600 SetStateAndEvent(Event::Random, random_, random);
601 }
602 }
603 else
604 {
605 ErrorString(ErrorNumber::ClientNoConnection);
606 }
607 });
608 }
609
SetSingle(bool const single)610 void Client::SetSingle(bool const single)
611 {
612 QueueCommand([this, single] ()
613 {
614 ClearCommand();
615
616 if (Connected() == true)
617 {
618 Debug("Client::Set single state %d", static_cast<int32_t>(single));
619
620 if (mpd_run_single(connection_, single) == true)
621 {
622 SetStateAndEvent(Event::Single, single_, single);
623 }
624 }
625 else
626 {
627 ErrorString(ErrorNumber::ClientNoConnection);
628 }
629 });
630 }
631
SetConsume(bool const consume)632 void Client::SetConsume(bool const consume)
633 {
634 QueueCommand([this, consume] ()
635 {
636 ClearCommand();
637
638 if (Connected() == true)
639 {
640 Debug("Client::Set consume state %d", static_cast<int32_t>(consume));
641
642 if (mpd_run_consume(connection_, consume) == true)
643 {
644 SetStateAndEvent(Event::Consume, consume_, consume);
645 }
646 }
647 else
648 {
649 ErrorString(ErrorNumber::ClientNoConnection);
650 }
651 });
652 }
653
SetRepeat(bool const repeat)654 void Client::SetRepeat(bool const repeat)
655 {
656 QueueCommand([this, repeat] ()
657 {
658 ClearCommand();
659
660 if (Connected() == true)
661 {
662 Debug("Client::Set repeat state %d", static_cast<int32_t>(repeat));
663
664 if (mpd_run_repeat(connection_, repeat) == true)
665 {
666 SetStateAndEvent(Event::Repeat, repeat_, repeat);
667 }
668 }
669 else
670 {
671 ErrorString(ErrorNumber::ClientNoConnection);
672 }
673 });
674 }
675
SetCrossfade(bool crossfade)676 void Client::SetCrossfade(bool crossfade)
677 {
678 QueueCommand([this, crossfade] ()
679 {
680 if (crossfade == true)
681 {
682 SetCrossfade(crossfadeTime_);
683 }
684 else
685 {
686 SetCrossfade(static_cast<uint32_t>(0));
687 }
688 });
689 }
690
SetCrossfade(uint32_t crossfade)691 void Client::SetCrossfade(uint32_t crossfade)
692 {
693 QueueCommand([this, crossfade] ()
694 {
695 ClearCommand();
696
697 if (Connected() == true)
698 {
699 Debug("Client::Set crossfade time %u", crossfade);
700
701 if (mpd_run_crossfade(connection_, crossfade) == true)
702 {
703 crossfade_ = (crossfade != 0);
704
705 if (crossfade_ == true)
706 {
707 crossfadeTime_ = crossfade;
708 EventData Data; Data.value = crossfade;
709 Main::Vimpc::CreateEvent(Event::CrossfadeTime, Data);
710 }
711
712 EventData Data; Data.state = crossfade_;
713 Main::Vimpc::CreateEvent(Event::Crossfade, Data);
714 }
715 }
716 else
717 {
718 ErrorString(ErrorNumber::ClientNoConnection);
719 }
720 });
721 }
722
SetVolume(uint32_t volume)723 void Client::SetVolume(uint32_t volume)
724 {
725 QueueCommand([this, volume] ()
726 {
727 ClearCommand();
728
729 if (Connected() == true)
730 {
731 Debug("Client::Set volume %u", volume);
732
733 if (mpd_run_set_volume(connection_, volume) == true)
734 {
735 volume_ = volume;
736
737 EventData Data; Data.value = volume;
738 Main::Vimpc::CreateEvent(Event::Volume, Data);
739 }
740 }
741 else
742 {
743 ErrorString(ErrorNumber::ClientNoConnection);
744 }
745 });
746 }
747
SetMute(bool mute)748 void Client::SetMute(bool mute)
749 {
750 QueueCommand([this, mute] ()
751 {
752 if ((mute == true) && (mute_ == false))
753 {
754 mVolume_ = volume_;
755 SetVolume(0);
756 }
757 else if ((mute == false) && (mute_ == true))
758 {
759 SetVolume(mVolume_);
760 }
761
762 mute_ = mute;
763 EventData Data; Data.state = mute_;
764 Main::Vimpc::CreateEvent(Event::Mute, Data);
765 });
766 }
767
DeltaVolume(int32_t Delta)768 void Client::DeltaVolume(int32_t Delta)
769 {
770 QueueCommand([this, Delta] ()
771 {
772 ClearCommand();
773
774 if (Connected() == true)
775 {
776 int CurrentVolume = volume_ + Delta;
777
778 if (CurrentVolume < 0)
779 {
780 CurrentVolume = 0;
781 }
782 else if (CurrentVolume > 100)
783 {
784 CurrentVolume = 100;
785 }
786
787 Debug("Client::Delta volume %d %d", Delta, CurrentVolume);
788
789 if (mpd_run_set_volume(connection_, CurrentVolume) == true)
790 {
791 volume_ = CurrentVolume;
792 EventData Data; Data.value = CurrentVolume;
793 Main::Vimpc::CreateEvent(Event::Volume, Data);
794 }
795 }
796 });
797 }
798
ToggleRandom()799 void Client::ToggleRandom()
800 {
801 QueueCommand([this] ()
802 {
803 ClearCommand();
804
805 if (Connected() == true)
806 {
807 Debug("Client::Toggle random state %d", static_cast<int32_t>(!random_));
808
809 if (mpd_run_random(connection_, !random_) == true)
810 {
811 SetStateAndEvent(Event::Random, random_, !random_);
812 }
813 }
814 else
815 {
816 ErrorString(ErrorNumber::ClientNoConnection);
817 }
818 });
819 }
820
ToggleSingle()821 void Client::ToggleSingle()
822 {
823 QueueCommand([this] ()
824 {
825 ClearCommand();
826
827 if (Connected() == true)
828 {
829 Debug("Client::Toggle single state %d", static_cast<int32_t>(!single_));
830
831 if (mpd_run_single(connection_, !single_) == true)
832 {
833 SetStateAndEvent(Event::Single, single_, !single_);
834 }
835 }
836 else
837 {
838 ErrorString(ErrorNumber::ClientNoConnection);
839 }
840 });
841 }
842
ToggleConsume()843 void Client::ToggleConsume()
844 {
845 QueueCommand([this] ()
846 {
847 ClearCommand();
848
849 if (Connected() == true)
850 {
851 Debug("Client::Toggle consume state %d", static_cast<int32_t>(!consume_));
852
853 if (mpd_run_consume(connection_, !consume_) == true)
854 {
855 SetStateAndEvent(Event::Consume, consume_, !consume_);
856 }
857 }
858 else
859 {
860 ErrorString(ErrorNumber::ClientNoConnection);
861 }
862 });
863 }
864
865
ToggleRepeat()866 void Client::ToggleRepeat()
867 {
868 QueueCommand([this] ()
869 {
870 ClearCommand();
871
872 if (Connected() == true)
873 {
874 Debug("Client::Toggle repeat state %d", static_cast<int32_t>(!repeat_));
875
876 if (mpd_run_repeat(connection_, !repeat_) == true)
877 {
878 SetStateAndEvent(Event::Repeat, repeat_, !repeat_);
879 }
880 }
881 else
882 {
883 ErrorString(ErrorNumber::ClientNoConnection);
884 }
885 });
886 }
887
ToggleCrossfade()888 void Client::ToggleCrossfade()
889 {
890 QueueCommand([this] ()
891 {
892 if (crossfade_ == false)
893 {
894 SetCrossfade(crossfadeTime_);
895 }
896 else
897 {
898 SetCrossfade(static_cast<uint32_t>(0));
899 }
900 });
901 }
902
Shuffle()903 void Client::Shuffle()
904 {
905 QueueCommand([this] ()
906 {
907 ClearCommand();
908
909 if (Connected() == true)
910 {
911 Debug("Client::Send shuffle");
912 mpd_send_shuffle(connection_);
913 }
914 else
915 {
916 ErrorString(ErrorNumber::ClientNoConnection);
917 }
918 });
919 }
920
Move(uint32_t position1,uint32_t position2)921 void Client::Move(uint32_t position1, uint32_t position2)
922 {
923 QueueCommand([this, position1, position2] ()
924 {
925 ClearCommand();
926
927 if (Connected() == true)
928 {
929 Debug("Client::Send move %u %u", position1, position2);
930 mpd_send_move(connection_, position1, position2);
931 }
932 else
933 {
934 ErrorString(ErrorNumber::ClientNoConnection);
935 }
936 });
937 }
938
Swap(uint32_t position1,uint32_t position2)939 void Client::Swap(uint32_t position1, uint32_t position2)
940 {
941 QueueCommand([this, position1, position2] ()
942 {
943 ClearCommand();
944
945 if (Connected() == true)
946 {
947 Debug("Client::Send swap %u %u", position1, position2);
948 mpd_send_swap(connection_, position1, position2);
949 }
950 else
951 {
952 ErrorString(ErrorNumber::ClientNoConnection);
953 }
954 });
955 }
956
957
CreatePlaylist(std::string const & name)958 void Client::CreatePlaylist(std::string const & name)
959 {
960 QueueCommand([this, name] ()
961 {
962 ClearCommand();
963
964 if (Connected() == true)
965 {
966 Debug("Client::Send save %s", name.c_str());
967
968 if (mpd_run_save(connection_, name.c_str()) == true)
969 {
970 EventData Data; Data.name = name;
971 Main::Vimpc::CreateEvent(Event::NewPlaylist, Data);
972 Main::Vimpc::CreateEvent(Event::Repaint, Data);
973 }
974
975 Debug("Client::Send clear playlist %s", name.c_str());
976 mpd_run_playlist_clear(connection_, name.c_str());
977 }
978 else
979 {
980 ErrorString(ErrorNumber::ClientNoConnection);
981 }
982 });
983 }
984
HasPlaylist(std::string const & name)985 bool Client::HasPlaylist(std::string const & name)
986 {
987 for (int i=0; i < lists_->Size(); i++) {
988 if (lists_->Get(i).name_.c_str() == name)
989 {
990 return true;
991 }
992 }
993 return false;
994 }
995
HasLoadedPlaylist()996 bool Client::HasLoadedPlaylist()
997 {
998 return loadedList_ != "";
999 }
1000
SaveLoadedPlaylist()1001 void Client::SaveLoadedPlaylist()
1002 {
1003 if (Client::HasLoadedPlaylist())
1004 {
1005 Client::SavePlaylist(loadedList_);
1006 }
1007 else
1008 {
1009 ErrorString(ErrorNumber::NoPlaylistLoaded);
1010 }
1011 }
1012
SavePlaylist(std::string const & name)1013 void Client::SavePlaylist(std::string const & name)
1014 {
1015 QueueCommand([this, name] ()
1016 {
1017 ClearCommand();
1018
1019 if (Connected() == true)
1020 {
1021
1022 if (HasPlaylist(name))
1023 {
1024 Debug("Client::Send remove %s", name.c_str());
1025 mpd_run_rm(connection_, name.c_str());
1026 }
1027
1028 Debug("Client::Send save %s", name.c_str());
1029 if (mpd_run_save(connection_, name.c_str()) == true)
1030 {
1031 EventData Data; Data.name = name;
1032 Main::Vimpc::CreateEvent(Event::NewPlaylist, Data);
1033 }
1034 }
1035 else
1036 {
1037 ErrorString(ErrorNumber::ClientNoConnection);
1038 }
1039 });
1040 }
1041
LoadPlaylist(std::string const & name)1042 void Client::LoadPlaylist(std::string const & name)
1043 {
1044 QueueCommand([this, name] ()
1045 {
1046 ClearCommand();
1047
1048 if (Connected() == true)
1049 {
1050 mpd_run_clear(connection_);
1051 Debug("Client::Send load %s", name.c_str());
1052 mpd_run_load(connection_, name.c_str());
1053 loadedList_ = name;
1054 }
1055 else
1056 {
1057 ErrorString(ErrorNumber::ClientNoConnection);
1058 }
1059 });
1060 }
1061
AppendPlaylist(std::string const & name)1062 void Client::AppendPlaylist(std::string const & name)
1063 {
1064 QueueCommand([this, name] ()
1065 {
1066 ClearCommand();
1067
1068 if (Connected() == true)
1069 {
1070 Debug("Client::Send load %s", name.c_str());
1071 mpd_run_load(connection_, name.c_str());
1072 loadedList_ = name;
1073 }
1074 else
1075 {
1076 ErrorString(ErrorNumber::ClientNoConnection);
1077 }
1078 });
1079 }
1080
RemovePlaylist(std::string const & name)1081 void Client::RemovePlaylist(std::string const & name)
1082 {
1083 QueueCommand([this, name] ()
1084 {
1085 ClearCommand();
1086
1087 if (Connected() == true)
1088 {
1089 Debug("Client::Send remove %s", name.c_str());
1090 mpd_run_rm(connection_, name.c_str());
1091 }
1092 else
1093 {
1094 ErrorString(ErrorNumber::ClientNoConnection);
1095 }
1096 });
1097 }
1098
AddToNamedPlaylist(std::string const & name,Mpc::Song * song)1099 void Client::AddToNamedPlaylist(std::string const & name, Mpc::Song * song)
1100 {
1101 std::string URI = song->URI();
1102
1103 QueueCommand([this, name, URI] ()
1104 {
1105 ClearCommand();
1106
1107 if (Connected() == true)
1108 {
1109 Debug("Client::Playlist add %s to %s", URI.c_str(), name.c_str());
1110 mpd_send_playlist_add(connection_, name.c_str(), URI.c_str());
1111 }
1112 else
1113 {
1114 ErrorString(ErrorNumber::ClientNoConnection);
1115 }
1116 });
1117 }
1118
1119
PlaylistContents(std::string const & name)1120 void Client::PlaylistContents(std::string const & name)
1121 {
1122 QueueCommand([this, name] ()
1123 {
1124 std::string const SongFormat = settings_.Get(Setting::SongFormat);
1125
1126 ClearCommand();
1127
1128 if (Connected() == true)
1129 {
1130 Debug("Client::Add songs from playlist %s", name.c_str());
1131
1132 mpd_send_list_playlist(connection_, name.c_str());
1133
1134 mpd_song * nextSong = mpd_recv_song(connection_);
1135
1136 std::vector<std::string> URIs;
1137
1138 if (nextSong == NULL)
1139 {
1140 ErrorString(ErrorNumber::PlaylistEmpty);
1141 }
1142 else
1143 {
1144 for (; nextSong != NULL; nextSong = mpd_recv_song(connection_))
1145 {
1146 if ((settings_.Get(Setting::ListAllMeta) == false))
1147 {
1148 Mpc::Song * song = Main::Library().Song(mpd_song_get_uri(nextSong));
1149
1150 if (song == NULL)
1151 {
1152 song = CreateSong(nextSong);
1153 // Pre cache the print of the song
1154 (void) song->FormatString(SongFormat);
1155 EventData Data; Data.song = song;
1156 Main::Vimpc::CreateEvent(Event::DatabaseSong, Data);
1157 }
1158 }
1159
1160 URIs.push_back(mpd_song_get_uri(nextSong));
1161 mpd_song_free(nextSong);
1162 }
1163
1164 EventData Data; Data.name = name; Data.uris = URIs;
1165 Main::Vimpc::CreateEvent(Event::PlaylistContents, Data);
1166 }
1167 }
1168 });
1169 }
1170
PlaylistContentsForRemove(std::string const & name)1171 void Client::PlaylistContentsForRemove(std::string const & name)
1172 {
1173 QueueCommand([this, name] ()
1174 {
1175 ClearCommand();
1176
1177 if (Connected() == true)
1178 {
1179 Debug("Client::Add songs from playlist %s", name.c_str());
1180
1181 mpd_send_list_playlist(connection_, name.c_str());
1182
1183 mpd_song * nextSong = mpd_recv_song(connection_);
1184
1185 std::vector<std::string> URIs;
1186
1187 if (nextSong != NULL)
1188 {
1189 for (; nextSong != NULL; nextSong = mpd_recv_song(connection_))
1190 {
1191 URIs.push_back(mpd_song_get_uri(nextSong));
1192 mpd_song_free(nextSong);
1193 }
1194
1195 EventData Data; Data.name = name; Data.uris = URIs;
1196 Main::Vimpc::CreateEvent(Event::PlaylistContentsForRemove, Data);
1197 }
1198 }
1199 });
1200 }
1201
SetOutput(Mpc::Output * output,bool enable)1202 void Client::SetOutput(Mpc::Output * output, bool enable)
1203 {
1204 if (enable == true)
1205 {
1206 EnableOutput(output);
1207 }
1208 else
1209 {
1210 DisableOutput(output);
1211 }
1212 }
1213
EnableOutput(Mpc::Output * output)1214 void Client::EnableOutput(Mpc::Output * output)
1215 {
1216 uint32_t Id = output->Id();
1217
1218 QueueCommand([this, Id] ()
1219 {
1220 ClearCommand();
1221
1222 if (Connected() == true)
1223 {
1224 Debug("Client::Enable output %d", Id);
1225
1226 if (mpd_run_enable_output(connection_, Id) == true)
1227 {
1228 EventData Data; Data.id = Id;
1229 Main::Vimpc::CreateEvent(Event::OutputEnabled, Data);
1230 Main::Vimpc::CreateEvent(Event::Repaint, Data);
1231 }
1232 }
1233 else
1234 {
1235 ErrorString(ErrorNumber::ClientNoConnection);
1236 }
1237 });
1238 }
1239
DisableOutput(Mpc::Output * output)1240 void Client::DisableOutput(Mpc::Output * output)
1241 {
1242 uint32_t Id = output->Id();
1243
1244 QueueCommand([this, Id] ()
1245 {
1246 ClearCommand();
1247
1248 if (Connected() == true)
1249 {
1250 Debug("Client::Disable output %d", Id);
1251
1252 if (mpd_run_disable_output(connection_, Id) == true)
1253 {
1254 EventData Data; Data.id = Id;
1255 Main::Vimpc::CreateEvent(Event::OutputDisabled, Data);
1256 Main::Vimpc::CreateEvent(Event::Repaint, Data);
1257 }
1258 }
1259 else
1260 {
1261 ErrorString(ErrorNumber::ClientNoConnection);
1262 }
1263 });
1264 }
1265
1266
Add(Mpc::Song * song)1267 void Client::Add(Mpc::Song * song)
1268 {
1269 if (song != NULL)
1270 {
1271 (void) Add(*song);
1272 }
1273 }
1274
Add(std::vector<Mpc::Song * > songs)1275 void Client::Add(std::vector<Mpc::Song *> songs)
1276 {
1277 std::vector<std::string> URIs;
1278
1279 for (auto song : songs)
1280 {
1281 URIs.push_back(song->URI());
1282 }
1283
1284 QueueCommand([this, URIs] ()
1285 {
1286 ClearCommand();
1287
1288 if (Connected() == true)
1289 {
1290 if (mpd_command_list_begin(connection_, false) == true)
1291 {
1292 listMode_ = true;
1293
1294 Debug("Client::List add started");
1295
1296 for (auto URI : URIs)
1297 {
1298 mpd_send_add(connection_, URI.c_str());
1299 }
1300
1301 if (mpd_command_list_end(connection_) == true)
1302 {
1303 Debug("Client::List add success");
1304 listMode_ = false;
1305
1306 for (auto URI : URIs)
1307 {
1308 EventData Data; Data.uri = URI; Data.pos1 = -1;
1309 Main::Vimpc::CreateEvent(Event::PlaylistAdd, Data);
1310 }
1311
1312 EventData Data;
1313 Main::Vimpc::CreateEvent(Event::CommandListSend, Data);
1314 Main::Vimpc::CreateEvent(Event::Repaint, Data);
1315 }
1316 else
1317 {
1318 CheckError();
1319 }
1320 }
1321 else
1322 {
1323 CheckError();
1324 }
1325 }
1326 else
1327 {
1328 ErrorString(ErrorNumber::ClientNoConnection);
1329 }
1330 });
1331 }
1332
Add(Mpc::Song & song)1333 void Client::Add(Mpc::Song & song)
1334 {
1335 std::string URI = song.URI();
1336
1337 QueueCommand([this, URI] ()
1338 {
1339 ClearCommand();
1340
1341 if (Connected() == true)
1342 {
1343 //Debug("Client::Add song %s", URI.c_str());
1344 mpd_send_add(connection_, URI.c_str());
1345
1346 EventData Data; Data.uri = URI; Data.pos1 = -1;
1347 Main::Vimpc::CreateEvent(Event::PlaylistAdd, Data);
1348 }
1349 else
1350 {
1351 ErrorString(ErrorNumber::ClientNoConnection);
1352 }
1353 });
1354 }
1355
Add(Mpc::Song & song,uint32_t position)1356 void Client::Add(Mpc::Song & song, uint32_t position)
1357 {
1358 std::string URI = song.URI();
1359
1360 QueueCommand([this, URI, position] ()
1361 {
1362 ClearCommand();
1363
1364 if (Connected() == true)
1365 {
1366 Debug("Client::Add song %s at %u", URI.c_str(), position);
1367 mpd_send_add_id_to(connection_, URI.c_str(), position);
1368
1369 EventData Data; Data.uri = URI; Data.pos1 = position;
1370 Main::Vimpc::CreateEvent(Event::PlaylistAdd, Data);
1371
1372 if ((currentSongId_ > -1) && (position <= static_cast<uint32_t>(currentSongId_)))
1373 {
1374 ++currentSongId_;
1375 EventData IdData; IdData.id = currentSongId_;
1376 Main::Vimpc::CreateEvent(Event::CurrentSongId, IdData);
1377 }
1378 }
1379 else
1380 {
1381 ErrorString(ErrorNumber::ClientNoConnection);
1382 }
1383 });
1384 }
1385
AddAllSongs()1386 void Client::AddAllSongs()
1387 {
1388 QueueCommand([this] ()
1389 {
1390 ClearCommand();
1391
1392 if (Connected() == true)
1393 {
1394 Debug("Client::Add all songs");
1395 mpd_send_add(connection_, "");
1396 }
1397 else
1398 {
1399 ErrorString(ErrorNumber::ClientNoConnection);
1400 }
1401 });
1402 }
1403
Add(std::string const & URI)1404 void Client::Add(std::string const & URI)
1405 {
1406 QueueCommand([this, URI] ()
1407 {
1408 ClearCommand();
1409
1410 if (Connected() == true)
1411 {
1412 Debug("Client::Add uri %s", URI.c_str());
1413 mpd_send_add(connection_, URI.c_str());
1414 }
1415 else
1416 {
1417 ErrorString(ErrorNumber::ClientNoConnection);
1418 }
1419 });
1420 }
1421
1422
Delete(uint32_t position)1423 void Client::Delete(uint32_t position)
1424 {
1425 QueueCommand([this, position] ()
1426 {
1427 ClearCommand();
1428
1429 // There might be an add in the queue, so we can't use the totalNumberOfSongs_ to determine
1430 // whether or not to do a delete
1431 if (Connected() == true) // && (totalNumberOfSongs_ > 0))
1432 {
1433 Debug("Client::Delete position %u", position);
1434 mpd_send_delete(connection_, position);
1435
1436 if ((currentSongId_ > -1) && (position < static_cast<uint32_t>(currentSongId_)))
1437 {
1438 --currentSongId_;
1439 EventData IdData; IdData.id = currentSongId_;
1440 Main::Vimpc::CreateEvent(Event::CurrentSongId, IdData);
1441 }
1442 }
1443 else if (Connected() == false)
1444 {
1445 ErrorString(ErrorNumber::ClientNoConnection);
1446 }
1447 });
1448
1449 Main::Playlist().Remove(position, 1);
1450 }
1451
Delete(uint32_t position1,uint32_t position2)1452 void Client::Delete(uint32_t position1, uint32_t position2)
1453 {
1454 QueueCommand([this, position1, position2] ()
1455 {
1456 // There might be an add in the queue, so we can't use the totalNumberOfSongs_ to determine
1457 // whether or not to do a delete
1458 if (Connected() == true) // && (totalNumberOfSongs_ > 0))
1459 {
1460 // Only use range if MPD is >= 0.16
1461 if ((versionMajor_ == 0) && (versionMinor_ < 16))
1462 {
1463 CommandList list(*this);
1464
1465 for (uint32_t i = 0; i < (position2 - position1); ++i)
1466 {
1467 Delete(position1);
1468 }
1469 }
1470 else
1471 {
1472 ClearCommand();
1473
1474 if (Connected() == true)
1475 {
1476 Debug("Client::Delete range %u:%u", position1, position2);
1477 mpd_send_delete_range(connection_, position1, position2);
1478
1479 if (currentSongId_ > -1)
1480 {
1481 uint32_t const songId = static_cast<uint32_t>(currentSongId_);
1482
1483 if ((position1 < songId) && (position2 < songId))
1484 {
1485 currentSongId_ -= position2 - position1;
1486 }
1487 else if ((position1 <= songId) && (position2 >= songId))
1488 {
1489 currentSongId_ -= (currentSongId_ - position1);
1490 }
1491
1492 EventData IdData; IdData.id = currentSongId_;
1493 Main::Vimpc::CreateEvent(Event::CurrentSongId, IdData);
1494 }
1495 }
1496 }
1497 }
1498
1499 if (Connected() == false)
1500 {
1501 ErrorString(ErrorNumber::ClientNoConnection);
1502 }
1503 });
1504
1505 Main::Playlist().Remove(position1, position2 - position1);
1506 }
1507
Clear()1508 void Client::Clear()
1509 {
1510 QueueCommand([this] ()
1511 {
1512 ClearCommand();
1513
1514 if (Connected() == true)
1515 {
1516 Debug("Client::Clear");
1517 mpd_send_clear(connection_);
1518 }
1519 else if (Connected() == false)
1520 {
1521 ErrorString(ErrorNumber::ClientNoConnection);
1522 }
1523 });
1524 }
1525
1526
SearchAny(std::string const & search,bool exact)1527 void Client::SearchAny(std::string const & search, bool exact)
1528 {
1529 QueueCommand([this, search, exact] ()
1530 {
1531 ClearCommand();
1532
1533 if (Connected() == true)
1534 {
1535 Debug("Client::Search any %s - exact %d", search.c_str(), static_cast<int32_t>(exact));
1536 mpd_search_db_songs(connection_, exact);
1537 mpd_search_add_any_tag_constraint(connection_, MPD_OPERATOR_DEFAULT, search.c_str());
1538 }
1539 });
1540 }
1541
SearchArtist(std::string const & search,bool exact)1542 void Client::SearchArtist(std::string const & search, bool exact)
1543 {
1544 QueueCommand([this, search, exact] ()
1545 {
1546 ClearCommand();
1547
1548 if (Connected() == true)
1549 {
1550 Debug("Client::Search artist %s - exact %d", search.c_str(), static_cast<int32_t>(exact));
1551 mpd_search_db_songs(connection_, exact);
1552 mpd_search_add_tag_constraint(connection_, MPD_OPERATOR_DEFAULT, MPD_TAG_ARTIST, search.c_str());
1553 }
1554 });
1555 }
1556
SearchGenre(std::string const & search,bool exact)1557 void Client::SearchGenre(std::string const & search, bool exact)
1558 {
1559 QueueCommand([this, search, exact] ()
1560 {
1561 ClearCommand();
1562
1563 if (Connected() == true)
1564 {
1565 Debug("Client::Search genre %s - exact %d", search.c_str(), static_cast<int32_t>(exact));
1566 mpd_search_db_songs(connection_, exact);
1567 mpd_search_add_tag_constraint(connection_, MPD_OPERATOR_DEFAULT, MPD_TAG_GENRE, search.c_str());
1568 }
1569 });
1570 }
1571
1572
SearchAlbum(std::string const & search,bool exact)1573 void Client::SearchAlbum(std::string const & search, bool exact)
1574 {
1575 QueueCommand([this, search, exact] ()
1576 {
1577 ClearCommand();
1578
1579 if (Connected() == true)
1580 {
1581 Debug("Client::Search album %s - exact %d", search.c_str(), static_cast<int32_t>(exact));
1582 mpd_search_db_songs(connection_, exact);
1583 mpd_search_add_tag_constraint(connection_, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM, search.c_str());
1584 }
1585 });
1586 }
1587
SearchSong(std::string const & search,bool exact)1588 void Client::SearchSong(std::string const & search, bool exact)
1589 {
1590 QueueCommand([this, search, exact] ()
1591 {
1592 ClearCommand();
1593
1594 if (Connected() == true)
1595 {
1596 Debug("Client::Search title %s - exact %d", search.c_str(), static_cast<int32_t>(exact));
1597 mpd_search_db_songs(connection_, exact);
1598 mpd_search_add_tag_constraint(connection_, MPD_OPERATOR_DEFAULT, MPD_TAG_TITLE, search.c_str());
1599 }
1600 });
1601 }
1602
1603
AddAllSearchResults()1604 void Client::AddAllSearchResults()
1605 {
1606 QueueCommand([this] ()
1607 {
1608 if (Connected())
1609 {
1610 Mpc::Song Song;
1611
1612 // Start the search
1613 Debug("Client::Add all search results");
1614 mpd_search_commit(connection_);
1615
1616 // Recv the songs and do some callbacks
1617 mpd_song * nextSong = mpd_recv_song(connection_);
1618
1619 if (nextSong == NULL)
1620 {
1621 ErrorString(ErrorNumber::FindNoResults);
1622 }
1623 else
1624 {
1625 for (; nextSong != NULL; nextSong = mpd_recv_song(connection_))
1626 {
1627 Song.SetURI(mpd_song_get_uri(nextSong));
1628 Add(Song);
1629 mpd_song_free(nextSong);
1630 }
1631 }
1632 }
1633 });
1634 }
1635
SearchResults(std::string const & name)1636 void Client::SearchResults(std::string const & name)
1637 {
1638 QueueCommand([this, name] ()
1639 {
1640 std::string const SongFormat = settings_.Get(Setting::SongFormat);
1641
1642 if (Connected())
1643 {
1644 Mpc::Song Song;
1645
1646 // Start the search
1647 Debug("Client::Search results via events");
1648 mpd_search_commit(connection_);
1649
1650 // Recv the songs and do some callbacks
1651 mpd_song * nextSong = mpd_recv_song(connection_);
1652
1653 if (nextSong == NULL)
1654 {
1655 ErrorString(ErrorNumber::FindNoResults);
1656 }
1657 else
1658 {
1659 EventData Data; Data.name = name;
1660
1661 for (; nextSong != NULL; nextSong = mpd_recv_song(connection_))
1662 {
1663 if ((settings_.Get(Setting::ListAllMeta) == false))
1664 {
1665 Mpc::Song * song = Main::Library().Song(mpd_song_get_uri(nextSong));
1666
1667 if (song == NULL)
1668 {
1669 song = CreateSong(nextSong);
1670 // Pre cache the print of the song
1671 (void) song->FormatString(SongFormat);
1672 EventData Data; Data.song = song;
1673 Main::Vimpc::CreateEvent(Event::DatabaseSong, Data);
1674 }
1675 }
1676
1677 Data.uris.push_back(mpd_song_get_uri(nextSong));
1678 mpd_song_free(nextSong);
1679 }
1680
1681 Main::Vimpc::CreateEvent(Event::SearchResults, Data);
1682 }
1683 }
1684 });
1685 }
1686
1687
StateEvent()1688 void Client::StateEvent()
1689 {
1690 if (Connected() == true)
1691 {
1692 switch (state_)
1693 {
1694 case MPD_STATE_STOP:
1695 currentState_ = "Stopped";
1696 break;
1697 case MPD_STATE_PLAY:
1698 currentState_ = "Playing";
1699 break;
1700 case MPD_STATE_PAUSE:
1701 currentState_ = "Paused";
1702 break;
1703 case MPD_STATE_UNKNOWN:
1704 default:
1705 currentState_ = "Unknown";
1706 break;
1707 }
1708 }
1709
1710 EventData Data; Data.clientstate = currentState_;
1711 Main::Vimpc::CreateEvent(Event::CurrentState, Data);
1712 }
1713
1714
Rescan(std::string const & Path)1715 void Client::Rescan(std::string const & Path)
1716 {
1717 QueueCommand([this, Path] ()
1718 {
1719 ClearCommand();
1720
1721 if (Connected() == true)
1722 {
1723 Debug("Client::Rescan %s", (Path != "") ? Path.c_str() : "all");
1724
1725 if (mpd_run_rescan(connection_, (Path != "") ? Path.c_str() : NULL) == true)
1726 {
1727 updating_ = true;
1728
1729 EventData Data;
1730 Main::Vimpc::CreateEvent(Event::Update, Data);
1731 }
1732 }
1733 else
1734 {
1735 ErrorString(ErrorNumber::ClientNoConnection);
1736 }
1737 });
1738 }
1739
Update(std::string const & Path)1740 void Client::Update(std::string const & Path)
1741 {
1742 QueueCommand([this, Path] ()
1743 {
1744 ClearCommand();
1745
1746 if (Connected() == true)
1747 {
1748 Debug("Client::Update %s", (Path != "") ? Path.c_str() : "all");
1749
1750 if (mpd_run_update(connection_, (Path != "") ? Path.c_str() : NULL) == true)
1751 {
1752 updating_ = true;
1753
1754 EventData Data;
1755 Main::Vimpc::CreateEvent(Event::Update, Data);
1756 }
1757 }
1758 else
1759 {
1760 ErrorString(ErrorNumber::ClientNoConnection);
1761 }
1762 });
1763 }
1764
IncrementTime(long time)1765 void Client::IncrementTime(long time)
1766 {
1767 bool const poll = settings_.Get(::Setting::Polling);
1768
1769 if (time >= 0)
1770 {
1771 timeSinceUpdate_ += time;
1772
1773 if (state_ == MPD_STATE_PLAY)
1774 {
1775 if (elapsed_ != (mpdelapsed_ + (timeSinceUpdate_ / 1000)))
1776 {
1777 elapsed_ = mpdelapsed_ + (timeSinceUpdate_ / 1000);
1778
1779 EventData EData; EData.value = elapsed_;
1780 Main::Vimpc::CreateEvent(Event::Elapsed, EData);
1781 }
1782 }
1783
1784 if ((poll == true) && (timeSinceUpdate_ > 900))
1785 {
1786 UpdateStatus();
1787 }
1788 }
1789 else
1790 {
1791 UpdateStatus();
1792 }
1793 }
1794
IdleMode()1795 void Client::IdleMode()
1796 {
1797 if ((Connected() == true) &&
1798 (idleMode_ == false) &&
1799 (ready_ == true) &&
1800 (settings_.Get(Setting::Polling) == false))
1801 {
1802 ClearCommand();
1803
1804 if ((Connected() == true))
1805 {
1806 if (mpd_send_idle(connection_) == true)
1807 {
1808 Debug("Client::Enter idle mode");
1809
1810 idleMode_ = true;
1811
1812 EventData Data;
1813 Main::Vimpc::CreateEvent(Event::IdleMode, Data);
1814 }
1815 }
1816 }
1817 }
1818
ExitIdleMode()1819 void Client::ExitIdleMode()
1820 {
1821 if ((idleMode_ == true) && (Connected() == true))
1822 {
1823 mpd_send_noidle(connection_);
1824
1825 if (mpd_recv_idle(connection_, false) != 0)
1826 {
1827 UpdateStatus();
1828 }
1829
1830 Debug("Client::Cancelled idle mode");
1831 CheckError();
1832
1833 idleMode_ = false;
1834
1835 EventData Data;
1836 Main::Vimpc::CreateEvent(Event::StopIdleMode, Data);
1837 }
1838 }
1839
CheckForEvents()1840 void Client::CheckForEvents()
1841 {
1842 if ((idleMode_ == true) && (Connected() == true) && (fd_ != -1))
1843 {
1844 pollfd fds;
1845
1846 fds.fd = fd_;
1847 fds.events = POLLIN;
1848
1849 if (poll(&fds, 1, 0) > 0)
1850 {
1851 idleMode_ = false;
1852
1853 EventData Data;
1854 Main::Vimpc::CreateEvent(Event::StopIdleMode, Data);
1855
1856 if (mpd_recv_idle(connection_, false) != 0)
1857 {
1858 UpdateStatus();
1859 Debug("Client::Event occurred");
1860 }
1861
1862 Debug("Client::Left idle mode");
1863
1864 CheckError();
1865 }
1866 }
1867 }
1868
UpdateCurrentSong()1869 void Client::UpdateCurrentSong()
1870 {
1871 ClearCommand();
1872
1873 if ((Connected() == true))
1874 {
1875 if (listMode_ == false)
1876 {
1877 if (currentSong_ != NULL)
1878 {
1879 mpd_song_free(currentSong_);
1880 currentSong_ = NULL;
1881 currentSongId_ = -1;
1882 currentSongURI_ = "";
1883 }
1884
1885 Debug("Client::Send get current song");
1886 currentSong_ = mpd_run_current_song(connection_);
1887 CheckError();
1888
1889 if (currentSong_ != NULL)
1890 {
1891 currentSongId_ = mpd_song_get_pos(currentSong_);
1892 currentSongURI_ = mpd_song_get_uri(currentSong_);
1893
1894 Debug("Client::Get current song %d:%s", currentSongId_, currentSongURI_.c_str());
1895 }
1896 }
1897 }
1898 else
1899 {
1900 currentSongId_ = -1;
1901 currentSongURI_ = "";
1902 }
1903
1904 EventData IdData; IdData.id = currentSongId_;
1905 Main::Vimpc::CreateEvent(Event::CurrentSongId, IdData);
1906
1907 if (autoscroll_ == true)
1908 {
1909 Main::Vimpc::CreateEvent(Event::Autoscroll, IdData);
1910 autoscroll_ = false;
1911 }
1912
1913 if (currentSong_ != NULL)
1914 {
1915 EventData SongData; SongData.currentSong = mpd_song_dup(currentSong_);
1916 Main::Vimpc::CreateEvent(Event::CurrentSong, SongData);
1917 }
1918 }
1919
ClientQueueExecutor(Mpc::Client * client)1920 void Client::ClientQueueExecutor(Mpc::Client * client)
1921 {
1922 struct timeval start, end;
1923 gettimeofday(&start, NULL);
1924
1925 while (Running == true)
1926 {
1927 gettimeofday(&end, NULL);
1928 long const seconds = end.tv_sec - start.tv_sec;
1929 long const useconds = end.tv_usec - start.tv_usec;
1930 long const mtime = (seconds * 1000 + (useconds/1000.0)) + 0.5;
1931 IncrementTime(mtime);
1932 gettimeofday(&start, NULL);
1933
1934 {
1935 UniqueLock<Mutex> Lock(QueueMutex);
1936
1937 if ((Queue.empty() == false) ||
1938 (ConditionWait(Condition, Lock, 250) != false))
1939 {
1940 if (Queue.empty() == false)
1941 {
1942 FUNCTION<void()> function = Queue.front();
1943 Queue.pop_front();
1944 Lock.unlock();
1945
1946 ExitIdleMode();
1947 function();
1948 continue;
1949 }
1950 }
1951 }
1952
1953 {
1954 UniqueLock<Mutex> Lock(QueueMutex);
1955
1956 if ((Queue.empty() == true) && (listMode_ == false))
1957 {
1958 if (queueUpdate_ == true)
1959 {
1960 Lock.unlock();
1961 QueueMetaChanges();
1962 }
1963 else if (idleMode_ == false)
1964 {
1965 Lock.unlock();
1966 IdleMode();
1967 }
1968 else if (idleMode_ == true)
1969 {
1970 Lock.unlock();
1971 CheckForEvents();
1972 }
1973 }
1974 }
1975 }
1976 }
1977
1978
ClearCommand()1979 void Client::ClearCommand()
1980 {
1981 error_ = false;
1982
1983 if ((listMode_ == false) && (idleMode_ == false) && (Connected() == true))
1984 {
1985 Debug("Client::Finish the response");
1986 mpd_response_finish(connection_);
1987 CheckError();
1988 }
1989 }
1990
IsPasswordRequired()1991 bool Client::IsPasswordRequired()
1992 {
1993 Debug("Client::Trying status update to determine if password required");
1994
1995 bool Result = false;
1996
1997 if (Connected() == true)
1998 {
1999 struct mpd_status * status = mpd_run_status(connection_);
2000
2001 mpd_error error = mpd_connection_get_error(connection_);
2002
2003 if (error != MPD_ERROR_SUCCESS)
2004 {
2005 if ((error == MPD_ERROR_SERVER) &&
2006 (mpd_connection_get_server_error(connection_) == MPD_SERVER_ERROR_PERMISSION))
2007 {
2008 Result = true;
2009 }
2010
2011 mpd_connection_clear_error(connection_);
2012 }
2013
2014 if (status != NULL)
2015 {
2016 mpd_status_free(status);
2017 }
2018 }
2019
2020 return Result;
2021 }
2022
2023
GetAllMetaInformation()2024 void Client::GetAllMetaInformation()
2025 {
2026 ClearCommand();
2027
2028 std::string const SongFormat = settings_.Get(Setting::SongFormat);
2029
2030 std::vector<Mpc::Song *> songs;
2031 std::vector<std::string> paths;
2032 std::vector<std::pair<std::string, std::string> > lists;
2033
2034 if (Connected() == true)
2035 {
2036 EventData DBData;
2037 Main::Vimpc::CreateEvent(Event::ClearDatabase, DBData);
2038
2039 Debug("Client::Get all meta information");
2040
2041 if ((settings_.Get(Setting::ListAllMeta) == true))
2042 {
2043 mpd_send_list_all_meta(connection_, NULL);
2044
2045 mpd_entity * nextEntity = mpd_recv_entity(connection_);
2046
2047 for(; nextEntity != NULL; nextEntity = mpd_recv_entity(connection_))
2048 {
2049 if (mpd_entity_get_type(nextEntity) == MPD_ENTITY_TYPE_SONG)
2050 {
2051 mpd_song const * const nextSong = mpd_entity_get_song(nextEntity);
2052
2053 if (nextSong != NULL)
2054 {
2055 Song * const newSong = CreateSong(nextSong);
2056 songs.push_back(newSong);
2057 }
2058 }
2059 else if (mpd_entity_get_type(nextEntity) == MPD_ENTITY_TYPE_DIRECTORY)
2060 {
2061 mpd_directory const * const nextDirectory = mpd_entity_get_directory(nextEntity);
2062 paths.push_back(std::string(mpd_directory_get_path(nextDirectory)));
2063 }
2064 else if (mpd_entity_get_type(nextEntity) == MPD_ENTITY_TYPE_PLAYLIST)
2065 {
2066 mpd_playlist const * const nextPlaylist = mpd_entity_get_playlist(nextEntity);
2067
2068 if (nextPlaylist != NULL)
2069 {
2070 std::string const path = mpd_playlist_get_path(nextPlaylist);
2071 std::string name = path;
2072
2073 if (name.find("/") != std::string::npos)
2074 {
2075 name = name.substr(name.find_last_of("/") + 1);
2076 }
2077
2078 lists.push_back(std::make_pair(name, path));
2079 }
2080 }
2081
2082 mpd_entity_free(nextEntity);
2083 }
2084 }
2085 }
2086
2087 ClearCommand();
2088
2089 if (error_)
2090 {
2091 Debug("List all failed, disabling\n");
2092 Error(ErrorNumber::ErrorClear, "");
2093 ErrorString(ErrorNumber::ClientNoMeta, "not supported on server");
2094 settings_.Set(Setting::ListAllMeta, false);
2095 }
2096
2097 EventData DatabaseEvent;
2098 DatabaseEvent.state = (settings_.Get(Setting::ListAllMeta));
2099 Main::Vimpc::CreateEvent(Event::DatabaseEnabled, DatabaseEvent);
2100
2101 if (Connected() == true)
2102 {
2103 // Songs, paths, lists, etc are collated and the events created this way
2104 // because mpd seems to disconnect you if you take to long to recv entities
2105 for (auto song : songs)
2106 {
2107 // Pre cache the print of the song
2108 (void) song->FormatString(SongFormat);
2109 EventData Data; Data.song = song;
2110 Main::Vimpc::CreateEvent(Event::DatabaseSong, Data);
2111 }
2112
2113 for (auto path : paths)
2114 {
2115 EventData Data; Data.uri = path;
2116 Main::Vimpc::CreateEvent(Event::DatabasePath, Data);
2117 }
2118
2119 for (auto list : lists)
2120 {
2121 EventData Data; Data.name = list.first; Data.uri = list.second;
2122 Main::Vimpc::CreateEvent(Event::DatabaseListFile, Data);
2123 }
2124 }
2125
2126 songs.clear();
2127
2128 if (Connected() == true)
2129 {
2130 Debug("Client::List queue meta data");
2131 mpd_send_list_queue_meta(connection_);
2132
2133 mpd_song * nextSong = mpd_recv_song(connection_);
2134
2135 for (; nextSong != NULL; nextSong = mpd_recv_song(connection_))
2136 {
2137 EventData Data; Data.song = NULL; Data.uri = mpd_song_get_uri(nextSong); Data.pos1 = -1;
2138
2139 if (((settings_.Get(Setting::ListAllMeta) == false) &&
2140 (Main::Library().Song(Data.uri) == NULL)) ||
2141 // Handle "virtual" songs embedded within files
2142 (mpd_song_get_end(nextSong) != 0))
2143 {
2144 Song * const newSong = CreateSong(nextSong);
2145 Data.song = newSong;
2146
2147 if (settings_.Get(Setting::ListAllMeta) == false) {
2148 songs.push_back(newSong);
2149 }
2150 }
2151
2152 Main::Vimpc::CreateEvent(Event::PlaylistAdd, Data);
2153
2154 mpd_song_free(nextSong);
2155 }
2156 }
2157
2158 if (settings_.Get(Setting::ListAllMeta) == false) {
2159 if (Connected() == true)
2160 {
2161 // Songs, paths, lists, etc are collated and the events created this way
2162 // because mpd seems to disconnect you if you take to long to recv entities
2163 for (auto song : songs)
2164 {
2165 // Pre cache the print of the song
2166 (void) song->FormatString(SongFormat);
2167 EventData Data; Data.song = song;
2168 Main::Vimpc::CreateEvent(Event::DatabaseSong, Data);
2169 }
2170 }
2171 }
2172
2173 if (Connected() == true)
2174 {
2175 #if LIBMPDCLIENT_CHECK_VERSION(2,5,0)
2176 ClearCommand();
2177
2178 if (Connected() == true)
2179 {
2180 Debug("Client::Request playlists");
2181
2182 if (mpd_send_list_playlists(connection_))
2183 {
2184 mpd_playlist * nextPlaylist = mpd_recv_playlist(connection_);
2185
2186 for(; nextPlaylist != NULL; nextPlaylist = mpd_recv_playlist(connection_))
2187 {
2188 std::string const playlist = mpd_playlist_get_path(nextPlaylist);
2189
2190 EventData Data; Data.uri = playlist; Data.name = playlist;
2191 Main::Vimpc::CreateEvent(Event::DatabaseList, Data);
2192
2193 mpd_playlist_free(nextPlaylist);
2194 }
2195 }
2196
2197 mpd_connection_clear_error(connection_);
2198 }
2199 #endif
2200 }
2201
2202 if (Connected() == true)
2203 {
2204 EventData Data;
2205 Main::Vimpc::CreateEvent(Event::AllMetaDataReady, Data);
2206 Main::Vimpc::CreateEvent(Event::Repaint, Data);
2207 }
2208
2209 #if !LIBMPDCLIENT_CHECK_VERSION(2,5,0)
2210 GetAllMetaFromRoot();
2211 #endif
2212 }
2213
GetAllOutputs()2214 void Client::GetAllOutputs()
2215 {
2216 QueueCommand([this] ()
2217 {
2218 ClearCommand();
2219
2220 if (Connected() == true)
2221 {
2222 Debug("Client::Get outputs");
2223 mpd_send_outputs(connection_);
2224
2225 mpd_output * next = mpd_recv_output(connection_);
2226
2227 for (; next != NULL; next = mpd_recv_output(connection_))
2228 {
2229 Mpc::Output * output = new Mpc::Output(mpd_output_get_id(next));
2230
2231 output->SetEnabled(mpd_output_get_enabled(next));
2232 output->SetName(mpd_output_get_name(next));
2233
2234 EventData Data; Data.output = output;
2235 Main::Vimpc::CreateEvent(Event::Output, Data);
2236
2237 mpd_output_free(next);
2238 }
2239
2240 Debug("Client::Get outputs complete");
2241 EventData Data;
2242 Main::Vimpc::CreateEvent(Event::Repaint,Data);
2243 }
2244 });
2245 }
2246
GetAllMetaFromRoot()2247 void Client::GetAllMetaFromRoot()
2248 {
2249 // This is a hack to get playlists when using older libmpdclients, it should
2250 // not be used unless absolutely necessary
2251 ClearCommand();
2252
2253 if (Connected() == true)
2254 {
2255 Debug("Client::Get all root meta");
2256 mpd_send_list_meta(connection_, "/");
2257
2258 mpd_entity * nextEntity = mpd_recv_entity(connection_);
2259
2260 for(; nextEntity != NULL; nextEntity = mpd_recv_entity(connection_))
2261 {
2262 if (mpd_entity_get_type(nextEntity) == MPD_ENTITY_TYPE_PLAYLIST)
2263 {
2264 mpd_playlist const * const nextPlaylist = mpd_entity_get_playlist(nextEntity);
2265
2266 if (nextPlaylist != NULL)
2267 {
2268 std::string const path = mpd_playlist_get_path(nextPlaylist);
2269 std::string name = path;
2270
2271 if (name.find("/") != std::string::npos)
2272 {
2273 name = name.substr(name.find_last_of("/") + 1);
2274 }
2275
2276 EventData Data; Data.uri = path; Data.name = name;
2277 Main::Vimpc::CreateEvent(Event::DatabaseList, Data);
2278 }
2279 }
2280
2281 mpd_entity_free(nextEntity);
2282 }
2283
2284 screen_.Invalidate(Ui::Screen::Lists);
2285 }
2286 }
2287
2288
StartCommandList()2289 void Client::StartCommandList()
2290 {
2291 QueueCommand([this] ()
2292 {
2293 ClearCommand();
2294
2295 if ((Connected() == true) && (listMode_ == false))
2296 {
2297 Debug("Client::Start command list");
2298
2299 if (mpd_command_list_begin(connection_, false) == true)
2300 {
2301 listMode_ = true;
2302 }
2303 else
2304 {
2305 Debug("Client::Start command list failed");
2306 CheckError();
2307 }
2308 }
2309 });
2310 }
2311
SendCommandList()2312 void Client::SendCommandList()
2313 {
2314 QueueCommand([this] ()
2315 {
2316 if ((Connected() == true) && (listMode_ == true))
2317 {
2318 Debug("Client::End command list");
2319
2320 if (mpd_command_list_end(connection_) == true)
2321 {
2322 listMode_ = false;
2323 EventData Data;
2324 Main::Vimpc::CreateEvent(Event::CommandListSend, Data);
2325 Main::Vimpc::CreateEvent(Event::Repaint, Data);
2326 }
2327 else
2328 {
2329 Debug("Client::End command list failed");
2330 CheckError();
2331 }
2332 }
2333 });
2334 }
2335
2336
QueueVersion()2337 unsigned int Client::QueueVersion()
2338 {
2339 return queueVersion_;
2340 }
2341
2342
SetStateAndEvent(int event,bool & state,bool value)2343 void Client::SetStateAndEvent(int event, bool & state, bool value)
2344 {
2345 state = value;
2346 EventData Data; Data.state = value;
2347 Main::Vimpc::CreateEvent(event, Data);
2348 }
2349
UpdateStatus(bool ExpectUpdate)2350 void Client::UpdateStatus(bool ExpectUpdate)
2351 {
2352 QueueCommand([this, ExpectUpdate] ()
2353 {
2354 ClearCommand();
2355
2356 if ((Connected() == true) && (listMode_ == false))
2357 {
2358 if (currentStatus_ != NULL)
2359 {
2360 mpd_status_free(currentStatus_);
2361 currentStatus_ = NULL;
2362 }
2363
2364 Debug("Client::Get current status");
2365 struct mpd_status * status = mpd_run_status(connection_);
2366 CheckError();
2367
2368 if (status != NULL)
2369 {
2370 currentStatus_ = status;
2371 timeSinceUpdate_ = 0;
2372
2373 unsigned int version = mpd_status_get_queue_version(currentStatus_);
2374 unsigned int qVersion = static_cast<uint32_t>(queueVersion_);
2375 bool const wasUpdating = updating_;
2376
2377 if (static_cast<int32_t>(volume_) != mpd_status_get_volume(currentStatus_))
2378 {
2379 volume_ = mpd_status_get_volume(currentStatus_);
2380
2381 EventData Data; Data.value = volume_;
2382 Main::Vimpc::CreateEvent(Event::Volume, Data);
2383 }
2384
2385 if (updating_ != (mpd_status_get_update_id(currentStatus_) >= 1))
2386 {
2387 updating_ = (mpd_status_get_update_id(currentStatus_) >= 1);
2388
2389 if (updating_ == true)
2390 {
2391 EventData Data;
2392 Main::Vimpc::CreateEvent(Event::Update, Data);
2393 }
2394 }
2395
2396 if (random_ != mpd_status_get_random(currentStatus_))
2397 {
2398 SetStateAndEvent(Event::Random, random_, mpd_status_get_random(currentStatus_));
2399 }
2400
2401 if (repeat_ != mpd_status_get_repeat(currentStatus_))
2402 {
2403 SetStateAndEvent(Event::Repeat, repeat_, mpd_status_get_repeat(currentStatus_));
2404 }
2405
2406 if (single_ != mpd_status_get_single(currentStatus_))
2407 {
2408 SetStateAndEvent(Event::Single, single_, mpd_status_get_single(currentStatus_));
2409 }
2410
2411 if (consume_ != mpd_status_get_consume(currentStatus_))
2412 {
2413 SetStateAndEvent(Event::Consume, consume_, mpd_status_get_consume(currentStatus_));
2414 }
2415
2416 if (totalNumberOfSongs_ != mpd_status_get_queue_length(currentStatus_))
2417 {
2418 totalNumberOfSongs_ = mpd_status_get_queue_length(currentStatus_);
2419
2420 EventData Data; Data.count = totalNumberOfSongs_;
2421 Main::Vimpc::CreateEvent(Event::TotalSongCount, Data);
2422 }
2423
2424 if (crossfade_ != (mpd_status_get_crossfade(currentStatus_) > 0))
2425 {
2426 crossfade_ = (mpd_status_get_crossfade(currentStatus_) > 0);
2427 EventData Data; Data.state = crossfade_;
2428 Main::Vimpc::CreateEvent(Event::Crossfade, Data);
2429 }
2430
2431 if (crossfade_ == true)
2432 {
2433 if (crossfadeTime_ != mpd_status_get_crossfade(currentStatus_))
2434 {
2435 crossfadeTime_ = mpd_status_get_crossfade(currentStatus_);
2436 EventData Data; Data.value = crossfadeTime_;
2437 Main::Vimpc::CreateEvent(Event::CrossfadeTime, Data);
2438 }
2439 }
2440
2441 // Check if we need to update the current song
2442 if ((mpdstate_ != mpd_status_get_state(currentStatus_)) ||
2443 ((mpdstate_ != MPD_STATE_STOP) && (currentSong_ == NULL)) ||
2444 ((mpdstate_ == MPD_STATE_STOP) && (currentSong_ != NULL)) ||
2445 ((currentSong_ != NULL) &&
2446 (mpd_song_get_duration(currentSong_) > 0) &&
2447 ((elapsed_ >= mpd_song_get_duration(currentSong_) - 3) ||
2448 (mpd_status_get_elapsed_time(currentStatus_) < mpdelapsed_) ||
2449 (mpd_status_get_elapsed_time(currentStatus_) <= 3))))
2450 {
2451 autoscroll_ = true;
2452 UpdateCurrentSong();
2453 }
2454
2455 mpdstate_ = mpd_status_get_state(currentStatus_);
2456 mpdelapsed_ = mpd_status_get_elapsed_time(currentStatus_);
2457
2458 if (state_ != mpdstate_)
2459 {
2460 state_ = mpdstate_;
2461 StateEvent();
2462 }
2463
2464 if (mpdstate_ == MPD_STATE_STOP)
2465 {
2466 currentSongId_ = -1;
2467 currentSongURI_ = "";
2468
2469 EventData IdData; IdData.id = currentSongId_;
2470 Main::Vimpc::CreateEvent(Event::CurrentSongId, IdData);
2471 EventData Data; Data.currentSong = NULL;
2472 Main::Vimpc::CreateEvent(Event::CurrentSong, Data);
2473 }
2474
2475 if (mpdstate_ != MPD_STATE_PLAY)
2476 {
2477 elapsed_ = mpdelapsed_;
2478 }
2479
2480 EventData EData; EData.value = elapsed_;
2481 Main::Vimpc::CreateEvent(Event::Elapsed, EData);
2482
2483 if ((queueVersion_ > -1) && (version > qVersion) && (queueUpdate_ == false))
2484 {
2485 oldVersion_ = queueVersion_;
2486 queueUpdate_ = true;
2487 }
2488
2489 if ((wasUpdating == true) && (updating_ == false))
2490 {
2491 GetAllMetaInformation();
2492 UpdateCurrentSong();
2493
2494 EventData Data;
2495 Main::Vimpc::CreateEvent(Event::UpdateComplete, Data);
2496 Main::Vimpc::CreateEvent(Event::Repaint, Data);
2497 }
2498
2499 queueVersion_ = version;
2500 }
2501 }
2502 });
2503 }
2504
CreateSong(mpd_song const * const song) const2505 Song * Client::CreateSong(mpd_song const * const song) const
2506 {
2507 static int count = 0;
2508
2509 Song * const newSong = new Song();
2510
2511 //Debug("Alloc a song %d %d", count, sizeof(Song));
2512
2513 newSong->SetArtist (mpd_song_get_tag(song, MPD_TAG_ARTIST, 0));
2514 newSong->SetAlbumArtist(mpd_song_get_tag(song, MPD_TAG_ALBUM_ARTIST, 0));
2515 newSong->SetAlbum (mpd_song_get_tag(song, MPD_TAG_ALBUM, 0));
2516 newSong->SetTitle (mpd_song_get_tag(song, MPD_TAG_TITLE, 0));
2517 newSong->SetTrack (mpd_song_get_tag(song, MPD_TAG_TRACK, 0));
2518 newSong->SetURI (mpd_song_get_uri(song));
2519 newSong->SetGenre (mpd_song_get_tag(song, MPD_TAG_GENRE, 0));
2520 newSong->SetDate (mpd_song_get_tag(song, MPD_TAG_DATE, 0));
2521 newSong->SetDisc (mpd_song_get_tag(song, MPD_TAG_DISC, 0));
2522 newSong->SetDuration (mpd_song_get_duration(song));
2523 newSong->SetVirtualEnd (mpd_song_get_end(song));
2524
2525 return newSong;
2526 }
2527
QueueMetaChanges()2528 void Client::QueueMetaChanges()
2529 {
2530 ClearCommand();
2531
2532 if (Connected() == true)
2533 {
2534 struct mpd_status * status = mpd_run_status(connection_);
2535 queueVersion_ = mpd_status_get_queue_version(status);
2536
2537 if (totalNumberOfSongs_ != mpd_status_get_queue_length(status))
2538 {
2539 totalNumberOfSongs_ = mpd_status_get_queue_length(status);
2540
2541 EventData Data; Data.count = totalNumberOfSongs_;
2542 Main::Vimpc::CreateEvent(Event::TotalSongCount, Data);
2543 }
2544
2545 if (oldVersion_ != queueVersion_)
2546 {
2547 Debug("Client::List queue meta data changes %d %d", oldVersion_, queueVersion_);
2548 mpd_send_queue_changes_meta(connection_, oldVersion_);
2549
2550 mpd_song * nextSong = mpd_recv_song(connection_);
2551 EventData Data;
2552 Data.count = totalNumberOfSongs_;
2553
2554 for (; nextSong != NULL; nextSong = mpd_recv_song(connection_))
2555 {
2556 Song * newSong = NULL;
2557
2558 if (((settings_.Get(Setting::ListAllMeta) == false) &&
2559 (Main::Library().Song(Data.uri) == NULL)) ||
2560 // Handle "virtual" songs embedded within files
2561 (mpd_song_get_end(nextSong) != 0))
2562 {
2563 newSong = CreateSong(nextSong);
2564 }
2565
2566 //Debug("Change: %d %s", mpd_song_get_pos(nextSong), mpd_song_get_uri(nextSong));
2567 Data.posuri.push_back(std::make_pair(mpd_song_get_pos(nextSong), std::make_pair(newSong, mpd_song_get_uri(nextSong))));
2568 mpd_song_free(nextSong);
2569 }
2570
2571 QueueMutex.lock();
2572
2573 if (Queue.size() == 0)
2574 {
2575 QueueMutex.unlock();
2576
2577 oldVersion_ = queueVersion_;
2578 queueUpdate_ = false;
2579 Main::Vimpc::CreateEvent(Event::PlaylistQueueReplace, Data);
2580
2581 EventData QueueData;
2582 Main::Vimpc::CreateEvent(Event::QueueUpdate, QueueData);
2583
2584 UpdateCurrentSong();
2585 }
2586 else
2587 {
2588 QueueMutex.unlock();
2589 }
2590 }
2591 else
2592 {
2593 queueUpdate_ = false;
2594 }
2595 }
2596 }
2597
2598
GetVersion()2599 void Client::GetVersion()
2600 {
2601 if (Connected() == true)
2602 {
2603 Debug("Client::Sending version request");
2604 unsigned const * version = mpd_connection_get_server_version(connection_);
2605 CheckError();
2606
2607 if (version != NULL)
2608 {
2609 versionMajor_ = version[0];
2610 versionMinor_ = version[1];
2611 versionPatch_ = version[2];
2612
2613 Debug("libmpdclient: %d.%d.%d", LIBMPDCLIENT_MAJOR_VERSION, LIBMPDCLIENT_MINOR_VERSION, LIBMPDCLIENT_PATCH_VERSION);
2614 Debug("MPD Server : %d.%d.%d", versionMajor_, versionMinor_, versionPatch_);
2615 }
2616 }
2617 }
2618
CheckError()2619 bool Client::CheckError()
2620 {
2621 if (connection_ != NULL)
2622 {
2623 if (mpd_connection_get_error(connection_) != MPD_ERROR_SUCCESS)
2624 {
2625 char error[255];
2626 snprintf(error, 255, "MPD Error: %s", mpd_connection_get_error_message(connection_));
2627 Error(ErrorNumber::ClientError, error);
2628
2629 Debug("Client::%s", error);
2630
2631 #ifdef _DEBUG_ASSERT_ON_ERROR
2632 ASSERT(false);
2633 #elif defined(_DEBUG_BREAK_ON_ERROR)
2634 BREAKPOINT
2635 #endif
2636 error_ = true;
2637
2638 bool const ClearError = mpd_connection_clear_error(connection_);
2639
2640 if (ClearError == false)
2641 {
2642 Debug("Client::Unable to clear error");
2643 DeleteConnection();
2644
2645 if ((settings_.Get(Setting::Reconnect) == true) && (retried_ == false))
2646 {
2647 retried_ = true;
2648 Connect(hostname_, port_);
2649 }
2650 }
2651 else
2652 {
2653 UpdateStatus();
2654 }
2655
2656 return true;
2657 }
2658 }
2659
2660 return false;
2661 }
2662
DeleteConnection()2663 void Client::DeleteConnection()
2664 {
2665 ready_ = false;
2666 listMode_ = false;
2667 currentState_ = "Disconnected";
2668 volume_ = -1;
2669 updating_ = false;
2670 random_ = false;
2671 single_ = false;
2672 consume_ = false;
2673 repeat_ = false;
2674 idleMode_ = false;
2675 crossfade_ = false;
2676 state_ = MPD_STATE_UNKNOWN;
2677
2678 totalNumberOfSongs_ = 0;
2679
2680 versionMajor_ = -1;
2681 versionMinor_ = -1;
2682 versionPatch_ = -1;
2683 queueVersion_ = -1;
2684
2685 StateEvent();
2686
2687 // \todo it would be nice to clear the queue here
2688 // but currently it also removes queued up connect attempts
2689 // to different hosts, etc, figure this out
2690 //Queue.clear();
2691
2692 if (connection_ != NULL)
2693 {
2694 mpd_connection_free(connection_);
2695 connection_ = NULL;
2696 fd_ = -1;
2697 }
2698
2699 EventData Data;
2700 Main::Vimpc::CreateEvent(Event::Disconnected, Data);
2701
2702 ENSURE(connection_ == NULL);
2703 }
2704
2705
2706 /* vim: set sw=3 ts=3: */
2707