From 6a3eb73a492946bd7079545f97894d3e8f174006 Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Sat, 6 Apr 2013 19:01:27 +0200 Subject: [PATCH] rewrite communication system with mpd --- src/actions.cpp | 170 ++-- src/browser.cpp | 31 +- src/browser.h | 2 + src/cmdargs.cpp | 37 +- src/display.cpp | 4 +- src/error.h | 2 - src/global.h | 3 - src/helpers.cpp | 10 +- src/helpers.h | 101 +- src/lastfm.cpp | 2 +- src/media_library.cpp | 28 +- src/mpdpp.cpp | 2384 ++++++++++++++++++++--------------------------- src/mpdpp.h | 218 ++--- src/ncmpcpp.cpp | 157 ++-- src/outputs.cpp | 11 +- src/playlist.cpp | 19 +- src/playlist.h | 1 + src/playlist_editor.cpp | 10 +- src/sel_items_adder.cpp | 41 +- src/server_info.cpp | 18 +- src/sort_playlist.cpp | 8 +- src/status.cpp | 244 ++--- src/status.h | 20 +- src/statusbar.cpp | 14 +- src/visualizer.cpp | 2 - 25 files changed, 1553 insertions(+), 1984 deletions(-) rewrite src/mpdpp.cpp (72%) diff --git a/src/actions.cpp b/src/actions.cpp index 5b39359..dcebc80 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -255,11 +255,20 @@ void setWindowsDimensions() bool connectToMPD() { - if (!Mpd.Connect()) + try + { + Mpd.Connect(); + if (Mpd.Version() < 16) + { + std::cout << "MPD < 0.16.0 is not supported, please upgrade\n"; + return false; + } + } + catch (std::exception &e) { std::cout << "Couldn't connect to MPD "; std::cout << "(host = " << Mpd.GetHostname() << ", port = " << Mpd.GetPort() << ")"; - std::cout << ": " << Mpd.GetErrorMessage() << std::endl; + std::cout << ": " << e.what() << std::endl; return false; } return true; @@ -341,13 +350,13 @@ void MouseEvent::run() && m_mouse_event.y == LINES-(Config.statusbar_visibility ? 2 : 1) ) // progressbar { - if (!Mpd.isPlaying()) + if (MpdStatus.playerState() == MPD::psStop) return; - Mpd.Seek(Mpd.GetTotalTime()*m_mouse_event.x/double(COLS)); + Mpd.Seek(MpdStatus.totalTime()*m_mouse_event.x/double(COLS)); } else if (m_mouse_event.bstate & BUTTON1_PRESSED && (Config.statusbar_visibility || Config.new_design) - && Mpd.isPlaying() + && MpdStatus.playerState() != MPD::psStop && m_mouse_event.y == (Config.new_design ? 1 : LINES-1) && m_mouse_event.x < 9 ) // playing/paused { @@ -359,9 +368,9 @@ void MouseEvent::run() ) // volume { if (m_mouse_event.bstate & BUTTON2_PRESSED) - Mpd.SetVolume(Mpd.GetVolume()-2); + Mpd.SetVolume(MpdStatus.volume()-2); else - Mpd.SetVolume(Mpd.GetVolume()+2); + Mpd.SetVolume(MpdStatus.volume()+2); } else if (m_mouse_event.bstate & (BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED | BUTTON4_PRESSED)) myScreen->mouseButtonPressed(m_mouse_event); @@ -503,8 +512,8 @@ void ToggleInterface::run() Progressbar::unlock(); Statusbar::unlock(); resizeScreen(false); - if (!Mpd.isPlaying()) - Status::Changes::mixer(); + Status::Changes::mixer(); + Status::Changes::elapsedTime(); Statusbar::msg("User interface: %s", Config.new_design ? "Alternative" : "Classic"); } @@ -615,13 +624,13 @@ void SlaveScreen::run() void VolumeUp::run() { - int volume = std::min(Mpd.GetVolume()+Config.volume_change_step, 100); + int volume = std::min(MpdStatus.volume()+Config.volume_change_step, 100); Mpd.SetVolume(volume); } void VolumeDown::run() { - int volume = std::max(Mpd.GetVolume()-Config.volume_change_step, 0); + int volume = std::max(MpdStatus.volume()-Config.volume_change_step, 0); Mpd.SetVolume(volume); } @@ -637,16 +646,16 @@ void DeletePlaylistItems::run() { Statusbar::msg("Deleting items..."); auto delete_fun = std::bind(&MPD::Connection::Delete, _1, _2); - if (deleteSelectedSongs(myPlaylist->main(), delete_fun)) - Statusbar::msg("Item(s) deleted"); + deleteSelectedSongs(myPlaylist->main(), delete_fun); + Statusbar::msg("Item(s) deleted"); } else if (myScreen->isActiveWindow(myPlaylistEditor->Content)) { std::string playlist = myPlaylistEditor->Playlists.current().value(); auto delete_fun = std::bind(&MPD::Connection::PlaylistDelete, _1, playlist, _2); Statusbar::msg("Deleting items..."); - if (deleteSelectedSongs(myPlaylistEditor->Content, delete_fun)) - Statusbar::msg("Item(s) deleted"); + deleteSelectedSongs(myPlaylistEditor->Content, delete_fun); + Statusbar::msg("Item(s) deleted"); } } @@ -730,8 +739,8 @@ void DeleteStoredPlaylist::run() Mpd.StartCommandsList(); for (auto it = list.begin(); it != list.end(); ++it) Mpd.DeletePlaylist((*it)->value()); - if (Mpd.CommitCommandsList()) - Statusbar::msg("Playlist%s deleted", list.size() == 1 ? "" : "s"); + Mpd.CommitCommandsList(); + Statusbar::msg("Playlist%s deleted", list.size() == 1 ? "" : "s"); } else Statusbar::msg("Aborted"); @@ -739,7 +748,7 @@ void DeleteStoredPlaylist::run() void ReplaySong::run() { - if (Mpd.isPlaying()) + if (MpdStatus.playerState() != MPD::psStop) Mpd.Seek(0); } @@ -779,29 +788,33 @@ void SavePlaylist::run() for (size_t i = 0; i < myPlaylist->main().size(); ++i) Mpd.AddToPlaylist(playlist_name, myPlaylist->main()[i].value()); Mpd.CommitCommandsList(); - if (Mpd.GetErrorMessage().empty()) - Statusbar::msg("Filtered items added to playlist \"%s\"", playlist_name.c_str()); + Statusbar::msg("Filtered items added to playlist \"%s\"", playlist_name.c_str()); } else { - int result = Mpd.SavePlaylist(playlist_name); - if (result == MPD_ERROR_SUCCESS) + try { + Mpd.SavePlaylist(playlist_name); Statusbar::msg("Playlist saved as \"%s\"", playlist_name.c_str()); } - else if (result == MPD_SERVER_ERROR_EXIST) + catch (MPD::ServerError &e) { - bool yes = askYesNoQuestion("Playlist \"" + playlist_name + "\" already exists, overwrite?", Status::trace); - if (yes) + if (e.code() == MPD_SERVER_ERROR_EXIST) { - Mpd.DeletePlaylist(playlist_name); - if (Mpd.SavePlaylist(playlist_name) == MPD_ERROR_SUCCESS) + bool yes = askYesNoQuestion("Playlist \"" + playlist_name + "\" already exists, overwrite?", Status::trace); + if (yes) + { + Mpd.DeletePlaylist(playlist_name); + Mpd.SavePlaylist(playlist_name); Statusbar::msg("Playlist overwritten"); + } + else + Statusbar::msg("Aborted"); + if (myScreen == myPlaylist) + myPlaylist->EnableHighlighting(); } else - Statusbar::msg("Aborted"); - if (myScreen == myPlaylist) - myPlaylist->EnableHighlighting(); + throw e; } } } @@ -964,7 +977,7 @@ void Add::run() bool SeekForward::canBeRun() const { - return Mpd.isPlaying() && Mpd.GetTotalTime() > 0; + return MpdStatus.playerState() != MPD::psStop && MpdStatus.totalTime() > 0; } void SeekForward::run() @@ -974,7 +987,7 @@ void SeekForward::run() bool SeekBackward::canBeRun() const { - return Mpd.isPlaying() && Mpd.GetTotalTime() > 0; + return MpdStatus.playerState() != MPD::psStop && MpdStatus.totalTime() > 0; } void SeekBackward::run() @@ -1079,8 +1092,10 @@ void TogglePlayingSongCentering::run() { Config.autocenter_mode = !Config.autocenter_mode; Statusbar::msg("Centering playing song: %s", Config.autocenter_mode ? "On" : "Off"); - if (Config.autocenter_mode && Mpd.isPlaying() && !myPlaylist->main().isFiltered()) - myPlaylist->main().highlight(Mpd.GetCurrentSongPos()); + if (Config.autocenter_mode + && MpdStatus.playerState() != MPD::psStop + && !myPlaylist->main().isFiltered()) + myPlaylist->main().highlight(MpdStatus.currentSongPosition()); } void UpdateDatabase::run() @@ -1100,13 +1115,13 @@ bool JumpToPlayingSong::canBeRun() const return ((myScreen == myPlaylist && !myPlaylist->isFiltered()) || myScreen == myBrowser || myScreen == myLibrary) - && Mpd.isPlaying(); + && MpdStatus.playerState() != MPD::psStop; } void JumpToPlayingSong::run() { if (myScreen == myPlaylist) - myPlaylist->main().highlight(Mpd.GetCurrentSongPos()); + myPlaylist->main().highlight(MpdStatus.currentSongPosition()); else if (myScreen == myBrowser) { myBrowser->LocateSong(myPlaylist->nowPlayingSong()); @@ -1120,7 +1135,7 @@ void JumpToPlayingSong::run() void ToggleRepeat::run() { - Mpd.SetRepeat(!Mpd.GetRepeat()); + Mpd.SetRepeat(!MpdStatus.repeat()); } void Shuffle::run() @@ -1130,7 +1145,7 @@ void Shuffle::run() void ToggleRandom::run() { - Mpd.SetRandom(!Mpd.GetRandom()); + Mpd.SetRandom(!MpdStatus.random()); } bool StartSearching::canBeRun() const @@ -1175,17 +1190,17 @@ void SaveTagChanges::run() void ToggleSingle::run() { - Mpd.SetSingle(!Mpd.GetSingle()); + Mpd.SetSingle(!MpdStatus.single()); } void ToggleConsume::run() { - Mpd.SetConsume(!Mpd.GetConsume()); + Mpd.SetConsume(!MpdStatus.consume()); } void ToggleCrossfade::run() { - Mpd.SetCrossfade(Mpd.GetCrossfade() ? 0 : Config.crossfade_time); + Mpd.SetCrossfade(MpdStatus.crossfade() ? 0 : Config.crossfade_time); } void SetCrossfade::run() @@ -1215,8 +1230,8 @@ void SetVolume::run() int volume = boost::lexical_cast(strvolume); if (volume >= 0 && volume <= 100) { - if (Mpd.SetVolume(volume)) - Statusbar::msg("Volume set to %d%%", volume); + Mpd.SetVolume(volume); + Statusbar::msg("Volume set to %d%%", volume); } } @@ -1451,13 +1466,11 @@ void EditPlaylistName::run() Statusbar::unlock(); if (!new_name.empty() && new_name != old_name) { - if (Mpd.Rename(old_name, new_name)) - { - const char msg[] = "Playlist renamed to \"%ls\""; - Statusbar::msg(msg, wideShorten(ToWString(new_name), COLS-const_strlen(msg)).c_str()); - if (!myBrowser->isLocal()) - myBrowser->GetDirectory("/"); - } + Mpd.Rename(old_name, new_name); + const char msg[] = "Playlist renamed to \"%ls\""; + Statusbar::msg(msg, wideShorten(ToWString(new_name), COLS-const_strlen(msg)).c_str()); + if (!myBrowser->isLocal()) + myBrowser->GetDirectory("/"); } } @@ -1562,7 +1575,7 @@ void JumpToTagEditor::run() bool JumpToPositionInSong::canBeRun() const { - return Mpd.isPlaying() && Mpd.GetTotalTime() > 0; + return MpdStatus.playerState() != MPD::psStop && MpdStatus.totalTime() > 0; } void JumpToPositionInSong::run() @@ -1579,12 +1592,12 @@ void JumpToPositionInSong::run() if (position.empty()) return; - int newpos = 0; + unsigned newpos = 0; if (position.find(':') != std::string::npos) // probably time in mm:ss { newpos = boost::lexical_cast(position)*60 + boost::lexical_cast(position.substr(position.find(':')+1)); - if (newpos >= 0 && newpos <= Mpd.GetTotalTime()) + if (newpos <= MpdStatus.totalTime()) Mpd.Seek(newpos); else Statusbar::msg("Out of bounds, 0:00-%s possible for mm:ss, %s given", s.getLength().c_str(), MPD::Song::ShowTime(newpos).c_str()); @@ -1592,7 +1605,7 @@ void JumpToPositionInSong::run() else if (position.find('s') != std::string::npos) // probably position in seconds { newpos = boost::lexical_cast(position); - if (newpos >= 0 && newpos <= Mpd.GetTotalTime()) + if (newpos <= MpdStatus.totalTime()) Mpd.Seek(newpos); else Statusbar::msg("Out of bounds, 0-%d possible for seconds, %d given", s.getDuration(), newpos); @@ -1600,8 +1613,8 @@ void JumpToPositionInSong::run() else { newpos = boost::lexical_cast(position); - if (newpos >= 0 && newpos <= 100) - Mpd.Seek(Mpd.GetTotalTime()*newpos/100.0); + if (newpos <= 100) + Mpd.Seek(MpdStatus.totalTime()*newpos/100.0); else Statusbar::msg("Out of bounds, 0-100 possible for %%, %d given", newpos); } @@ -1689,8 +1702,8 @@ void CropMainPlaylist::run() if (yes) { Statusbar::msg("Cropping playlist..."); - if (cropPlaylist(myPlaylist->main(), std::bind(&MPD::Connection::Delete, _1, _2))) - Statusbar::msg("Cropping playlist..."); + cropPlaylist(myPlaylist->main(), std::bind(&MPD::Connection::Delete, _1, _2)); + Statusbar::msg("Cropping playlist..."); } } @@ -1710,8 +1723,8 @@ void CropPlaylist::run() { auto delete_fun = std::bind(&MPD::Connection::PlaylistDelete, _1, playlist, _2); Statusbar::msg("Cropping playlist \"%s\"...", playlist.c_str()); - if (cropPlaylist(myPlaylistEditor->Content, delete_fun)) - Statusbar::msg("Playlist \"%s\" cropped", playlist.c_str()); + cropPlaylist(myPlaylistEditor->Content, delete_fun); + Statusbar::msg("Playlist \"%s\" cropped", playlist.c_str()); } } @@ -1725,8 +1738,8 @@ void ClearMainPlaylist::run() auto delete_fun = std::bind(&MPD::Connection::Delete, _1, _2); auto clear_fun = std::bind(&MPD::Connection::ClearMainPlaylist, _1); Statusbar::msg("Deleting items..."); - if (clearPlaylist(myPlaylist->main(), delete_fun, clear_fun)) - Statusbar::msg("Items deleted"); + clearPlaylist(myPlaylist->main(), delete_fun, clear_fun); + Statusbar::msg("Items deleted"); } } @@ -1747,8 +1760,8 @@ void ClearPlaylist::run() auto delete_fun = std::bind(&MPD::Connection::PlaylistDelete, _1, playlist, _2); auto clear_fun = std::bind(&MPD::Connection::ClearPlaylist, _1, playlist); Statusbar::msg("Deleting items from \"%s\"...", playlist.c_str()); - if (clearPlaylist(myPlaylistEditor->Content, delete_fun, clear_fun)) - Statusbar::msg("Items deleted from \"%s\"", playlist.c_str()); + clearPlaylist(myPlaylistEditor->Content, delete_fun, clear_fun); + Statusbar::msg("Items deleted from \"%s\"", playlist.c_str()); } } @@ -2578,7 +2591,7 @@ void seek() using Global::Timer; using Global::SeekingInProgress; - if (!Mpd.GetTotalTime()) + if (!MpdStatus.totalTime()) { Statusbar::msg("Unknown item length"); return; @@ -2587,7 +2600,7 @@ void seek() Progressbar::lock(); Statusbar::lock(); - int songpos = Mpd.GetElapsedTime(); + unsigned songpos = MpdStatus.elapsedTime(); timeval t = Timer; int old_timeout = wFooter->getTimeout(); @@ -2602,7 +2615,7 @@ void seek() Status::trace(); myPlaylist->UpdateTimer(); - int howmuch = Config.incremental_seeking ? (Timer.tv_sec-t.tv_sec)/2+Config.seek_time : Config.seek_time; + unsigned howmuch = Config.incremental_seeking ? (Timer.tv_sec-t.tv_sec)/2+Config.seek_time : Config.seek_time; Key input = Key::read(*wFooter); auto k = Bindings.get(input); @@ -2611,20 +2624,17 @@ void seek() auto a = k.first->action(); if (a == seekForward) { - if (songpos < Mpd.GetTotalTime()) - { - songpos += howmuch; - if (songpos > Mpd.GetTotalTime()) - songpos = Mpd.GetTotalTime(); - } + if (songpos < MpdStatus.totalTime()) + songpos = std::min(songpos + howmuch, MpdStatus.totalTime()); } else if (a == seekBackward) { if (songpos > 0) { - songpos -= howmuch; - if (songpos < 0) + if (songpos < howmuch) songpos = 0; + else + songpos -= howmuch; } } else @@ -2637,12 +2647,12 @@ void seek() if (Config.display_remaining_time) { tracklength = "-"; - tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime()-songpos); + tracklength += MPD::Song::ShowTime(MpdStatus.totalTime()-songpos); } else tracklength = MPD::Song::ShowTime(songpos); tracklength += "/"; - tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime()); + tracklength += MPD::Song::ShowTime(MpdStatus.totalTime()); *wHeader << NC::XY(0, 0) << tracklength << " "; wHeader->refresh(); } @@ -2652,17 +2662,17 @@ void seek() if (Config.display_remaining_time) { tracklength += "-"; - tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime()-songpos); + tracklength += MPD::Song::ShowTime(MpdStatus.totalTime()-songpos); } else tracklength += MPD::Song::ShowTime(songpos); tracklength += "/"; - tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime()); + tracklength += MPD::Song::ShowTime(MpdStatus.totalTime()); tracklength += "]"; *wFooter << NC::XY(wFooter->getWidth()-tracklength.length(), 1) << tracklength; } *wFooter << NC::Format::NoBold; - Progressbar::draw(songpos, Mpd.GetTotalTime()); + Progressbar::draw(songpos, MpdStatus.totalTime()); wFooter->refresh(); } SeekingInProgress = false; diff --git a/src/browser.cpp b/src/browser.cpp index 17e8a7b..934d17b 100644 --- a/src/browser.cpp +++ b/src/browser.cpp @@ -74,9 +74,6 @@ Browser::Browser() : itsBrowseLocally(0), itsScrollBeginning(0), itsBrowsedDir(" w.setSelectedPrefix(Config.selected_item_prefix); w.setSelectedSuffix(Config.selected_item_suffix); w.setItemDisplayer(std::bind(Display::Items, _1, proxySongList())); - - if (SupportedExtensions.empty()) - Mpd.GetSupportedExtensions(SupportedExtensions); } void Browser::resize() @@ -136,11 +133,9 @@ void Browser::enterPressed() } case itPlaylist: { - if (Mpd.LoadPlaylist(item.name)) - { - Statusbar::msg("Playlist \"%s\" loaded", item.name.c_str()); - myPlaylist->PlayNewlyAddedSongs(); - } + Mpd.LoadPlaylist(item.name); + Statusbar::msg("Playlist \"%s\" loaded", item.name.c_str()); + myPlaylist->PlayNewlyAddedSongs(); } } } @@ -168,7 +163,6 @@ void Browser::spacePressed() { case itDirectory: { - bool result; # ifndef WIN32 if (isLocal()) { @@ -179,13 +173,12 @@ void Browser::spacePressed() list.reserve(items.size()); for (MPD::ItemList::const_iterator it = items.begin(); it != items.end(); ++it) list.push_back(*it->song); - result = addSongsToPlaylist(list, false, -1); + addSongsToPlaylist(list, false, -1); } else # endif // !WIN32 - result = Mpd.Add(item.name); - if (result) - Statusbar::msg("Directory \"%s\" added", item.name.c_str()); + Mpd.Add(item.name); + Statusbar::msg("Directory \"%s\" added", item.name.c_str()); break; } case itSong: @@ -195,8 +188,8 @@ void Browser::spacePressed() } case itPlaylist: { - if (Mpd.LoadPlaylist(item.name)) - Statusbar::msg("Playlist \"%s\" loaded", item.name.c_str()); + Mpd.LoadPlaylist(item.name); + Statusbar::msg("Playlist \"%s\" loaded", item.name.c_str()); break; } } @@ -363,6 +356,12 @@ MPD::SongList Browser::getSelectedSongs() return result; } +void Browser::fetchSupportedExtensions() +{ + SupportedExtensions.clear(); + Mpd.GetSupportedExtensions(SupportedExtensions); +} + void Browser::LocateSong(const MPD::Song &s) { if (s.getDirectory().empty()) @@ -580,7 +579,7 @@ bool Browser::deleteItem(const MPD::Item &item) // playlist created by mpd if (!isLocal() && item.type == itPlaylist && CurrentDir() == "/") - return Mpd.DeletePlaylist(item.name); + Mpd.DeletePlaylist(item.name); std::string path; if (!isLocal()) diff --git a/src/browser.h b/src/browser.h index 9b39edd..74b04b5 100644 --- a/src/browser.h +++ b/src/browser.h @@ -65,6 +65,8 @@ struct Browser: Screen>, Filterable, HasSongs, Searchable, T // private members const std::string &CurrentDir() { return itsBrowsedDir; } + void fetchSupportedExtensions(); + bool isLocal() { return itsBrowseLocally; } void LocateSong(const MPD::Song &); void GetDirectory(std::string, std::string = "/"); diff --git a/src/cmdargs.cpp b/src/cmdargs.cpp index f9145df..395ea80 100644 --- a/src/cmdargs.cpp +++ b/src/cmdargs.cpp @@ -117,13 +117,11 @@ void ParseArgv(int argc, char **argv) << " -s, --screen specify the startup screen\n" << " -?, --help show help message\n" << " -v, --version display version information\n" - << " --now-playing display now playing song [" << now_playing_format << "]\n" ; exit(0); } - if (!Actions::connectToMPD()) - exit(1); + Actions::connectToMPD(); if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--screen")) { @@ -164,34 +162,6 @@ void ParseArgv(int argc, char **argv) exit(1); } } - else if (!strcmp(argv[i], "--now-playing")) - { - Mpd.UpdateStatus(); - if (!Mpd.GetErrorMessage().empty()) - { - std::cerr << "MPD error: " << Mpd.GetErrorMessage() << std::endl; - exit(1); - } - if (Mpd.isPlaying()) - { - if (argc > ++i) - { - if (MPD::Song::isFormatOk("now-playing format", argv[i])) - { - // apply additional pair of braces - now_playing_format = "{"; - now_playing_format += argv[i]; - now_playing_format += "}"; - boost::replace_all(now_playing_format, "\\n", "\n"); - boost::replace_all(now_playing_format, "\\t", "\t"); - } - } - std::string np = Mpd.GetCurrentlyPlayingSong().toString( - now_playing_format, Config.tags_separator); - std::cout << Charset::utf8ToLocale(np) << "\n"; - } - exit(0); - } else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--config")) { // this is used in Configuration::CheckForCommandLineConfigFilePath, ignoring here. @@ -202,10 +172,5 @@ void ParseArgv(int argc, char **argv) std::cerr << "Invalid option: " << argv[i] << std::endl; exit(1); } - if (!Mpd.GetErrorMessage().empty()) - { - std::cerr << "Error: " << Mpd.GetErrorMessage() << std::endl; - exit(1); - } } } diff --git a/src/display.cpp b/src/display.cpp index 3c273e5..92dc968 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -97,8 +97,8 @@ void setProperties(NC::Menu &menu, const MPD::Song &s, const ProxySongList &p discard_colors = Config.discard_colors_if_item_is_selected && is_selected; int song_pos = menu.isFiltered() ? s.getPosition() : drawn_pos; - is_now_playing = Mpd.isPlaying() && myPlaylist->isActiveWindow(menu) - && song_pos == Mpd.GetCurrentSongPos(); + is_now_playing = MpdStatus.playerState() != MPD::psStop && myPlaylist->isActiveWindow(menu) + && song_pos == MpdStatus.currentSongPosition(); if (is_now_playing) menu << Config.now_playing_prefix; } diff --git a/src/error.h b/src/error.h index 746b495..85849c6 100644 --- a/src/error.h +++ b/src/error.h @@ -24,8 +24,6 @@ #include #include "gcc.h" -#define Error(msg) std::cerr << "ncmpcpp: " << msg; - void FatalError(const std::string &msg) GNUC_NORETURN; #endif // NCMPCPP_ERROR_H diff --git a/src/global.h b/src/global.h index 3fa1255..a5c6b28 100644 --- a/src/global.h +++ b/src/global.h @@ -49,9 +49,6 @@ extern size_t MainStartY; // height of main window extern size_t MainHeight; -// indicates whether messages from Statusbar::msg function should be shown -extern bool ShowMessages; - // indicates whether seeking action in currently in progress extern bool SeekingInProgress; diff --git a/src/helpers.cpp b/src/helpers.cpp index bb9df58..6e4b8b7 100644 --- a/src/helpers.cpp +++ b/src/helpers.cpp @@ -49,7 +49,7 @@ bool addSongToPlaylist(const MPD::Song &s, bool play, size_t position) } else { - position = std::min(position, Mpd.GetPlaylistLength()); + position = std::min(position, myPlaylist->main().size()); int id = Mpd.AddSong(s, position); if (id >= 0) { @@ -64,18 +64,18 @@ bool addSongToPlaylist(const MPD::Song &s, bool play, size_t position) return result; } -bool addSongsToPlaylist(const MPD::SongList &list, bool play, size_t position) +void addSongsToPlaylist(const MPD::SongList &list, bool play, size_t position) { if (list.empty()) - return false; - position = std::min(position, Mpd.GetPlaylistLength()); + return; + position = std::min(position, myPlaylist->main().size()); Mpd.StartCommandsList(); for (auto s = list.rbegin(); s != list.rend(); ++s) if (Mpd.AddSong(*s, position) < 0) break; if (play) Mpd.Play(position); - return Mpd.CommitCommandsList(); + Mpd.CommitCommandsList(); } std::string Timestamp(time_t t) diff --git a/src/helpers.h b/src/helpers.h index 6d7b5b0..58e1708 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -152,24 +152,22 @@ void moveSelectedItemsUp(NC::Menu &m, F swap_fun) Mpd.StartCommandsList(); for (auto it = list.begin(); it != list.end(); ++it) swap_fun(Mpd, *it - begin, *it - begin - 1); - if (Mpd.CommitCommandsList()) + Mpd.CommitCommandsList(); + if (list.size() > 1) { - if (list.size() > 1) + for (auto it = list.begin(); it != list.end(); ++it) { - for (auto it = list.begin(); it != list.end(); ++it) - { - (*it)->setSelected(false); - (*it-1)->setSelected(true); - } - m.highlight(list[(list.size())/2] - begin - 1); - } - else - { - // if we move only one item, do not select it. however, if single item - // was selected prior to move, it'll deselect it. oh well. - list[0]->setSelected(false); - m.scroll(NC::Scroll::Up); + (*it)->setSelected(false); + (*it-1)->setSelected(true); } + m.highlight(list[(list.size())/2] - begin - 1); + } + else + { + // if we move only one item, do not select it. however, if single item + // was selected prior to move, it'll deselect it. oh well. + list[0]->setSelected(false); + m.scroll(NC::Scroll::Up); } } } @@ -186,24 +184,22 @@ void moveSelectedItemsDown(NC::Menu &m, F swap_fun) Mpd.StartCommandsList(); for (auto it = list.begin(); it != list.end(); ++it) swap_fun(Mpd, it->base() - begin, it->base() - begin + 1); - if (Mpd.CommitCommandsList()) + Mpd.CommitCommandsList(); + if (list.size() > 1) { - if (list.size() > 1) - { - for (auto it = list.begin(); it != list.end(); ++it) - { - (*it)->setSelected(false); - (*it-1)->setSelected(true); - } - m.highlight(list[(list.size())/2].base() - begin + 1); - } - else + for (auto it = list.begin(); it != list.end(); ++it) { - // if we move only one item, do not select it. however, if single item - // was selected prior to move, it'll deselect it. oh well. - list[0]->setSelected(false); - m.scroll(NC::Scroll::Down); + (*it)->setSelected(false); + (*it-1)->setSelected(true); } + m.highlight(list[(list.size())/2].base() - begin + 1); + } + else + { + // if we move only one item, do not select it. however, if single item + // was selected prior to move, it'll deselect it. oh well. + list[0]->setSelected(false); + m.scroll(NC::Scroll::Down); } } } @@ -236,14 +232,12 @@ void moveSelectedItemsTo(NC::Menu &m, F move_fun) size_t i = list.size()-1; for (auto it = list.rbegin(); it != list.rend(); ++it, --i) move_fun(Mpd, *it - begin, pos+i); - if (Mpd.CommitCommandsList()) + Mpd.CommitCommandsList(); + i = list.size()-1; + for (auto it = list.rbegin(); it != list.rend(); ++it, --i) { - i = list.size()-1; - for (auto it = list.rbegin(); it != list.rend(); ++it, --i) - { - (*it)->setSelected(false); - m[pos+i].setSelected(true); - } + (*it)->setSelected(false); + m[pos+i].setSelected(true); } } else if (diff < 0) // move up @@ -251,23 +245,20 @@ void moveSelectedItemsTo(NC::Menu &m, F move_fun) size_t i = 0; for (auto it = list.begin(); it != list.end(); ++it, ++i) move_fun(Mpd, *it - begin, pos+i); - if (Mpd.CommitCommandsList()) + Mpd.CommitCommandsList(); + i = 0; + for (auto it = list.begin(); it != list.end(); ++it, ++i) { - i = 0; - for (auto it = list.begin(); it != list.end(); ++it, ++i) - { - (*it)->setSelected(false); - m[pos+i].setSelected(true); - } + (*it)->setSelected(false); + m[pos+i].setSelected(true); } } }); } template -bool deleteSelectedSongs(NC::Menu &m, F delete_fun) +void deleteSelectedSongs(NC::Menu &m, F delete_fun) { - bool result = false; selectCurrentIfNoneSelected(m); // ok, this is tricky. we need to operate on whole playlist // to get positions right, but at the same time we need to @@ -298,31 +289,27 @@ bool deleteSelectedSongs(NC::Menu &m, F delete_fun) ++cur_filtered; } } - if (Mpd.CommitCommandsList()) - result = true; - return result; + Mpd.CommitCommandsList(); } template -bool cropPlaylist(NC::Menu &m, F delete_fun) +void cropPlaylist(NC::Menu &m, F delete_fun) { reverseSelectionHelper(m.begin(), m.end()); - return deleteSelectedSongs(m, delete_fun); + deleteSelectedSongs(m, delete_fun); } template -bool clearPlaylist(NC::Menu &m, F delete_fun, G clear_fun) +void clearPlaylist(NC::Menu &m, F delete_fun, G clear_fun) { - bool result = false; if (m.isFiltered()) { for (auto it = m.begin(); it != m.end(); ++it) it->setSelected(true); - result = deleteSelectedSongs(m, delete_fun); + deleteSelectedSongs(m, delete_fun); } else - result = clear_fun(Mpd); - return result; + clear_fun(Mpd); } template std::string getSharedDirectory(Iterator first, Iterator last) @@ -471,7 +458,7 @@ template void ShowTag(BufferT &buf, const std::string &tag) } bool addSongToPlaylist(const MPD::Song &s, bool play, size_t position = -1); -bool addSongsToPlaylist(const MPD::SongList &list, bool play, size_t position = -1); +void addSongsToPlaylist(const MPD::SongList &list, bool play, size_t position = -1); std::string Timestamp(time_t t); diff --git a/src/lastfm.cpp b/src/lastfm.cpp index d632c6a..9279fa3 100644 --- a/src/lastfm.cpp +++ b/src/lastfm.cpp @@ -191,7 +191,7 @@ void Lastfm::Save(const std::string &data) output.close(); } else - Error("couldn't save file \"" << itsFilename << "\""); + std::cerr << "ncmpcpp: couldn't save file \"" << itsFilename << "\"\n"; } void Lastfm::Refetch() diff --git a/src/media_library.cpp b/src/media_library.cpp index 4d7ea8f..46c5274 100644 --- a/src/media_library.cpp +++ b/src/media_library.cpp @@ -249,8 +249,6 @@ std::wstring MediaLibrary::title() void MediaLibrary::update() { - Mpd.BlockIdle(true); - if (hasTwoColumns) { if (Albums.reallyEmpty() || m_albums_update_request) @@ -407,8 +405,6 @@ void MediaLibrary::update() }); Songs.refresh(); } - - Mpd.BlockIdle(false); } void MediaLibrary::enterPressed() @@ -969,22 +965,20 @@ void MediaLibrary::AddToPlaylist(bool add_n_play) addSongToPlaylist(Songs.current().value(), add_n_play); else { - auto list = getSelectedSongs(); - if (addSongsToPlaylist(list, add_n_play)) + addSongsToPlaylist(getSelectedSongs(), add_n_play); + if ((!Tags.empty() && isActiveWindow(Tags)) + || (isActiveWindow(Albums) && Albums.current().value().isAllTracksEntry())) { - if ((!Tags.empty() && isActiveWindow(Tags)) - || (isActiveWindow(Albums) && Albums.current().value().isAllTracksEntry())) - { - std::string tag_type = boost::locale::to_lower( - tagTypeToString(Config.media_lib_primary_tag)); - Statusbar::msg("Songs with %s = \"%s\" added", tag_type.c_str(), Tags.current().value().tag().c_str()); - } - else if (isActiveWindow(Albums)) - Statusbar::msg("Songs from album \"%s\" added", - Albums.current().value().entry().album().c_str()); + std::string tag_type = boost::locale::to_lower( + tagTypeToString(Config.media_lib_primary_tag)); + Statusbar::msg("Songs with %s = \"%s\" added", + tag_type.c_str(), Tags.current().value().tag().c_str()); } + else if (isActiveWindow(Albums)) + Statusbar::msg("Songs from album \"%s\" added", + Albums.current().value().entry().album().c_str()); } - + if (!add_n_play) { w->scroll(NC::Scroll::Down); diff --git a/src/mpdpp.cpp b/src/mpdpp.cpp dissimilarity index 72% index 80dd66d..1705500 100644 --- a/src/mpdpp.cpp +++ b/src/mpdpp.cpp @@ -1,1390 +1,994 @@ -/*************************************************************************** - * Copyright (C) 2008-2012 by Andrzej Rybczak * - * electricityispower@gmail.com * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * - ***************************************************************************/ - -#include -#include -#include -#include - -#include "charset.h" -#include "error.h" -#include "mpdpp.h" - -MPD::Connection Mpd; - -namespace MPD {// - -bool Statistics::empty() const -{ - return m_stats.get() == 0; -} - -unsigned Statistics::artists() const -{ - assert(!empty()); - return mpd_stats_get_number_of_artists(m_stats.get()); -} - -unsigned Statistics::albums() const -{ - assert(!empty()); - return mpd_stats_get_number_of_albums(m_stats.get()); -} - -unsigned Statistics::songs() const -{ - assert(!empty()); - return mpd_stats_get_number_of_songs(m_stats.get()); -} - -unsigned long Statistics::playTime() const -{ - assert(!empty()); - return mpd_stats_get_play_time(m_stats.get()); -} - -unsigned long Statistics::uptime() const -{ - assert(!empty()); - return mpd_stats_get_uptime(m_stats.get()); -} - -unsigned long Statistics::dbUpdateTime() const -{ - assert(!empty()); - return mpd_stats_get_db_update_time(m_stats.get()); -} - -unsigned long Statistics::dbPlayTime() const -{ - assert(!empty()); - return mpd_stats_get_db_play_time(m_stats.get()); -} - -Connection::Connection() : itsConnection(0), - isCommandsListEnabled(0), - isIdle(0), - supportsIdle(0), - itsHost("localhost"), - itsPort(6600), - itsTimeout(15), - itsCurrentStatus(0), - itsOldStatus(0), - itsUpdater(0), - itsErrorHandler(0) -{ -} - -Connection::~Connection() -{ - if (itsConnection) - mpd_connection_free(itsConnection); - if (itsOldStatus) - mpd_status_free(itsOldStatus); - if (itsCurrentStatus) - mpd_status_free(itsCurrentStatus); -} - -bool Connection::Connect() -{ - if (itsConnection) - return true; - itsConnection = mpd_connection_new(itsHost.c_str(), itsPort, itsTimeout*1000 /* timeout is in ms now */); - if (CheckForErrors()) - return false; - if (!itsPassword.empty()) - SendPassword(); - itsFD = mpd_connection_get_fd(itsConnection); - supportsIdle = isIdleEnabled && Version() > 13; - // in UpdateStatus() we compare it to itsElapsedTimer[0], - // and for the first time it has always evaluate to true - // so we need it to be zero at this point - itsElapsedTimer[1] = 0; - return !CheckForErrors(); -} - -bool Connection::Connected() const -{ - return itsConnection; -} - -void Connection::Disconnect() -{ - if (itsConnection) - mpd_connection_free(itsConnection); - if (itsOldStatus) - mpd_status_free(itsOldStatus); - if (itsCurrentStatus) - mpd_status_free(itsCurrentStatus); - itsConnection = 0; - isIdle = 0; - itsCurrentStatus = 0; - itsOldStatus = 0; - isCommandsListEnabled = 0; -} - -unsigned Connection::Version() const -{ - return itsConnection ? mpd_connection_get_server_version(itsConnection)[1] : 0; -} - -void Connection::SetHostname(const std::string &host) -{ - size_t at = host.find("@"); - if (at != std::string::npos) - { - itsPassword = host.substr(0, at); - itsHost = host.substr(at+1); - } - else - itsHost = host; -} - -bool Connection::SendPassword() -{ - assert(itsConnection); - GoBusy(); - assert(!isCommandsListEnabled); - mpd_run_password(itsConnection, itsPassword.c_str()); - return !CheckForErrors(); -} - -void Connection::SetStatusUpdater(StatusUpdater updater, void *data) -{ - itsUpdater = updater; - itsStatusUpdaterUserdata = data; -} - -void Connection::SetErrorHandler(ErrorHandler handler, void *data) -{ - itsErrorHandler = handler; - itsErrorHandlerUserdata = data; -} - -void Connection::GoIdle() -{ - if (supportsIdle && !itsIdleBlocked && !isIdle && mpd_send_idle(itsConnection)) - isIdle = 1; -} - -int Connection::GoBusy() -{ - int flags = 0; - if (isIdle && mpd_send_noidle(itsConnection)) - { - isIdle = 0; - if (hasData) - flags = mpd_recv_idle(itsConnection, 1); - mpd_response_finish(itsConnection); - } - return flags; -} - -Statistics Connection::getStatistics() -{ - assert(itsConnection); - GoBusy(); - mpd_stats *stats = mpd_run_stats(itsConnection); - return Statistics(stats); -} - -void Connection::UpdateStatus() -{ - if (!itsConnection) - return; - - int idle_mask = 0; - if (isIdle) - { - if (hasData) - { - idle_mask = GoBusy(); - hasData = 0; - } - else - { - // count local elapsed time as we don't receive - // this from mpd while being in idle mode - time(&itsElapsedTimer[1]); - double diff = difftime(itsElapsedTimer[1], itsElapsedTimer[0]); - if (diff >= 1.0 && Mpd.GetState() == psPlay) - { - time(&itsElapsedTimer[0]); - itsElapsed += diff; - StatusChanges changes; - changes.ElapsedTime = 1; - if (itsUpdater) - itsUpdater(this, changes, itsErrorHandlerUserdata); - } - return; - } - } - - // if CheckForErrors() invokes callback, it can do some communication with mpd. - // the problem is, we *have* to be out from idle mode here and issuing commands - // will enter it again, which certainly is not desired, so let's block it for - // a while. - BlockIdle(true); - CheckForErrors(); - BlockIdle(false); - - if (!itsConnection) - return; - - if (itsOldStatus) - mpd_status_free(itsOldStatus); - - itsOldStatus = itsCurrentStatus; - itsCurrentStatus = 0; - - itsCurrentStatus = mpd_run_status(itsConnection); - - if (CheckForErrors()) - return; - - if (itsCurrentStatus && itsUpdater) - { - if (supportsIdle) - { - // sync local elapsed time counter with mpd - unsigned old_elapsed = itsElapsed; - itsElapsed = mpd_status_get_elapsed_time(itsCurrentStatus); - itsChanges.ElapsedTime = itsElapsed != old_elapsed; - time(&itsElapsedTimer[0]); - } - else - itsElapsed = mpd_status_get_elapsed_time(itsCurrentStatus); - - if (!itsOldStatus) - { - itsChanges.Playlist = 1; - itsChanges.SongID = 1; - itsChanges.Database = 1; - itsChanges.DBUpdating = 1; - itsChanges.Volume = 1; - itsChanges.ElapsedTime = 1; - itsChanges.Crossfade = 1; - itsChanges.Random = 1; - itsChanges.Repeat = 1; - itsChanges.Single = 1; - itsChanges.Consume = 1; - itsChanges.PlayerState = 1; - itsChanges.StatusFlags = 1; - itsChanges.Outputs = 1; - } - else - { - if (idle_mask != 0) - { - itsChanges.Playlist = idle_mask & MPD_IDLE_QUEUE; - itsChanges.StoredPlaylists = idle_mask & MPD_IDLE_STORED_PLAYLIST; - itsChanges.Database = idle_mask & MPD_IDLE_DATABASE; - itsChanges.DBUpdating = idle_mask & MPD_IDLE_UPDATE; - itsChanges.Volume = idle_mask & MPD_IDLE_MIXER; - itsChanges.StatusFlags = idle_mask & (MPD_IDLE_OPTIONS | MPD_IDLE_UPDATE); - itsChanges.Outputs = idle_mask & MPD_IDLE_OUTPUT; - } - else - { - itsChanges.Playlist = mpd_status_get_queue_version(itsOldStatus) - != mpd_status_get_queue_version(itsCurrentStatus); - - itsChanges.ElapsedTime = mpd_status_get_elapsed_time(itsOldStatus) - != mpd_status_get_elapsed_time(itsCurrentStatus); - - itsChanges.Database = mpd_status_get_update_id(itsOldStatus) - && !mpd_status_get_update_id(itsCurrentStatus); - - itsChanges.DBUpdating = mpd_status_get_update_id(itsOldStatus) - != mpd_status_get_update_id(itsCurrentStatus); - - itsChanges.Volume = mpd_status_get_volume(itsOldStatus) - != mpd_status_get_volume(itsCurrentStatus); - - itsChanges.StatusFlags = itsChanges.Repeat - || itsChanges.Random - || itsChanges.Single - || itsChanges.Consume - || itsChanges.Crossfade - || itsChanges.DBUpdating; - - // there is no way to determine if the output has changed or not - // from mpd status, it's possible only with idle notifications - itsChanges.Outputs = 0; - } - - itsChanges.SongID = mpd_status_get_song_id(itsOldStatus) - != mpd_status_get_song_id(itsCurrentStatus); - - itsChanges.Crossfade = mpd_status_get_crossfade(itsOldStatus) - != mpd_status_get_crossfade(itsCurrentStatus); - - itsChanges.Random = mpd_status_get_random(itsOldStatus) - != mpd_status_get_random(itsCurrentStatus); - - itsChanges.Repeat = mpd_status_get_repeat(itsOldStatus) - != mpd_status_get_repeat(itsCurrentStatus); - - itsChanges.Single = mpd_status_get_single(itsOldStatus) - != mpd_status_get_single(itsCurrentStatus); - - itsChanges.Consume = mpd_status_get_consume(itsOldStatus) - != mpd_status_get_consume(itsCurrentStatus); - - itsChanges.PlayerState = mpd_status_get_state(itsOldStatus) - != mpd_status_get_state(itsCurrentStatus); - } - itsUpdater(this, itsChanges, itsErrorHandlerUserdata); - // status updater could invoke mpd commands that - // could fail se we need to check for errors - CheckForErrors(); - GoIdle(); - } -} - -bool Connection::UpdateDirectory(const std::string &path) -{ - if (!itsConnection) - return false; - if (!isCommandsListEnabled) - { - GoBusy(); - bool success = mpd_run_update(itsConnection, path.c_str()); - if (!supportsIdle && success) - UpdateStatus(); - return success; - } - else - { - assert(!isIdle); - return mpd_send_update(itsConnection, path.c_str()); - } - -} - -void Connection::Play() -{ - if (!itsConnection) - return; - if (!isCommandsListEnabled) - { - GoBusy(); - mpd_run_play(itsConnection); - } - else - { - assert(!isIdle); - mpd_send_play(itsConnection); - } -} - -void Connection::Play(int pos) -{ - if (!itsConnection) - return; - if (!isCommandsListEnabled) - { - GoBusy(); - mpd_run_play_pos(itsConnection, pos); - } - else - { - assert(!isIdle); - mpd_send_play_pos(itsConnection, pos); - } -} - -void Connection::PlayID(int id) -{ - if (!itsConnection) - return; - if (!isCommandsListEnabled) - { - GoBusy(); - mpd_run_play_id(itsConnection, id); - } - else - { - assert(!isIdle); - mpd_send_play_id(itsConnection, id); - } -} - -void Connection::Pause(bool state) -{ - if (!itsConnection) - return; - if (!isCommandsListEnabled) - { - GoBusy(); - mpd_run_pause(itsConnection, state); - } - else - { - assert(!isIdle); - mpd_send_pause(itsConnection, state); - } -} - -void Connection::Toggle() -{ - if (!itsConnection) - return; - if (!isCommandsListEnabled) - { - GoBusy(); - if (isPlaying()) - mpd_run_toggle_pause(itsConnection); - else - mpd_run_play(itsConnection); - } - else - { - assert(!isIdle); - if (isPlaying()) - mpd_send_toggle_pause(itsConnection); - else - mpd_send_play(itsConnection); - } -} - -void Connection::Stop() -{ - if (!itsConnection) - return; - if (!isCommandsListEnabled) - { - GoBusy(); - mpd_run_stop(itsConnection); - } - else - { - assert(!isIdle); - mpd_send_stop(itsConnection); - } -} - -void Connection::Next() -{ - if (!itsConnection) - return; - if (!isCommandsListEnabled) - { - GoBusy(); - mpd_run_next(itsConnection); - } - else - { - assert(!isIdle); - mpd_send_next(itsConnection); - } -} - -void Connection::Prev() -{ - if (!itsConnection) - return; - if (!isCommandsListEnabled) - { - GoBusy(); - mpd_run_previous(itsConnection); - } - else - { - assert(!isIdle); - mpd_send_previous(itsConnection); - } -} - -bool Connection::Move(unsigned from, unsigned to) -{ - if (!itsConnection) - return false; - if (!isCommandsListEnabled) - { - GoBusy(); - return mpd_run_move(itsConnection, from, to); - } - else - { - assert(!isIdle); - return mpd_send_move(itsConnection, from, to); - } -} - -void Connection::Swap(unsigned from, unsigned to) -{ - if (!itsConnection) - return; - if (!isCommandsListEnabled) - { - GoBusy(); - mpd_run_swap(itsConnection, from, to); - } - else - { - assert(!isIdle); - mpd_send_swap(itsConnection, from, to); - } -} - -void Connection::Seek(unsigned where) -{ - if (!itsConnection) - return; - if (!isCommandsListEnabled) - { - GoBusy(); - mpd_run_seek_pos(itsConnection, Mpd.GetCurrentSongPos(), where); - } - else - { - assert(!isIdle); - mpd_send_seek_pos(itsConnection, Mpd.GetCurrentSongPos(), where); - } -} - -void Connection::Shuffle() -{ - if (!itsConnection) - return; - if (!isCommandsListEnabled) - { - GoBusy(); - mpd_run_shuffle(itsConnection); - } - else - { - assert(!isIdle); - mpd_send_shuffle(itsConnection); - } -} - -bool Connection::ClearMainPlaylist() -{ - if (!itsConnection) - return false; - if (!isCommandsListEnabled) - { - GoBusy(); - return mpd_run_clear(itsConnection); - } - else - { - assert(!isIdle); - return mpd_send_clear(itsConnection); - } -} - -bool Connection::ClearPlaylist(const std::string &playlist) -{ - if (!itsConnection) - return false; - if (!isCommandsListEnabled) - { - GoBusy(); - return mpd_run_playlist_clear(itsConnection, playlist.c_str()); - } - else - { - return mpd_send_playlist_clear(itsConnection, playlist.c_str()); - assert(!isIdle); - } -} - -void Connection::AddToPlaylist(const std::string &path, const Song &s) -{ - AddToPlaylist(path, s.getURI()); -} - -void Connection::AddToPlaylist(const std::string &path, const std::string &file) -{ - if (!itsConnection) - return; - if (!isCommandsListEnabled) - { - GoBusy(); - mpd_run_playlist_add(itsConnection, path.c_str(), file.c_str()); - } - else - { - assert(!isIdle); - mpd_send_playlist_add(itsConnection, path.c_str(), file.c_str()); - } -} - -bool Connection::PlaylistMove(const std::string &path, int from, int to) -{ - if (!itsConnection) - return false; - if (!isCommandsListEnabled) - { - GoBusy(); - return mpd_send_playlist_move(itsConnection, path.c_str(), from, to) - && mpd_response_finish(itsConnection); - } - else - { - assert(!isIdle); - return mpd_send_playlist_move(itsConnection, path.c_str(), from, to); - } -} - -bool Connection::Rename(const std::string &from, const std::string &to) -{ - if (!itsConnection) - return false; - if (!isCommandsListEnabled) - { - GoBusy(); - return mpd_run_rename(itsConnection, from.c_str(), to.c_str()); - } - else - { - assert(!isIdle); - return mpd_send_rename(itsConnection, from.c_str(), to.c_str()); - } -} - -void Connection::GetPlaylistChanges(unsigned version, SongConsumer f) -{ - if (!itsConnection) - return; - assert(!isCommandsListEnabled); - GoBusy(); - mpd_send_queue_changes_meta(itsConnection, version); - while (mpd_song *s = mpd_recv_song(itsConnection)) - f(Song(s)); - mpd_response_finish(itsConnection); - GoIdle(); -} - -Song Connection::GetSong(const std::string &path) -{ - if (!itsConnection) - return Song(); - assert(!isCommandsListEnabled); - GoBusy(); - mpd_send_list_all_meta(itsConnection, path.c_str()); - mpd_song *s = mpd_recv_song(itsConnection); - mpd_response_finish(itsConnection); - GoIdle(); - return Song(s); -} - -int Connection::GetCurrentSongPos() const -{ - return itsCurrentStatus ? mpd_status_get_song_pos(itsCurrentStatus) : -1; -} - -Song Connection::GetCurrentlyPlayingSong() -{ - assert(!isCommandsListEnabled); - GoBusy(); - mpd_song *s = itsConnection && isPlaying() ? mpd_run_current_song(itsConnection) : 0; - Song result = s ? Song(s) : Song(); - GoIdle(); - return result; -} - -void Connection::GetPlaylistContent(const std::string &path, SongConsumer f) -{ - if (!itsConnection) - return; - assert(!isCommandsListEnabled); - GoBusy(); - mpd_send_list_playlist_meta(itsConnection, path.c_str()); - while (mpd_song *s = mpd_recv_song(itsConnection)) - f(Song(s)); - mpd_response_finish(itsConnection); - GoIdle(); -} - -void Connection::GetSupportedExtensions(std::set &acc) -{ - if (!itsConnection) - return; - assert(!isCommandsListEnabled); - GoBusy(); - - mpd_send_command(itsConnection, "decoders", NULL); - while (mpd_pair *pair = mpd_recv_pair_named(itsConnection, "suffix")) - { - acc.insert(pair->value); - mpd_return_pair(itsConnection, pair); - } - mpd_response_finish(itsConnection); - - GoIdle(); -} - -void Connection::SetRepeat(bool mode) -{ - if (!itsConnection) - return; - if (!isCommandsListEnabled) - { - GoBusy(); - mpd_run_repeat(itsConnection, mode); - } - else - { - assert(!isIdle); - mpd_send_repeat(itsConnection, mode); - } -} - -void Connection::SetRandom(bool mode) -{ - if (!itsConnection) - return; - if (!isCommandsListEnabled) - { - GoBusy(); - mpd_run_random(itsConnection, mode); - } - else - { - assert(!isIdle); - mpd_send_random(itsConnection, mode); - } -} - -void Connection::SetSingle(bool mode) -{ - if (!itsConnection) - return; - if (!isCommandsListEnabled) - { - GoBusy(); - mpd_run_single(itsConnection, mode); - } - else - { - assert(!isIdle); - mpd_send_single(itsConnection, mode); - } -} - -void Connection::SetConsume(bool mode) -{ - if (!itsConnection) - return; - if (!isCommandsListEnabled) - { - GoBusy(); - mpd_run_consume(itsConnection, mode); - } - else - { - assert(!isIdle); - mpd_send_consume(itsConnection, mode); - } -} - -bool Connection::SetVolume(unsigned vol) -{ - if (!itsConnection || vol > 100) - return false; - assert(!isCommandsListEnabled); - GoBusy(); - bool success = mpd_run_set_volume(itsConnection, vol); - if (success && !supportsIdle) - UpdateStatus(); - return success; -} - -std::string Connection::GetReplayGainMode() -{ - if (!itsConnection) - return "Unknown"; - assert(!isCommandsListEnabled); - GoBusy(); - if (!mpd_send_command(itsConnection, "replay_gain_status", NULL)) - return "Unknown"; - std::string result; - if (mpd_pair *pair = mpd_recv_pair_named(itsConnection, "replay_gain_mode")) - { - result = pair->value; - if (!result.empty()) - result[0] = toupper(result[0]); - mpd_return_pair(itsConnection, pair); - } - mpd_response_finish(itsConnection); - return result; -} - -void Connection::SetReplayGainMode(ReplayGainMode mode) -{ - if (!itsConnection) - return; - const char *rg_mode; - switch (mode) - { - case rgmOff: - rg_mode = "off"; - break; - case rgmTrack: - rg_mode = "track"; - break; - case rgmAlbum: - rg_mode = "album"; - break; - default: - FatalError("undefined value of ReplayGainMode!"); - } - if (!isCommandsListEnabled) - GoBusy(); - else - assert(!isIdle); - if (!mpd_send_command(itsConnection, "replay_gain_mode", rg_mode, NULL)) - return; - if (!isCommandsListEnabled) - mpd_response_finish(itsConnection); -} - -void Connection::SetCrossfade(unsigned crossfade) -{ - if (!itsConnection) - return; - if (!isCommandsListEnabled) - { - GoBusy(); - mpd_run_crossfade(itsConnection, crossfade); - } - else - { - assert(!isIdle); - mpd_send_crossfade(itsConnection, crossfade); - } -} - -bool Connection::SetPriority(const Song &s, int prio) -{ - if (!itsConnection) - return false; - if (!isCommandsListEnabled) - { - GoBusy(); - return mpd_run_prio_id(itsConnection, prio, s.getID()); - } - else - { - assert(!isIdle); - return mpd_send_prio_id(itsConnection, prio, s.getID()); - } -} - -int Connection::AddSong(const std::string &path, int pos) -{ - if (!itsConnection) - return -1; - int id; - if (!isCommandsListEnabled) - GoBusy(); - else - assert(!isIdle); - if (pos < 0) - mpd_send_add_id(itsConnection, path.c_str()); - else - mpd_send_add_id_to(itsConnection, path.c_str(), pos); - if (!isCommandsListEnabled) - { - id = mpd_recv_song_id(itsConnection); - mpd_response_finish(itsConnection); - } - else - id = 0; - return id; -} - -int Connection::AddSong(const Song &s, int pos) -{ - return AddSong((!s.isFromDatabase() ? "file://" : "") + s.getURI(), pos); -} - -bool Connection::Add(const std::string &path) -{ - if (!itsConnection) - return false; - if (!isCommandsListEnabled) - { - GoBusy(); - return mpd_run_add(itsConnection, path.c_str()); - } - else - { - assert(!isIdle); - return mpd_send_add(itsConnection, path.c_str()); - } -} - -bool Connection::AddRandomTag(mpd_tag_type tag, size_t number) -{ - if (!itsConnection && !number) - return false; - assert(!isCommandsListEnabled); - - StringList tags; - GetList(tag, [&tags](std::string &&tag_name) { - tags.push_back(tag_name); - }); - if (number > tags.size()) - { - if (itsErrorHandler) - itsErrorHandler(this, 0, "Requested number is out of range", itsErrorHandlerUserdata); - return false; - } - else - { - std::random_shuffle(tags.begin(), tags.end()); - auto it = tags.begin()+rand()%(tags.size()-number); - for (size_t i = 0; i < number && it != tags.end(); ++i) - { - StartSearch(1); - AddSearch(tag, *it++); - SongList songs; - CommitSearchSongs([&songs](MPD::Song &&s) { - songs.push_back(s); - }); - StartCommandsList(); - for (auto s = songs.begin(); s != songs.end(); ++s) - AddSong(*s); - CommitCommandsList(); - } - } - return true; -} - -bool Connection::AddRandomSongs(size_t number) -{ - if (!itsConnection && !number) - return false; - assert(!isCommandsListEnabled); - - StringList files; - - GoBusy(); - mpd_send_list_all(itsConnection, "/"); - while (mpd_pair *item = mpd_recv_pair_named(itsConnection, "file")) - { - files.push_back(item->value); - mpd_return_pair(itsConnection, item); - } - mpd_response_finish(itsConnection); - - if (number > files.size()) - { - if (itsErrorHandler) - itsErrorHandler(this, 0, "Requested number of random songs is bigger than size of your library", itsErrorHandlerUserdata); - return false; - } - else - { - std::random_shuffle(files.begin(), files.end()); - StartCommandsList(); - auto it = files.begin()+rand()%(std::max(size_t(1), files.size()-number)); - for (size_t i = 0; i < number && it != files.end(); ++i, ++it) - AddSong(*it); - CommitCommandsList(); - } - return true; -} - -bool Connection::Delete(unsigned pos) -{ - if (!itsConnection) - return false; - if (!isCommandsListEnabled) - GoBusy(); - else - assert(!isIdle); - bool result = mpd_send_delete(itsConnection, pos); - if (!isCommandsListEnabled) - result = mpd_response_finish(itsConnection); - return result; -} - -bool Connection::DeleteID(unsigned id) -{ - if (!itsConnection) - return false; - if (!isCommandsListEnabled) - GoBusy(); - else - assert(!isIdle); - bool result = mpd_send_delete_id(itsConnection, id); - if (!isCommandsListEnabled) - result = mpd_response_finish(itsConnection); - return result; -} - -bool Connection::PlaylistDelete(const std::string &playlist, unsigned pos) -{ - if (!itsConnection) - return false; - if (!isCommandsListEnabled) - { - GoBusy(); - return mpd_run_playlist_delete(itsConnection, playlist.c_str(), pos); - } - else - { - assert(!isIdle); - return mpd_send_playlist_delete(itsConnection, playlist.c_str(), pos); - } -} - -void Connection::StartCommandsList() -{ - if (!itsConnection) - return; - assert(!isCommandsListEnabled); - GoBusy(); - mpd_command_list_begin(itsConnection, 1); - isCommandsListEnabled = 1; -} - -bool Connection::CommitCommandsList() -{ - if (!itsConnection) - return false; - assert(isCommandsListEnabled); - assert(!isIdle); - mpd_command_list_end(itsConnection); - isCommandsListEnabled = 0; - return mpd_response_finish(itsConnection); -} - -bool Connection::DeletePlaylist(const std::string &name) -{ - if (!itsConnection) - return false; - if (!isCommandsListEnabled) - { - GoBusy(); - return mpd_run_rm(itsConnection, name.c_str()); - } - else - { - assert(!isIdle); - return mpd_send_rm(itsConnection, name.c_str()); - } -} - -bool Connection::LoadPlaylist(const std::string &name) -{ - if (!itsConnection) - return false; - assert(!isCommandsListEnabled); - GoBusy(); - return mpd_run_load(itsConnection, name.c_str()); -} - -int Connection::SavePlaylist(const std::string &name) -{ - if (!itsConnection) - return false; - assert(!isCommandsListEnabled); - GoBusy(); - mpd_send_save(itsConnection, name.c_str()); - mpd_response_finish(itsConnection); - - if (mpd_connection_get_error(itsConnection) == MPD_ERROR_SERVER - && mpd_connection_get_server_error(itsConnection) == MPD_SERVER_ERROR_EXIST) - return MPD_SERVER_ERROR_EXIST; - else - return CheckForErrors(); -} - -void Connection::GetPlaylists(StringConsumer f) -{ - if (!itsConnection) - return; - GetDirectory("/", [&f](Item &&item) { - if (item.type == itPlaylist) - f(std::move(item.name)); - }); -} - -void Connection::GetList(mpd_tag_type type, StringConsumer f) -{ - if (!itsConnection) - return; - assert(!isCommandsListEnabled); - GoBusy(); - mpd_search_db_tags(itsConnection, type); - mpd_search_commit(itsConnection); - while (mpd_pair *item = mpd_recv_pair_tag(itsConnection, type)) - { - f(std::string(item->value)); - mpd_return_pair(itsConnection, item); - } - mpd_response_finish(itsConnection); - GoIdle(); -} - -void Connection::StartSearch(bool exact_match) -{ - if (itsConnection) - mpd_search_db_songs(itsConnection, exact_match); -} - -void Connection::StartFieldSearch(mpd_tag_type item) -{ - if (itsConnection) - { - itsSearchedField = item; - mpd_search_db_tags(itsConnection, item); - } -} - -void Connection::AddSearch(mpd_tag_type item, const std::string &str) const -{ - // mpd version < 0.14.* doesn't support empty search constraints - if (Version() < 14 && str.empty()) - return; - if (itsConnection) - mpd_search_add_tag_constraint(itsConnection, MPD_OPERATOR_DEFAULT, item, str.c_str()); -} - -void Connection::AddSearchAny(const std::string &str) const -{ - assert(!str.empty()); - if (itsConnection) - mpd_search_add_any_tag_constraint(itsConnection, MPD_OPERATOR_DEFAULT, str.c_str()); -} - -void Connection::AddSearchURI(const std::string &str) const -{ - assert(!str.empty()); - if (itsConnection) - mpd_search_add_uri_constraint(itsConnection, MPD_OPERATOR_DEFAULT, str.c_str()); -} - -void Connection::CommitSearchSongs(SongConsumer f) -{ - if (!itsConnection) - return; - assert(!isCommandsListEnabled); - GoBusy(); - mpd_search_commit(itsConnection); - while (mpd_song *s = mpd_recv_song(itsConnection)) - f(Song(s)); - mpd_response_finish(itsConnection); - GoIdle(); -} - -void Connection::CommitSearchTags(StringConsumer f) -{ - if (!itsConnection) - return; - assert(!isCommandsListEnabled); - GoBusy(); - mpd_search_commit(itsConnection); - while (mpd_pair *tag = mpd_recv_pair_tag(itsConnection, itsSearchedField)) - { - f(std::string(tag->value)); - mpd_return_pair(itsConnection, tag); - } - mpd_response_finish(itsConnection); - GoIdle(); -} - -void Connection::GetDirectory(const std::string &directory, ItemConsumer f) -{ - if (!itsConnection) - return; - assert(!isCommandsListEnabled); - GoBusy(); - mpd_send_list_meta(itsConnection, directory.c_str()); - while (mpd_entity *item = mpd_recv_entity(itsConnection)) - { - Item it; - switch (mpd_entity_get_type(item)) - { - case MPD_ENTITY_TYPE_DIRECTORY: - it.name = mpd_directory_get_path(mpd_entity_get_directory(item)); - it.type = itDirectory; - break; - case MPD_ENTITY_TYPE_SONG: - it.song = std::make_shared(Song(mpd_song_dup(mpd_entity_get_song(item)))); - it.type = itSong; - break; - case MPD_ENTITY_TYPE_PLAYLIST: - it.name = mpd_playlist_get_path(mpd_entity_get_playlist(item)); - it.type = itPlaylist; - break; - default: - assert(false); - } - mpd_entity_free(item); - f(std::move(it)); - } - mpd_response_finish(itsConnection); - GoIdle(); -} - -void Connection::GetDirectoryRecursive(const std::string &directory, SongConsumer f) -{ - if (!itsConnection) - return; - assert(!isCommandsListEnabled); - GoBusy(); - mpd_send_list_all_meta(itsConnection, directory.c_str()); - while (mpd_song *s = mpd_recv_song(itsConnection)) - f(Song(s)); - mpd_response_finish(itsConnection); - GoIdle(); -} - -void Connection::GetDirectories(const std::string &directory, StringConsumer f) -{ - if (!itsConnection) - return; - assert(!isCommandsListEnabled); - GoBusy(); - mpd_send_list_meta(itsConnection, directory.c_str()); - while (mpd_directory *dir = mpd_recv_directory(itsConnection)) - { - f(std::string(mpd_directory_get_path(dir))); - mpd_directory_free(dir); - } - mpd_response_finish(itsConnection); - GoIdle(); -} - -void Connection::GetSongs(const std::string &directory, SongConsumer f) -{ - if (!itsConnection) - return; - assert(!isCommandsListEnabled); - GoBusy(); - mpd_send_list_meta(itsConnection, directory.c_str()); - while (mpd_song *s = mpd_recv_song(itsConnection)) - f(Song(s)); - mpd_response_finish(itsConnection); - GoIdle(); -} - -void Connection::GetOutputs(OutputConsumer f) -{ - if (!itsConnection) - return; - assert(!isCommandsListEnabled); - GoBusy(); - mpd_send_outputs(itsConnection); - while (mpd_output *output = mpd_recv_output(itsConnection)) - { - f(Output(mpd_output_get_name(output), mpd_output_get_enabled(output))); - mpd_output_free(output); - } - mpd_response_finish(itsConnection); - GoIdle(); -} - -bool Connection::EnableOutput(int id) -{ - if (!itsConnection) - return false; - if (!isCommandsListEnabled) - { - GoBusy(); - return mpd_run_enable_output(itsConnection, id); - } - else - { - assert(!isIdle); - return mpd_send_enable_output(itsConnection, id); - } -} - -bool Connection::DisableOutput(int id) -{ - if (!itsConnection) - return false; - if (!isCommandsListEnabled) - { - GoBusy(); - return mpd_run_disable_output(itsConnection, id); - } - else - { - assert(!isIdle); - return mpd_send_disable_output(itsConnection, id); - } -} - -void Connection::GetURLHandlers(StringConsumer f) -{ - if (!itsConnection) - return; - assert(!isCommandsListEnabled); - GoBusy(); - mpd_send_list_url_schemes(itsConnection); - while (mpd_pair *handler = mpd_recv_pair_named(itsConnection, "handler")) - { - f(std::string(handler->value)); - mpd_return_pair(itsConnection, handler); - } - mpd_response_finish(itsConnection); - GoIdle(); -} - -void Connection::GetTagTypes(StringConsumer f) -{ - if (!itsConnection) - return; - assert(!isCommandsListEnabled); - GoBusy(); - mpd_send_list_tag_types(itsConnection); - while (mpd_pair *tag_type = mpd_recv_pair_named(itsConnection, "tagtype")) - { - f(std::string(tag_type->value)); - mpd_return_pair(itsConnection, tag_type); - } - mpd_response_finish(itsConnection); - GoIdle(); -} - -int Connection::CheckForErrors() -{ - int error_code = MPD_ERROR_SUCCESS; - if ((error_code = mpd_connection_get_error(itsConnection)) != MPD_ERROR_SUCCESS) - { - itsErrorMessage = mpd_connection_get_error_message(itsConnection); - if (error_code == MPD_ERROR_SERVER) - error_code |= (mpd_connection_get_server_error(itsConnection) << 8); - if (!mpd_connection_clear_error(itsConnection)) - { - Disconnect(); - // notify about mpd state changed to unknown. - StatusChanges changes; - changes.PlayerState = 1; - if (itsUpdater) - itsUpdater(this, changes, itsErrorHandlerUserdata); - } - if (itsErrorHandler) - itsErrorHandler(this, error_code, itsErrorMessage.c_str(), itsErrorHandlerUserdata); - } - return error_code; -} - -} +/*************************************************************************** + * Copyright (C) 2008-2012 by Andrzej Rybczak * + * electricityispower@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include +#include +#include +#include + +#include "charset.h" +#include "error.h" +#include "mpdpp.h" + +MPD::Connection Mpd; +MPD::Status MpdStatus; + +namespace MPD {// + +bool Statistics::empty() const +{ + return m_stats.get() == 0; +} + +unsigned Statistics::artists() const +{ + assert(!empty()); + return mpd_stats_get_number_of_artists(m_stats.get()); +} + +unsigned Statistics::albums() const +{ + assert(!empty()); + return mpd_stats_get_number_of_albums(m_stats.get()); +} + +unsigned Statistics::songs() const +{ + assert(!empty()); + return mpd_stats_get_number_of_songs(m_stats.get()); +} + +unsigned long Statistics::playTime() const +{ + assert(!empty()); + return mpd_stats_get_play_time(m_stats.get()); +} + +unsigned long Statistics::uptime() const +{ + assert(!empty()); + return mpd_stats_get_uptime(m_stats.get()); +} + +unsigned long Statistics::dbUpdateTime() const +{ + assert(!empty()); + return mpd_stats_get_db_update_time(m_stats.get()); +} + +unsigned long Statistics::dbPlayTime() const +{ + assert(!empty()); + return mpd_stats_get_db_play_time(m_stats.get()); +} + +void Status::clear() +{ + m_status.reset(); +} + +bool Status::empty() const +{ + return m_status.get() == 0; +} + +int Status::volume() const +{ + assert(!empty()); + return mpd_status_get_volume(m_status.get()); +} + +bool Status::repeat() const +{ + assert(!empty()); + return mpd_status_get_repeat(m_status.get()); +} + +bool Status::random() const +{ + assert(!empty()); + return mpd_status_get_random(m_status.get()); +} + +bool Status::single() const +{ + assert(!empty()); + return mpd_status_get_single(m_status.get()); +} + +bool Status::consume() const +{ + assert(!empty()); + return mpd_status_get_consume(m_status.get()); +} + +unsigned Status::playlistLength() const +{ + assert(!empty()); + return mpd_status_get_queue_length(m_status.get()); +} + +unsigned int Status::playlistVersion() const +{ + assert(!empty()); + return mpd_status_get_queue_version(m_status.get()); +} + +PlayerState Status::playerState() const +{ + assert(!empty()); + return PlayerState(mpd_status_get_state(m_status.get())); +} + +unsigned Status::crossfade() const +{ + assert(!empty()); + return mpd_status_get_crossfade(m_status.get()); +} + +int Status::currentSongPosition() const +{ + assert(!empty()); + return mpd_status_get_song_pos(m_status.get()); +} + +int Status::currentSongID() const +{ + assert(!empty()); + return mpd_status_get_song_id(m_status.get()); +} + +int Status::nextSongPosition() const +{ + assert(!empty()); + return mpd_status_get_next_song_pos(m_status.get()); +} + +int Status::nextSongID() const +{ + assert(!empty()); + return mpd_status_get_next_song_id(m_status.get()); +} + +unsigned Status::elapsedTime() const +{ + assert(!empty()); + return mpd_status_get_elapsed_time(m_status.get()); +} + +unsigned Status::totalTime() const +{ + assert(!empty()); + return mpd_status_get_total_time(m_status.get()); +} + +unsigned Status::kbps() const +{ + assert(!empty()); + return mpd_status_get_kbit_rate(m_status.get()); +} + +unsigned Status::updateID() const +{ + assert(!empty()); + return mpd_status_get_update_id(m_status.get()); +} + +const char *Status::error() const +{ + assert(!empty()); + return mpd_status_get_error(m_status.get()); +} + +Connection::Connection() : m_connection(nullptr), + m_command_list_active(false), + m_idle(false), + m_host("localhost"), + m_port(6600), + m_timeout(15) +{ +} + +Connection::~Connection() +{ + if (m_connection) + mpd_connection_free(m_connection); +} + +void Connection::Connect() +{ + assert(!m_connection); + try + { + m_connection = mpd_connection_new(m_host.c_str(), m_port, m_timeout * 1000); + checkErrors(); + if (!m_password.empty()) + SendPassword(); + m_fd = mpd_connection_get_fd(m_connection); + checkErrors(); + } + catch (MPD::ClientError &e) + { + Disconnect(); + throw e; + } +} + +bool Connection::Connected() const +{ + return m_connection; +} + +void Connection::Disconnect() +{ + if (m_connection) + mpd_connection_free(m_connection); + m_connection = nullptr; + m_command_list_active = false; + m_idle = false; +} + +unsigned Connection::Version() const +{ + return m_connection ? mpd_connection_get_server_version(m_connection)[1] : 0; +} + +void Connection::SetHostname(const std::string &host) +{ + size_t at = host.find("@"); + if (at != std::string::npos) + { + m_password = host.substr(0, at); + m_host = host.substr(at+1); + } + else + m_host = host; +} + +void Connection::SendPassword() +{ + assert(m_connection); + noidle(); + assert(!m_command_list_active); + mpd_run_password(m_connection, m_password.c_str()); + checkErrors(); +} + +void Connection::idle() +{ + checkConnection(); + if (!m_idle) + { + mpd_send_idle(m_connection); + checkErrors(); + } + m_idle = true; +} + +int Connection::noidle() +{ + checkConnection(); + int flags = 0; + if (m_idle && mpd_send_noidle(m_connection)) + { + m_idle = false; + flags = mpd_recv_idle(m_connection, true); + mpd_response_finish(m_connection); + checkErrors(); + } + return flags; +} + +Statistics Connection::getStatistics() +{ + prechecks(); + mpd_stats *stats = mpd_run_stats(m_connection); + checkErrors(); + return Statistics(stats); +} + +Status Connection::getStatus() +{ + prechecks(); + mpd_status *status = mpd_run_status(m_connection); + checkErrors(); + return Status(status); +} + +void Connection::UpdateDirectory(const std::string &path) +{ + prechecksNoCommandsList(); + mpd_run_update(m_connection, path.c_str()); + checkErrors(); +} + +void Connection::Play() +{ + prechecksNoCommandsList(); + mpd_run_play(m_connection); + checkErrors(); +} + +void Connection::Play(int pos) +{ + prechecksNoCommandsList(); + mpd_run_play_pos(m_connection, pos); + checkErrors(); +} + +void Connection::PlayID(int id) +{ + prechecksNoCommandsList(); + mpd_run_play_id(m_connection, id); + checkErrors(); +} + +void Connection::Pause(bool state) +{ + prechecksNoCommandsList(); + mpd_run_pause(m_connection, state); + checkErrors(); +} + +void Connection::Toggle() +{ + prechecksNoCommandsList(); + mpd_run_toggle_pause(m_connection); + checkErrors(); +} + +void Connection::Stop() +{ + prechecksNoCommandsList(); + mpd_run_stop(m_connection); + checkErrors(); +} + +void Connection::Next() +{ + prechecksNoCommandsList(); + mpd_run_next(m_connection); + checkErrors(); +} + +void Connection::Prev() +{ + prechecksNoCommandsList(); + mpd_run_previous(m_connection); + checkErrors(); +} + +void Connection::Move(unsigned from, unsigned to) +{ + prechecks(); + if (m_command_list_active) + mpd_send_move(m_connection, from, to); + else + { + mpd_run_move(m_connection, from, to); + checkErrors(); + } +} + +void Connection::Swap(unsigned from, unsigned to) +{ + prechecks(); + if (m_command_list_active) + mpd_send_swap(m_connection, from, to); + else + { + mpd_run_swap(m_connection, from, to); + checkErrors(); + } +} + +void Connection::Seek(unsigned where) +{ + prechecksNoCommandsList(); + mpd_run_seek_pos(m_connection, MpdStatus.currentSongPosition(), where); + checkErrors(); +} + +void Connection::Shuffle() +{ + prechecksNoCommandsList(); + mpd_run_shuffle(m_connection); + checkErrors(); +} + +void Connection::ClearMainPlaylist() +{ + prechecksNoCommandsList(); + mpd_run_clear(m_connection); + checkErrors(); +} + +void Connection::ClearPlaylist(const std::string &playlist) +{ + prechecksNoCommandsList(); + mpd_run_playlist_clear(m_connection, playlist.c_str()); + checkErrors(); +} + +void Connection::AddToPlaylist(const std::string &path, const Song &s) +{ + AddToPlaylist(path, s.getURI()); +} + +void Connection::AddToPlaylist(const std::string &path, const std::string &file) +{ + prechecks(); + if (m_command_list_active) + mpd_send_playlist_add(m_connection, path.c_str(), file.c_str()); + else + { + mpd_run_playlist_add(m_connection, path.c_str(), file.c_str()); + checkErrors(); + } +} + +void Connection::PlaylistMove(const std::string &path, int from, int to) +{ + prechecks(); + if (m_command_list_active) + mpd_send_playlist_move(m_connection, path.c_str(), from, to); + else + { + mpd_send_playlist_move(m_connection, path.c_str(), from, to); + mpd_response_finish(m_connection); + checkErrors(); + } +} + +void Connection::Rename(const std::string &from, const std::string &to) +{ + prechecksNoCommandsList(); + mpd_run_rename(m_connection, from.c_str(), to.c_str()); + checkErrors(); +} + +void Connection::GetPlaylistChanges(unsigned version, SongConsumer f) +{ + prechecksNoCommandsList(); + mpd_send_queue_changes_meta(m_connection, version); + while (mpd_song *s = mpd_recv_song(m_connection)) + f(Song(s)); + mpd_response_finish(m_connection); + checkErrors(); +} + +Song Connection::GetSong(const std::string &path) +{ + prechecksNoCommandsList(); + mpd_send_list_all_meta(m_connection, path.c_str()); + mpd_song *s = mpd_recv_song(m_connection); + mpd_response_finish(m_connection); + checkErrors(); + return Song(s); +} + +void Connection::GetPlaylistContent(const std::string &path, SongConsumer f) +{ + prechecksNoCommandsList(); + mpd_send_list_playlist_meta(m_connection, path.c_str()); + while (mpd_song *s = mpd_recv_song(m_connection)) + f(Song(s)); + mpd_response_finish(m_connection); + checkErrors(); +} + +void Connection::GetSupportedExtensions(std::set &acc) +{ + prechecksNoCommandsList(); + mpd_send_command(m_connection, "decoders", NULL); + while (mpd_pair *pair = mpd_recv_pair_named(m_connection, "suffix")) + { + acc.insert(pair->value); + mpd_return_pair(m_connection, pair); + } + mpd_response_finish(m_connection); + checkErrors(); +} + +void Connection::SetRepeat(bool mode) +{ + prechecksNoCommandsList(); + mpd_run_repeat(m_connection, mode); + checkErrors(); +} + +void Connection::SetRandom(bool mode) +{ + prechecksNoCommandsList(); + mpd_run_random(m_connection, mode); + checkErrors(); +} + +void Connection::SetSingle(bool mode) +{ + prechecksNoCommandsList(); + mpd_run_single(m_connection, mode); + checkErrors(); +} + +void Connection::SetConsume(bool mode) +{ + prechecksNoCommandsList(); + mpd_run_consume(m_connection, mode); + checkErrors(); +} + +void Connection::SetVolume(unsigned vol) +{ + prechecksNoCommandsList(); + mpd_run_set_volume(m_connection, vol); + checkErrors(); +} + +std::string Connection::GetReplayGainMode() +{ + prechecksNoCommandsList(); + mpd_send_command(m_connection, "replay_gain_status", NULL); + std::string result; + if (mpd_pair *pair = mpd_recv_pair_named(m_connection, "replay_gain_mode")) + { + result = pair->value; + if (!result.empty()) + result[0] = toupper(result[0]); + mpd_return_pair(m_connection, pair); + } + mpd_response_finish(m_connection); + checkErrors(); + return result; +} + +void Connection::SetReplayGainMode(ReplayGainMode mode) +{ + prechecksNoCommandsList(); + const char *rg_mode; + switch (mode) + { + case rgmOff: + rg_mode = "off"; + break; + case rgmTrack: + rg_mode = "track"; + break; + case rgmAlbum: + rg_mode = "album"; + break; + default: + rg_mode = ""; + break; + } + mpd_send_command(m_connection, "replay_gain_mode", rg_mode, NULL); + mpd_response_finish(m_connection); + checkErrors(); +} + +void Connection::SetCrossfade(unsigned crossfade) +{ + prechecksNoCommandsList(); + mpd_run_crossfade(m_connection, crossfade); + checkErrors(); +} + +void Connection::SetPriority(const Song &s, int prio) +{ + prechecks(); + if (m_command_list_active) + mpd_send_prio_id(m_connection, prio, s.getID()); + else + { + mpd_run_prio_id(m_connection, prio, s.getID()); + checkErrors(); + } +} + +int Connection::AddSong(const std::string &path, int pos) +{ + prechecks(); + int id; + if (pos < 0) + mpd_send_add_id(m_connection, path.c_str()); + else + mpd_send_add_id_to(m_connection, path.c_str(), pos); + if (!m_command_list_active) + { + id = mpd_recv_song_id(m_connection); + mpd_response_finish(m_connection); + checkErrors(); + } + else + id = 0; + return id; +} + +int Connection::AddSong(const Song &s, int pos) +{ + return AddSong((!s.isFromDatabase() ? "file://" : "") + s.getURI(), pos); +} + +void Connection::Add(const std::string &path) +{ + prechecks(); + if (m_command_list_active) + mpd_send_add(m_connection, path.c_str()); + else + { + mpd_run_add(m_connection, path.c_str()); + checkErrors(); + } +} + +bool Connection::AddRandomTag(mpd_tag_type tag, size_t number) +{ + StringList tags; + GetList(tag, [&tags](std::string &&tag_name) { + tags.push_back(tag_name); + }); + if (number > tags.size()) + { + //if (itsErrorHandler) + // itsErrorHandler(this, 0, "Requested number is out of range", itsErrorHandlerUserdata); + return false; + } + else + { + std::random_shuffle(tags.begin(), tags.end()); + auto it = tags.begin()+rand()%(tags.size()-number); + for (size_t i = 0; i < number && it != tags.end(); ++i) + { + StartSearch(1); + AddSearch(tag, *it++); + SongList songs; + CommitSearchSongs([&songs](MPD::Song &&s) { + songs.push_back(s); + }); + StartCommandsList(); + for (auto s = songs.begin(); s != songs.end(); ++s) + AddSong(*s); + CommitCommandsList(); + } + } + return true; +} + +bool Connection::AddRandomSongs(size_t number) +{ + prechecksNoCommandsList(); + StringList files; + mpd_send_list_all(m_connection, "/"); + while (mpd_pair *item = mpd_recv_pair_named(m_connection, "file")) + { + files.push_back(item->value); + mpd_return_pair(m_connection, item); + } + mpd_response_finish(m_connection); + checkErrors(); + + if (number > files.size()) + { + //if (itsErrorHandler) + // itsErrorHandler(this, 0, "Requested number of random songs is bigger than size of your library", itsErrorHandlerUserdata); + return false; + } + else + { + std::random_shuffle(files.begin(), files.end()); + StartCommandsList(); + auto it = files.begin()+rand()%(std::max(size_t(1), files.size()-number)); + for (size_t i = 0; i < number && it != files.end(); ++i, ++it) + AddSong(*it); + CommitCommandsList(); + } + return true; +} + +void Connection::Delete(unsigned pos) +{ + prechecks(); + mpd_send_delete(m_connection, pos); + if (!m_command_list_active) + { + mpd_response_finish(m_connection); + checkErrors(); + } +} + +void Connection::PlaylistDelete(const std::string &playlist, unsigned pos) +{ + prechecksNoCommandsList(); + mpd_run_playlist_delete(m_connection, playlist.c_str(), pos); + checkErrors(); +} + +void Connection::StartCommandsList() +{ + prechecksNoCommandsList(); + mpd_command_list_begin(m_connection, true); + m_command_list_active = true; + checkErrors(); +} + +void Connection::CommitCommandsList() +{ + prechecks(); + assert(m_command_list_active); + mpd_command_list_end(m_connection); + mpd_response_finish(m_connection); + m_command_list_active = false; + checkErrors(); +} + +void Connection::DeletePlaylist(const std::string &name) +{ + prechecksNoCommandsList(); + mpd_run_rm(m_connection, name.c_str()); + checkErrors(); +} + +void Connection::LoadPlaylist(const std::string &name) +{ + prechecksNoCommandsList(); + mpd_run_load(m_connection, name.c_str()); + checkErrors(); +} + +void Connection::SavePlaylist(const std::string &name) +{ + prechecksNoCommandsList(); + mpd_send_save(m_connection, name.c_str()); + mpd_response_finish(m_connection); + checkErrors(); +} + +void Connection::GetPlaylists(StringConsumer f) +{ + GetDirectory("/", [&f](Item &&item) { + if (item.type == itPlaylist) + f(std::move(item.name)); + }); +} + +void Connection::GetList(mpd_tag_type type, StringConsumer f) +{ + prechecksNoCommandsList(); + mpd_search_db_tags(m_connection, type); + mpd_search_commit(m_connection); + while (mpd_pair *item = mpd_recv_pair_tag(m_connection, type)) + { + f(std::string(item->value)); + mpd_return_pair(m_connection, item); + } + mpd_response_finish(m_connection); + checkErrors(); +} + +void Connection::StartSearch(bool exact_match) +{ + prechecksNoCommandsList(); + mpd_search_db_songs(m_connection, exact_match); +} + +void Connection::StartFieldSearch(mpd_tag_type item) +{ + prechecksNoCommandsList(); + mpd_search_db_tags(m_connection, item); +} + +void Connection::AddSearch(mpd_tag_type item, const std::string &str) const +{ + checkConnection(); + mpd_search_add_tag_constraint(m_connection, MPD_OPERATOR_DEFAULT, item, str.c_str()); +} + +void Connection::AddSearchAny(const std::string &str) const +{ + checkConnection(); + mpd_search_add_any_tag_constraint(m_connection, MPD_OPERATOR_DEFAULT, str.c_str()); +} + +void Connection::AddSearchURI(const std::string &str) const +{ + checkConnection(); + mpd_search_add_uri_constraint(m_connection, MPD_OPERATOR_DEFAULT, str.c_str()); +} + +void Connection::CommitSearchSongs(SongConsumer f) +{ + prechecksNoCommandsList(); + mpd_search_commit(m_connection); + while (mpd_song *s = mpd_recv_song(m_connection)) + f(Song(s)); + mpd_response_finish(m_connection); + checkErrors(); +} + +void Connection::CommitSearchTags(StringConsumer f) +{ + prechecksNoCommandsList(); + mpd_search_commit(m_connection); + while (mpd_pair *tag = mpd_recv_pair_tag(m_connection, m_searched_field)) + { + f(std::string(tag->value)); + mpd_return_pair(m_connection, tag); + } + mpd_response_finish(m_connection); + checkErrors(); +} + +void Connection::GetDirectory(const std::string &directory, ItemConsumer f) +{ + prechecksNoCommandsList(); + mpd_send_list_meta(m_connection, directory.c_str()); + while (mpd_entity *item = mpd_recv_entity(m_connection)) + { + Item it; + switch (mpd_entity_get_type(item)) + { + case MPD_ENTITY_TYPE_DIRECTORY: + it.name = mpd_directory_get_path(mpd_entity_get_directory(item)); + it.type = itDirectory; + break; + case MPD_ENTITY_TYPE_SONG: + it.song = std::make_shared(Song(mpd_song_dup(mpd_entity_get_song(item)))); + it.type = itSong; + break; + case MPD_ENTITY_TYPE_PLAYLIST: + it.name = mpd_playlist_get_path(mpd_entity_get_playlist(item)); + it.type = itPlaylist; + break; + default: + assert(false); + } + mpd_entity_free(item); + f(std::move(it)); + } + mpd_response_finish(m_connection); + checkErrors(); +} + +void Connection::GetDirectoryRecursive(const std::string &directory, SongConsumer f) +{ + prechecksNoCommandsList(); + mpd_send_list_all_meta(m_connection, directory.c_str()); + while (mpd_song *s = mpd_recv_song(m_connection)) + f(Song(s)); + mpd_response_finish(m_connection); + checkErrors(); +} + +void Connection::GetDirectories(const std::string &directory, StringConsumer f) +{ + prechecksNoCommandsList(); + mpd_send_list_meta(m_connection, directory.c_str()); + while (mpd_directory *dir = mpd_recv_directory(m_connection)) + { + f(std::string(mpd_directory_get_path(dir))); + mpd_directory_free(dir); + } + mpd_response_finish(m_connection); + checkErrors(); +} + +void Connection::GetSongs(const std::string &directory, SongConsumer f) +{ + prechecksNoCommandsList(); + mpd_send_list_meta(m_connection, directory.c_str()); + while (mpd_song *s = mpd_recv_song(m_connection)) + f(Song(s)); + mpd_response_finish(m_connection); + checkErrors(); +} + +void Connection::GetOutputs(OutputConsumer f) +{ + prechecksNoCommandsList(); + mpd_send_outputs(m_connection); + while (mpd_output *output = mpd_recv_output(m_connection)) + { + f(Output(mpd_output_get_name(output), mpd_output_get_enabled(output))); + mpd_output_free(output); + } + mpd_response_finish(m_connection); + checkErrors(); +} + +void Connection::EnableOutput(int id) +{ + prechecksNoCommandsList(); + mpd_run_enable_output(m_connection, id); + checkErrors(); +} + +void Connection::DisableOutput(int id) +{ + prechecksNoCommandsList(); + mpd_run_disable_output(m_connection, id); + checkErrors(); +} + +void Connection::GetURLHandlers(StringConsumer f) +{ + prechecksNoCommandsList(); + mpd_send_list_url_schemes(m_connection); + while (mpd_pair *handler = mpd_recv_pair_named(m_connection, "handler")) + { + f(std::string(handler->value)); + mpd_return_pair(m_connection, handler); + } + mpd_response_finish(m_connection); + checkErrors(); +} + +void Connection::GetTagTypes(StringConsumer f) +{ + + prechecksNoCommandsList(); + mpd_send_list_tag_types(m_connection); + while (mpd_pair *tag_type = mpd_recv_pair_named(m_connection, "tagtype")) + { + f(std::string(tag_type->value)); + mpd_return_pair(m_connection, tag_type); + } + mpd_response_finish(m_connection); + checkErrors(); +} + +void Connection::checkConnection() const +{ + if (!m_connection) + throw ClientError(MPD_ERROR_STATE, "No active MPD connection", false); +} + +void Connection::prechecks() +{ + checkConnection(); + noidle(); +} + +void Connection::prechecksNoCommandsList() +{ + assert(!m_command_list_active); + prechecks(); +} + +void Connection::checkErrors() const +{ + mpd_error code = mpd_connection_get_error(m_connection); + if (code != MPD_ERROR_SUCCESS) + { + std::string msg = mpd_connection_get_error_message(m_connection); + if (code == MPD_ERROR_SERVER) + { + mpd_server_error server_code = mpd_connection_get_server_error(m_connection); + bool clearable = mpd_connection_clear_error(m_connection); + throw ServerError(server_code, msg, clearable); + } + else + { + bool clearable = mpd_connection_clear_error(m_connection); + throw ClientError(code, msg, clearable); + } + } +} + +} diff --git a/src/mpdpp.h b/src/mpdpp.h index 91488b8..a391b81 100644 --- a/src/mpdpp.h +++ b/src/mpdpp.h @@ -22,6 +22,7 @@ #define NCMPCPP_MPDPP_H #include +#include #include #include @@ -34,6 +35,36 @@ enum ItemType { itDirectory, itSong, itPlaylist }; enum PlayerState { psUnknown, psStop, psPlay, psPause }; enum ReplayGainMode { rgmOff, rgmTrack, rgmAlbum }; +struct ClientError: public std::exception +{ + ClientError(mpd_error code_, std::string msg, bool clearable_) + : m_code(code_), m_msg(msg), m_clearable(clearable_) { } + + virtual const char *what() const noexcept { return m_msg.c_str(); } + mpd_error code() const { return m_code; } + bool clearable() const { return m_clearable; } + +private: + mpd_error m_code; + std::string m_msg; + bool m_clearable; +}; + +struct ServerError: public std::exception +{ + ServerError(mpd_server_error code_, std::string msg, bool clearable_) + : m_code(code_), m_msg(msg), m_clearable(clearable_) { } + + virtual const char *what() const noexcept { return m_msg.c_str(); } + mpd_server_error code() const { return m_code; } + bool clearable() const { return m_clearable; } + +private: + mpd_server_error m_code; + std::string m_msg; + bool m_clearable; +}; + struct Statistics { friend class Connection; @@ -54,6 +85,40 @@ private: std::shared_ptr m_stats; }; +struct Status +{ + friend class Connection; + + Status() { } + + void clear(); + bool empty() const; + + int volume() const; + bool repeat() const; + bool random() const; + bool single() const; + bool consume() const; + unsigned playlistLength() const; + unsigned playlistVersion() const; + PlayerState playerState() const; + unsigned crossfade() const; + int currentSongPosition() const; + int currentSongID() const; + int nextSongPosition() const; + int nextSongID() const; + unsigned elapsedTime() const; + unsigned totalTime() const; + unsigned kbps() const; + unsigned updateID() const; + const char *error() const; + +private: + Status(mpd_status *status) : m_status(status, mpd_status_free) { } + + std::shared_ptr m_status; +}; + struct Item { std::shared_ptr song; @@ -61,26 +126,6 @@ struct Item std::string name; }; -struct StatusChanges -{ - StatusChanges() : Playlist(0), StoredPlaylists(0), SongID(0), Database(0), DBUpdating(0), Volume(0), ElapsedTime(0), Crossfade(0), Random(0), Repeat(0), Single(0), Consume(0), PlayerState(0), StatusFlags(0), Outputs(0) { } - bool Playlist:1; - bool StoredPlaylists:1; - bool SongID:1; - bool Database:1; - bool DBUpdating:1; - bool Volume:1; - bool ElapsedTime:1; - bool Crossfade:1; - bool Random:1; - bool Repeat:1; - bool Single:1; - bool Consume:1; - bool PlayerState:1; - bool StatusFlags:1; - bool Outputs:1; -}; - struct Output { Output(const std::string &name_, bool enabled) : m_name(name_), m_enabled(enabled) { } @@ -99,7 +144,6 @@ typedef std::vector OutputList; class Connection { - typedef void (*StatusUpdater) (Connection *, StatusChanges, void *); typedef void (*ErrorHandler) (Connection *, int, const char *, void *); typedef std::function ItemConsumer; @@ -111,33 +155,27 @@ public: Connection(); ~Connection(); - bool Connect(); + void Connect(); bool Connected() const; void Disconnect(); - const std::string & GetHostname() { return itsHost; } - int GetPort() { return itsPort; } + const std::string &GetHostname() { return m_host; } + int GetPort() { return m_port; } unsigned Version() const; - void SetIdleEnabled(bool val) { isIdleEnabled = val; } - void BlockIdle(bool val) { itsIdleBlocked = val; } - bool SupportsIdle() const { return supportsIdle; } - void OrderDataFetching() { hasData = 1; } - int GetFD() const { return itsFD; } + int GetFD() const { return m_fd; } void SetHostname(const std::string &); - void SetPort(int port) { itsPort = port; } - void SetTimeout(int timeout) { itsTimeout = timeout; } - void SetPassword(const std::string &password) { itsPassword = password; } - bool SendPassword(); + void SetPort(int port) { m_port = port; } + void SetTimeout(int timeout) { m_timeout = timeout; } + void SetPassword(const std::string &password) { m_password = password; } + void SendPassword(); Statistics getStatistics(); + Status getStatus(); - void SetStatusUpdater(StatusUpdater, void *); - void SetErrorHandler(ErrorHandler, void *); - void UpdateStatus(); - bool UpdateDirectory(const std::string &); + void UpdateDirectory(const std::string &); void Play(); void Play(int); @@ -147,36 +185,15 @@ public: void Stop(); void Next(); void Prev(); - bool Move(unsigned, unsigned); + void Move(unsigned int from, unsigned int to); void Swap(unsigned, unsigned); void Seek(unsigned); void Shuffle(); - bool ClearMainPlaylist(); - - bool isPlaying() const { return GetState() > psStop; } - - PlayerState GetState() const { return itsCurrentStatus ? PlayerState(mpd_status_get_state(itsCurrentStatus)) : psUnknown; } - PlayerState GetOldState() const { return itsOldStatus ? PlayerState(mpd_status_get_state(itsOldStatus)) : psUnknown; } - bool GetRepeat() const { return itsCurrentStatus ? mpd_status_get_repeat(itsCurrentStatus) : 0; } - bool GetRandom() const { return itsCurrentStatus ? mpd_status_get_random(itsCurrentStatus) : 0; } - bool GetSingle() const { return itsCurrentStatus ? mpd_status_get_single(itsCurrentStatus) : 0; } - bool GetConsume() const { return itsCurrentStatus ? mpd_status_get_consume(itsCurrentStatus) : 0; } - bool GetDBIsUpdating() const { return itsCurrentStatus ? mpd_status_get_update_id(itsCurrentStatus) : 0; } - int GetVolume() const { return itsCurrentStatus ? mpd_status_get_volume(itsCurrentStatus) : -1; } - unsigned GetCrossfade() const { return itsCurrentStatus ? mpd_status_get_crossfade(itsCurrentStatus) : 0; } - unsigned GetPlaylistID() const { return itsCurrentStatus ? mpd_status_get_queue_version(itsCurrentStatus) : 0; } - unsigned GetOldPlaylistID() const { return itsOldStatus ? mpd_status_get_queue_version(itsOldStatus) : 0; } - unsigned GetElapsedTime() const { return itsCurrentStatus ? itsElapsed : 0; } - int GetTotalTime() const { return itsCurrentStatus ? mpd_status_get_total_time(itsCurrentStatus) : 0; } - unsigned GetBitrate() const { return itsCurrentStatus ? mpd_status_get_kbit_rate(itsCurrentStatus) : 0; } - - size_t GetPlaylistLength() const { return itsCurrentStatus ? mpd_status_get_queue_length(itsCurrentStatus) : 0; } - void GetPlaylistChanges(unsigned, SongConsumer f); + void ClearMainPlaylist(); - const std::string &GetErrorMessage() const { return itsErrorMessage; } + void GetPlaylistChanges(unsigned, SongConsumer f); Song GetCurrentlyPlayingSong(); - int GetCurrentSongPos() const; Song GetSong(const std::string &); void GetPlaylistContent(const std::string &name, SongConsumer f); @@ -187,36 +204,35 @@ public: void SetSingle(bool); void SetConsume(bool); void SetCrossfade(unsigned); - bool SetVolume(unsigned); + void SetVolume(unsigned int vol); std::string GetReplayGainMode(); void SetReplayGainMode(ReplayGainMode); - bool SetPriority(const Song &s, int prio); + void SetPriority(const MPD::Song &s, int prio); int AddSong(const std::string &, int = -1); // returns id of added song int AddSong(const Song &, int = -1); // returns id of added song bool AddRandomTag(mpd_tag_type, size_t); bool AddRandomSongs(size_t); - bool Add(const std::string &path); - bool Delete(unsigned); - bool DeleteID(unsigned); - bool PlaylistDelete(const std::string &, unsigned); + void Add(const std::string &path); + void Delete(unsigned int pos); + void PlaylistDelete(const std::string &playlist, unsigned int pos); void StartCommandsList(); - bool CommitCommandsList(); + void CommitCommandsList(); - bool DeletePlaylist(const std::string &); - bool LoadPlaylist(const std::string &name); - int SavePlaylist(const std::string &); - bool ClearPlaylist(const std::string &); + void DeletePlaylist(const std::string &name); + void LoadPlaylist(const std::string &name); + void SavePlaylist(const std::string &); + void ClearPlaylist(const std::string &playlist); void AddToPlaylist(const std::string &, const Song &); void AddToPlaylist(const std::string &, const std::string &); - bool PlaylistMove(const std::string &, int, int); - bool Rename(const std::string &, const std::string &); + void PlaylistMove(const std::string &path, int from, int to); + void Rename(const std::string &from, const std::string &to); void StartSearch(bool); void StartFieldSearch(mpd_tag_type); - void AddSearch(mpd_tag_type, const std::string &) const; + void AddSearch(mpd_tag_type item, const std::string &str) const; void AddSearchAny(const std::string &str) const; void AddSearchURI(const std::string &str) const; void CommitSearchSongs(SongConsumer f); @@ -230,53 +246,39 @@ public: void GetDirectories(const std::string &directory, StringConsumer f); void GetOutputs(OutputConsumer f); - bool EnableOutput(int); - bool DisableOutput(int); + void EnableOutput(int id); + void DisableOutput(int id); void GetURLHandlers(StringConsumer f); void GetTagTypes(StringConsumer f); + void idle(); + int noidle(); + private: - void GoIdle(); - int GoBusy(); - int CheckForErrors(); + void checkConnection() const; + void prechecks(); + void prechecksNoCommandsList(); + void checkErrors() const; - mpd_connection *itsConnection; - bool isCommandsListEnabled; - - std::string itsErrorMessage; - - int itsFD; - bool isIdle; - bool isIdleEnabled; - bool itsIdleBlocked; - bool supportsIdle; - bool hasData; - - std::string itsHost; - int itsPort; - int itsTimeout; - std::string itsPassword; - - mpd_status *itsCurrentStatus; - mpd_status *itsOldStatus; - - unsigned itsElapsed; - time_t itsElapsedTimer[2]; + mpd_connection *m_connection; + bool m_command_list_active; - StatusChanges itsChanges; + int m_fd; + bool m_idle; - StatusUpdater itsUpdater; - void *itsStatusUpdaterUserdata; - ErrorHandler itsErrorHandler; - void *itsErrorHandlerUserdata; + std::string m_host; + int m_port; + int m_timeout; + std::string m_password; - mpd_tag_type itsSearchedField; + mpd_tag_type m_searched_field; }; } extern MPD::Connection Mpd; +extern MPD::Status MpdStatus; #endif // NCMPCPP_MPDPP_H diff --git a/src/ncmpcpp.cpp b/src/ncmpcpp.cpp index 91cb06c..b09bd94 100644 --- a/src/ncmpcpp.cpp +++ b/src/ncmpcpp.cpp @@ -40,6 +40,7 @@ #include "error.h" #include "helpers.h" #include "lyrics.h" +#include "outputs.h" #include "playlist.h" #include "settings.h" #include "status.h" @@ -97,7 +98,6 @@ int main(int argc, char **argv) using Global::wHeader; using Global::wFooter; - using Global::ShowMessages; using Global::VolumeState; using Global::Timer; @@ -126,20 +126,10 @@ int main(int argc, char **argv) Mpd.SetPort(Config.mpd_port); Mpd.SetTimeout(Config.mpd_connection_timeout); - Mpd.SetIdleEnabled(Config.enable_idle_notifications); if (argc > 1) ParseArgv(argc, argv); - if (!Actions::connectToMPD()) - exit(1); - - if (Mpd.Version() < 16) - { - std::cout << "MPD < 0.16.0 is not supported, please upgrade.\n"; - exit(1); - } - CreateDir(Config.ncmpcpp_directory); // always execute these commands, even if ncmpcpp use exit function @@ -171,8 +161,6 @@ int main(int argc, char **argv) wFooter = new NC::Window(0, Actions::FooterStartY, COLS, Actions::FooterHeight, "", Config.statusbar_color, NC::Border::None); wFooter->setTimeout(500); wFooter->setGetStringHelper(Statusbar::Helpers::getString); - if (Mpd.SupportsIdle()) - wFooter->addFDCallback(Mpd.GetFD(), Statusbar::Helpers::mpd); wFooter->createHistory(); // initialize global timer @@ -186,9 +174,6 @@ int main(int argc, char **argv) if (Config.startup_screen != myScreen) Config.startup_screen->switchTo(); - Mpd.SetStatusUpdater(Status::update, 0); - Mpd.SetErrorHandler(Status::handleError, 0); - // local variables Key input(0, Key::Standard); timeval past = { 0, 0 }; @@ -198,15 +183,6 @@ int main(int argc, char **argv) if (Config.mouse_support) mousemask(ALL_MOUSE_EVENTS, 0); - Mpd.OrderDataFetching(); - if (Config.jump_to_now_playing_song_at_start) - { - Status::trace(); - int curr_pos = Mpd.GetCurrentSongPos(); - if (curr_pos >= 0) - myPlaylist->main().highlight(curr_pos); - } - # ifndef WIN32 signal(SIGPIPE, sighandler); signal(SIGWINCH, sighandler); @@ -214,66 +190,95 @@ int main(int argc, char **argv) while (!Actions::ExitMainLoop) { - if (!Mpd.Connected()) + try { - if (!wFooter->FDCallbacksListEmpty()) - wFooter->clearFDCallbacksList(); - Statusbar::msg("Attempting to reconnect..."); - if (Mpd.Connect()) + if (!Mpd.Connected()) { - Statusbar::msg("Connected to %s", Mpd.GetHostname().c_str()); - if (Mpd.SupportsIdle()) + wFooter->clearFDCallbacksList(); + try { + Mpd.Connect(); + if (Mpd.Version() < 16) + { + // FIXME workaround so we won't get assertion fails + MpdStatus = Mpd.getStatus(); + Mpd.Disconnect(); + throw MPD::ClientError(MPD_ERROR_STATE, "MPD < 0.16.0 is not supported", false); + } + MpdStatus.clear(); wFooter->addFDCallback(Mpd.GetFD(), Statusbar::Helpers::mpd); - Mpd.OrderDataFetching(); // we need info about new connection + Status::update(-1); // we need info about new connection + + if (Config.jump_to_now_playing_song_at_start) + { + int curr_pos = MpdStatus.currentSongPosition(); + if (curr_pos >= 0) + myPlaylist->main().highlight(curr_pos); + } + + myBrowser->fetchSupportedExtensions(); +# ifdef ENABLE_OUTPUTS + myOutputs->FetchList(); +# endif // ENABLE_OUTPUTS +# ifdef ENABLE_VISUALIZER + myVisualizer->ResetFD(); + if (myScreen == myVisualizer) + myVisualizer->SetFD(); + myVisualizer->FindOutputID(); +# endif // ENABLE_VISUALIZER + + Statusbar::msg("Connected to %s", Mpd.GetHostname().c_str()); + } + catch (MPD::ClientError &e) + { + Status::handleClientError(e); } - ShowMessages = false; -# ifdef ENABLE_VISUALIZER - myVisualizer->ResetFD(); - if (myScreen == myVisualizer) - myVisualizer->SetFD(); - myVisualizer->FindOutputID(); -# endif // ENABLE_VISUALIZER } + + Status::trace(); + + // header stuff + if (((Timer.tv_sec == past.tv_sec && Timer.tv_usec >= past.tv_usec+500000) || Timer.tv_sec > past.tv_sec) + && (myScreen == myPlaylist || myScreen == myBrowser || myScreen == myLyrics) + ) + { + drawHeader(); + past = Timer; + } + // header stuff end + + if (input != Key::noOp) + myScreen->refreshWindow(); + input = Key::read(*wFooter); + + if (input == Key::noOp) + continue; + + auto k = Bindings.get(input); + for (; k.first != k.second; ++k.first) + if (k.first->execute()) + break; + + if (myScreen == myPlaylist) + myPlaylist->EnableHighlighting(); + +# ifdef ENABLE_VISUALIZER + // visualizer sets timeout to 40ms, but since only it needs such small + // value, we should restore defalt one after switching to another screen. + if (wFooter->getTimeout() < 500 + && !(myScreen == myVisualizer || myLockedScreen == myVisualizer || myInactiveScreen == myVisualizer) + ) + wFooter->setTimeout(500); +# endif // ENABLE_VISUALIZER + } + catch (MPD::ClientError &e) + { + Status::handleClientError(e); } - - Status::trace(); - - ShowMessages = true; - - // header stuff - if (((Timer.tv_sec == past.tv_sec && Timer.tv_usec >= past.tv_usec+500000) || Timer.tv_sec > past.tv_sec) - && (myScreen == myPlaylist || myScreen == myBrowser || myScreen == myLyrics) - ) + catch (MPD::ServerError &e) { - drawHeader(); - past = Timer; + Status::handleServerError(e); } - // header stuff end - - if (input != Key::noOp) - myScreen->refreshWindow(); - input = Key::read(*wFooter); - - if (input == Key::noOp) - continue; - - auto k = Bindings.get(input); - for (; k.first != k.second; ++k.first) - if (k.first->execute()) - break; - - if (myScreen == myPlaylist) - myPlaylist->EnableHighlighting(); - -# ifdef ENABLE_VISUALIZER - // visualizer sets timeout to 40ms, but since only it needs such small - // value, we should restore defalt one after switching to another screen. - if (wFooter->getTimeout() < 500 - && !(myScreen == myVisualizer || myLockedScreen == myVisualizer || myInactiveScreen == myVisualizer) - ) - wFooter->setTimeout(500); -# endif // ENABLE_VISUALIZER } return 0; } diff --git a/src/outputs.cpp b/src/outputs.cpp index 6070942..93f6ae9 100644 --- a/src/outputs.cpp +++ b/src/outputs.cpp @@ -46,7 +46,6 @@ Outputs::Outputs() w.setItemDisplayer([](NC::Menu &menu) { menu << Charset::utf8ToLocale(menu.drawn()->value().name()); }); - FetchList(); } void Outputs::switchTo() @@ -73,16 +72,14 @@ void Outputs::enterPressed() { if (w.current().value().isEnabled()) { - if (Mpd.DisableOutput(w.choice())) - Statusbar::msg("Output \"%s\" disabled", w.current().value().name().c_str()); + Mpd.DisableOutput(w.choice()); + Statusbar::msg("Output \"%s\" disabled", w.current().value().name().c_str()); } else { - if (Mpd.EnableOutput(w.choice())) - Statusbar::msg("Output \"%s\" enabled", w.current().value().name().c_str()); + Mpd.EnableOutput(w.choice()); + Statusbar::msg("Output \"%s\" enabled", w.current().value().name().c_str()); } - if (!Mpd.SupportsIdle()) - FetchList(); } void Outputs::mouseButtonPressed(MEVENT me) diff --git a/src/playlist.cpp b/src/playlist.cpp index 0527bac..2868bf4 100644 --- a/src/playlist.cpp +++ b/src/playlist.cpp @@ -43,6 +43,7 @@ Playlist *myPlaylist; bool Playlist::ReloadTotalLength = 0; bool Playlist::ReloadRemaining = false; +unsigned Playlist::Version = 0; namespace {// @@ -217,9 +218,9 @@ MPD::SongList Playlist::getSelectedSongs() MPD::Song Playlist::nowPlayingSong() { MPD::Song s; - if (Mpd.isPlaying()) + if (MpdStatus.playerState() != MPD::psStop) withUnfilteredMenu(w, [this, &s]() { - s = w.at(Mpd.GetCurrentSongPos()).value(); + s = w.at(MpdStatus.currentSongPosition()).value(); }); return s; } @@ -242,8 +243,8 @@ void Playlist::Reverse() Mpd.StartCommandsList(); for (--end; begin < end; ++begin, --end) Mpd.Swap(begin->value().getPosition(), end->value().getPosition()); - if (Mpd.CommitCommandsList()) - Statusbar::msg("Playlist reversed"); + Mpd.CommitCommandsList(); + Statusbar::msg("Playlist reversed"); } void Playlist::EnableHighlighting() @@ -271,7 +272,7 @@ std::string Playlist::TotalLength() if (Config.playlist_show_remaining_time && ReloadRemaining && !w.isFiltered()) { itsRemainingTime = 0; - for (size_t i = Mpd.GetCurrentSongPos(); i < w.size(); ++i) + for (size_t i = MpdStatus.currentSongPosition(); i < w.size(); ++i) itsRemainingTime += w[i].value().getDuration(); ReloadRemaining = false; } @@ -284,7 +285,7 @@ std::string Playlist::TotalLength() size_t real_size = w.size(); w.showFiltered(); if (w.size() != real_size) - result << " (out of " << Mpd.GetPlaylistLength() << ")"; + result << " (out of " << real_size << ")"; } if (itsTotalLength) @@ -307,7 +308,7 @@ void Playlist::PlayNewlyAddedSongs() bool is_filtered = w.isFiltered(); w.showAll(); size_t old_size = w.size(); - Mpd.UpdateStatus(); + //Mpd.UpdateStatus(); if (old_size < w.size()) Mpd.Play(old_size); if (is_filtered) @@ -320,8 +321,8 @@ void Playlist::SetSelectedItemsPriority(int prio) Mpd.StartCommandsList(); for (auto it = list.begin(); it != list.end(); ++it) Mpd.SetPriority((*it)->value(), prio); - if (Mpd.CommitCommandsList()) - Statusbar::msg("Priority set"); + Mpd.CommitCommandsList(); + Statusbar::msg("Priority set"); } bool Playlist::checkForSong(const MPD::Song &s) diff --git a/src/playlist.h b/src/playlist.h index 35aa697..54cb4b5 100644 --- a/src/playlist.h +++ b/src/playlist.h @@ -83,6 +83,7 @@ struct Playlist: Screen>, Filterable, HasSongs, Searchable, void registerHash(size_t hash); void unregisterHash(size_t hash); + static unsigned Version; static bool ReloadTotalLength; static bool ReloadRemaining; diff --git a/src/playlist_editor.cpp b/src/playlist_editor.cpp index 5826441..0a8bb5c 100644 --- a/src/playlist_editor.cpp +++ b/src/playlist_editor.cpp @@ -221,12 +221,10 @@ void PlaylistEditor::AddToPlaylist(bool add_n_play) if (isActiveWindow(Playlists) && !Playlists.empty()) { - if (Mpd.LoadPlaylist(Playlists.current().value())) - { - Statusbar::msg("Playlist \"%s\" loaded", Playlists.current().value().c_str()); - if (add_n_play) - myPlaylist->PlayNewlyAddedSongs(); - } + Mpd.LoadPlaylist(Playlists.current().value()); + Statusbar::msg("Playlist \"%s\" loaded", Playlists.current().value().c_str()); + if (add_n_play) + myPlaylist->PlayNewlyAddedSongs(); } else if (isActiveWindow(Content) && !Content.empty()) addSongToPlaylist(Content.current().value(), add_n_play); diff --git a/src/sel_items_adder.cpp b/src/sel_items_adder.cpp index ab2f589..71b6716 100644 --- a/src/sel_items_adder.cpp +++ b/src/sel_items_adder.cpp @@ -234,61 +234,54 @@ void SelectedItemsAdder::addToExistingPlaylist(const std::string &playlist) cons Mpd.StartCommandsList(); for (auto s = m_selected_items.begin(); s != m_selected_items.end(); ++s) Mpd.AddToPlaylist(playlist, *s); - if (Mpd.CommitCommandsList()) - { - Statusbar::msg("Selected item(s) added to playlist \"%s\"", playlist.c_str()); - switchToPreviousScreen(); - } + Mpd.CommitCommandsList(); + Statusbar::msg("Selected item(s) added to playlist \"%s\"", playlist.c_str()); + switchToPreviousScreen(); } void SelectedItemsAdder::addAtTheEndOfPlaylist() const { - bool success = addSongsToPlaylist(m_selected_items, false); - if (success) - exitSuccessfully(); + addSongsToPlaylist(m_selected_items, false); + exitSuccessfully(); } void SelectedItemsAdder::addAtTheBeginningOfPlaylist() const { - bool success = addSongsToPlaylist(m_selected_items, false, 0); - if (success) - exitSuccessfully(); + addSongsToPlaylist(m_selected_items, false, 0); + exitSuccessfully(); } void SelectedItemsAdder::addAfterCurrentSong() const { - if (!Mpd.isPlaying()) + if (MpdStatus.playerState() == MPD::psStop) return; - size_t pos = Mpd.GetCurrentSongPos(); + size_t pos = MpdStatus.currentSongPosition(); ++pos; - bool success = addSongsToPlaylist(m_selected_items, false, pos); - if (success) - exitSuccessfully(); + addSongsToPlaylist(m_selected_items, false, pos); + exitSuccessfully(); } void SelectedItemsAdder::addAfterCurrentAlbum() const { - if (!Mpd.isPlaying()) + if (MpdStatus.playerState() == MPD::psStop) return; auto &pl = myPlaylist->main(); - size_t pos = Mpd.GetCurrentSongPos(); + size_t pos = MpdStatus.currentSongPosition(); withUnfilteredMenu(pl, [&pos, &pl]() { std::string album = pl[pos].value().getAlbum(); while (pos < pl.size() && pl[pos].value().getAlbum() == album) ++pos; }); - bool success = addSongsToPlaylist(m_selected_items, false, pos); - if (success) - exitSuccessfully(); + addSongsToPlaylist(m_selected_items, false, pos); + exitSuccessfully(); } void SelectedItemsAdder::addAfterHighlightedSong() const { size_t pos = myPlaylist->main().current().value().getPosition(); ++pos; - bool success = addSongsToPlaylist(m_selected_items, false, pos); - if (success) - exitSuccessfully(); + addSongsToPlaylist(m_selected_items, false, pos); + exitSuccessfully(); } void SelectedItemsAdder::cancel() diff --git a/src/server_info.cpp b/src/server_info.cpp index c4ea665..5e7dfb3 100644 --- a/src/server_info.cpp +++ b/src/server_info.cpp @@ -36,19 +36,25 @@ ServerInfo::ServerInfo() { SetDimensions(); w = NC::Scrollpad((COLS-itsWidth)/2, (MainHeight-itsHeight)/2+MainStartY, itsWidth, itsHeight, "MPD server info", Config.main_color, Config.window_border); - Mpd.GetURLHandlers([this](std::string &&handler) { - itsURLHandlers.push_back(handler); - }); - Mpd.GetTagTypes([this](std::string &&tag_type) { - itsTagTypes.push_back(tag_type); - }); } void ServerInfo::switchTo() { using Global::myScreen; if (myScreen != this) + { SwitchTo::execute(this); + + itsURLHandlers.clear(); + itsTagTypes.clear(); + + Mpd.GetURLHandlers([this](std::string &&handler) { + itsURLHandlers.push_back(handler); + }); + Mpd.GetTagTypes([this](std::string &&tag_type) { + itsTagTypes.push_back(tag_type); + }); + } else switchToPreviousScreen(); } diff --git a/src/sort_playlist.cpp b/src/sort_playlist.cpp index 6923b10..a71bde1 100644 --- a/src/sort_playlist.cpp +++ b/src/sort_playlist.cpp @@ -201,11 +201,9 @@ void SortPlaylistDialog::sort() const Statusbar::msg("Sorting..."); Mpd.StartCommandsList(); quick_sort(playlist.begin(), playlist.end()); - if (Mpd.CommitCommandsList()) - { - Statusbar::msg("Playlist sorted"); - switchToPreviousScreen(); - } + Mpd.CommitCommandsList(); + Statusbar::msg("Playlist sorted"); + switchToPreviousScreen(); } void SortPlaylistDialog::cancel() const diff --git a/src/status.cpp b/src/status.cpp index 67693e2..e87a9db 100644 --- a/src/status.cpp +++ b/src/status.cpp @@ -71,68 +71,68 @@ void drawTitle(const MPD::Song &np) } -void Status::trace() +void Status::handleClientError(MPD::ClientError &e) { - gettimeofday(&Timer, 0); - if (Mpd.Connected() && (Mpd.SupportsIdle() || Timer.tv_sec > past.tv_sec)) - { - if (!Mpd.SupportsIdle()) - { - past = Timer; - } - else if (Config.display_bitrate && Global::Timer.tv_sec > past.tv_sec && Mpd.GetState() == MPD::psPlay) - { - // ncmpcpp doesn't fetch status constantly if mpd supports - // idle mode so current song's bitrate is never updated. - // we need to force ncmpcpp to fetch it. - Mpd.OrderDataFetching(); - past = Timer; - } - Mpd.UpdateStatus(); - } - - applyToVisibleWindows(&BaseScreen::update); - - if (isVisible(myPlaylist) - && Timer.tv_sec == myPlaylist->Timer()+Config.playlist_disable_highlight_delay - && myPlaylist->main().isHighlighted() - && Config.playlist_disable_highlight_delay) - { - myPlaylist->main().setHighlighting(false); - myPlaylist->main().refresh(); - } - - Statusbar::tryRedraw(); + if (!e.clearable()) + Mpd.Disconnect(); + Statusbar::msg("NCMPCPP: %s", e.what()); } -void Status::handleError(MPD::Connection * , int errorid, const char *msg, void *) +void Status::handleServerError(MPD::ServerError &e) { - // for errorid: - // - 0-7 bits define MPD_ERROR_* codes, compare them with (0xff & errorid) - // - 8-15 bits define MPD_SERVER_ERROR_* codes, compare them with (errorid >> 8) - if ((errorid >> 8) == MPD_SERVER_ERROR_PERMISSION) + if (e.code() == MPD_SERVER_ERROR_PERMISSION) { - wFooter->setGetStringHelper(0); + wFooter->setGetStringHelper(nullptr); Statusbar::put() << "Password: "; Mpd.SetPassword(wFooter->getString(-1, 0, 1)); - if (Mpd.SendPassword()) - Statusbar::msg("Password accepted"); + Mpd.SendPassword(); + Statusbar::msg("Password accepted"); wFooter->setGetStringHelper(Statusbar::Helpers::getString); } - else if ((errorid >> 8) == MPD_SERVER_ERROR_NO_EXIST && myScreen == myBrowser) + else if (e.code() == MPD_SERVER_ERROR_NO_EXIST && myScreen == myBrowser) { myBrowser->GetDirectory(getParentDirectory(myBrowser->CurrentDir())); myBrowser->refresh(); } - else - Statusbar::msg("MPD: %s", msg); + Statusbar::msg("MPD: %s", e.what()); +} + +void Status::trace() +{ + gettimeofday(&Timer, 0); + if (Mpd.Connected()) + { + if (MpdStatus.playerState() == MPD::psPlay && Global::Timer.tv_sec > past.tv_sec) + { + // update elapsed time/bitrate of the current song + MpdStatus = Mpd.getStatus(); + Status::Changes::elapsedTime(); + wFooter->refresh(); + past = Timer; + } + + applyToVisibleWindows(&BaseScreen::update); + + if (isVisible(myPlaylist) + && Timer.tv_sec == myPlaylist->Timer()+Config.playlist_disable_highlight_delay + && myPlaylist->main().isHighlighted() + && Config.playlist_disable_highlight_delay) + { + myPlaylist->main().setHighlighting(false); + myPlaylist->main().refresh(); + } + + Statusbar::tryRedraw(); + + Mpd.idle(); + } } void Status::Changes::playlist() { myPlaylist->main().clearSearchResults(); withUnfilteredMenuReapplyFilter(myPlaylist->main(), []() { - size_t playlist_length = Mpd.GetPlaylistLength(); + size_t playlist_length = MpdStatus.playlistLength(); if (playlist_length < myPlaylist->main().size()) { auto it = myPlaylist->main().begin()+playlist_length; @@ -142,7 +142,7 @@ void Status::Changes::playlist() myPlaylist->main().resizeList(playlist_length); } - Mpd.GetPlaylistChanges(Mpd.GetOldPlaylistID(), [](MPD::Song &&s) { + Mpd.GetPlaylistChanges(myPlaylist->Version, [](MPD::Song &&s) { size_t pos = s.getPosition(); if (pos < myPlaylist->main().size()) { @@ -155,9 +155,11 @@ void Status::Changes::playlist() myPlaylist->main().addItem(s); myPlaylist->registerHash(s.getHash()); }); + + myPlaylist->Version = MpdStatus.playlistVersion(); }); - if (Mpd.isPlaying()) + if (MpdStatus.playerState() != MPD::psStop) drawTitle(myPlaylist->nowPlayingSong()); Playlist::ReloadTotalLength = true; @@ -201,7 +203,7 @@ void Status::Changes::database() void Status::Changes::playerState() { - MPD::PlayerState state = Mpd.GetState(); + MPD::PlayerState state = MpdStatus.playerState(); switch (state) { case MPD::psUnknown: @@ -214,8 +216,6 @@ void Status::Changes::playerState() drawTitle(myPlaylist->nowPlayingSong()); player_state = Config.new_design ? "[playing]" : "Playing: "; Playlist::ReloadRemaining = true; - if (Mpd.GetOldState() == MPD::psStop) // show track info in status immediately - elapsedTime(); break; } case MPD::psPause: @@ -272,7 +272,7 @@ void Status::Changes::songID() playing_song_scroll_begin = 0; first_line_scroll_begin = 0; second_line_scroll_begin = 0; - if (Mpd.isPlaying()) + if (MpdStatus.playerState() != MPD::psStop) { GNUC_UNUSED int res; if (!Config.execute_on_song_change.empty()) @@ -286,18 +286,17 @@ void Status::Changes::songID() drawTitle(myPlaylist->nowPlayingSong()); if (Config.autocenter_mode && !myPlaylist->main().isFiltered()) - myPlaylist->main().highlight(Mpd.GetCurrentSongPos()); + myPlaylist->main().highlight(MpdStatus.currentSongPosition()); if (Config.now_playing_lyrics && isVisible(myLyrics) && myLyrics->previousScreen() == myPlaylist) myLyrics->ReloadNP = 1; - - elapsedTime(); } + elapsedTime(); } void Status::Changes::elapsedTime() { - if (!Mpd.isPlaying()) + if (MpdStatus.playerState() == MPD::psStop) { if (Statusbar::isUnlocked() && Config.statusbar_visibility) *wFooter << NC::XY(0, 1) << wclrtoeol; @@ -313,20 +312,20 @@ void Status::Changes::elapsedTime() if (Config.display_remaining_time) { tracklength = "-"; - tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime()-Mpd.GetElapsedTime()); + tracklength += MPD::Song::ShowTime(MpdStatus.totalTime()-MpdStatus.elapsedTime()); } else - tracklength = MPD::Song::ShowTime(Mpd.GetElapsedTime()); - if (Mpd.GetTotalTime()) + tracklength = MPD::Song::ShowTime(MpdStatus.elapsedTime()); + if (MpdStatus.totalTime()) { tracklength += "/"; - tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime()); + tracklength += MPD::Song::ShowTime(MpdStatus.totalTime()); } // bitrate here doesn't look good, but it can be moved somewhere else later - if (Config.display_bitrate && Mpd.GetBitrate()) + if (Config.display_bitrate && MpdStatus.kbps()) { tracklength += " "; - tracklength += boost::lexical_cast(Mpd.GetBitrate()); + tracklength += boost::lexical_cast(MpdStatus.kbps()); tracklength += " kbps"; } @@ -357,29 +356,29 @@ void Status::Changes::elapsedTime() } else if (Statusbar::isUnlocked() && Config.statusbar_visibility) { - if (Config.display_bitrate && Mpd.GetBitrate()) + if (Config.display_bitrate && MpdStatus.kbps()) { tracklength += " ["; - tracklength += boost::lexical_cast(Mpd.GetBitrate()); + tracklength += boost::lexical_cast(MpdStatus.kbps()); tracklength += " kbps]"; } tracklength += " ["; - if (Mpd.GetTotalTime()) + if (MpdStatus.totalTime()) { if (Config.display_remaining_time) { tracklength += "-"; - tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime()-Mpd.GetElapsedTime()); + tracklength += MPD::Song::ShowTime(MpdStatus.totalTime()-MpdStatus.elapsedTime()); } else - tracklength += MPD::Song::ShowTime(Mpd.GetElapsedTime()); + tracklength += MPD::Song::ShowTime(MpdStatus.elapsedTime()); tracklength += "/"; - tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime()); + tracklength += MPD::Song::ShowTime(MpdStatus.totalTime()); tracklength += "]"; } else { - tracklength += MPD::Song::ShowTime(Mpd.GetElapsedTime()); + tracklength += MPD::Song::ShowTime(MpdStatus.elapsedTime()); tracklength += "]"; } NC::WBuffer np_song; @@ -389,44 +388,50 @@ void Status::Changes::elapsedTime() *wFooter << NC::Format::Bold << NC::XY(wFooter->getWidth()-tracklength.length(), 1) << tracklength << NC::Format::NoBold; } if (Progressbar::isUnlocked()) - Progressbar::draw(Mpd.GetElapsedTime(), Mpd.GetTotalTime()); + Progressbar::draw(MpdStatus.elapsedTime(), MpdStatus.totalTime()); } -void Status::Changes::repeat() +void Status::Changes::repeat(bool show_msg) { - mpd_repeat = Mpd.GetRepeat() ? 'r' : 0; - Statusbar::msg("Repeat mode is %s", !mpd_repeat ? "off" : "on"); + mpd_repeat = MpdStatus.repeat() ? 'r' : 0; + if (show_msg) + Statusbar::msg("Repeat mode is %s", !mpd_repeat ? "off" : "on"); } -void Status::Changes::random() +void Status::Changes::random(bool show_msg) { - mpd_random = Mpd.GetRandom() ? 'z' : 0; - Statusbar::msg("Random mode is %s", !mpd_random ? "off" : "on"); + mpd_random = MpdStatus.random() ? 'z' : 0; + if (show_msg) + Statusbar::msg("Random mode is %s", !mpd_random ? "off" : "on"); } -void Status::Changes::single() +void Status::Changes::single(bool show_msg) { - mpd_single = Mpd.GetSingle() ? 's' : 0; - Statusbar::msg("Single mode is %s", !mpd_single ? "off" : "on"); + mpd_single = MpdStatus.single() ? 's' : 0; + if (show_msg) + Statusbar::msg("Single mode is %s", !mpd_single ? "off" : "on"); } -void Status::Changes::consume() +void Status::Changes::consume(bool show_msg) { - mpd_consume = Mpd.GetConsume() ? 'c' : 0; - Statusbar::msg("Consume mode is %s", !mpd_consume ? "off" : "on"); + mpd_consume = MpdStatus.consume() ? 'c' : 0; + if (show_msg) + Statusbar::msg("Consume mode is %s", !mpd_consume ? "off" : "on"); } -void Status::Changes::crossfade() +void Status::Changes::crossfade(bool show_msg) { - int crossfade = Mpd.GetCrossfade(); + int crossfade = MpdStatus.crossfade(); mpd_crossfade = crossfade ? 'x' : 0; - Statusbar::msg("Crossfade set to %d seconds", crossfade); + if (show_msg) + Statusbar::msg("Crossfade set to %d seconds", crossfade); } -void Status::Changes::dbUpdateState() +void Status::Changes::dbUpdateState(bool show_msg) { - mpd_db_updating = Mpd.GetDBIsUpdating() ? 'U' : 0; - Statusbar::msg(Mpd.GetDBIsUpdating() ? "Database update started" : "Database update finished"); + mpd_db_updating = MpdStatus.updateID() ? 'U' : 0; + if (show_msg) + Statusbar::msg(MpdStatus.updateID() ? "Database update started" : "Database update finished"); } void Status::Changes::flags() @@ -494,7 +499,7 @@ void Status::Changes::mixer() return; VolumeState = Config.new_design ? " Vol: " : " Volume: "; - int volume = Mpd.GetVolume(); + int volume = MpdStatus.volume(); if (volume < 0) VolumeState += "n/a"; else @@ -515,41 +520,50 @@ void Status::Changes::outputs() # endif // ENABLE_OUTPUTS } -void Status::update(MPD::Connection *, MPD::StatusChanges changes, void *) +void Status::update(int event) { - if (changes.Playlist) - Changes::playlist(); - if (changes.StoredPlaylists) - Changes::storedPlaylists(); - if (changes.Database) + MPD::Status old = MpdStatus; + MpdStatus = Mpd.getStatus(); + + if (event & MPD_IDLE_DATABASE) Changes::database(); - if (changes.PlayerState) + if (event & MPD_IDLE_STORED_PLAYLIST) + Changes::storedPlaylists(); + if (event & MPD_IDLE_PLAYLIST) + Changes::playlist(); + if (event & MPD_IDLE_PLAYER) + { Changes::playerState(); - if (changes.SongID) - Changes::songID(); - if (changes.ElapsedTime) - Changes::elapsedTime(); - if (changes.Repeat) - Changes::repeat(); - if (changes.Random) - Changes::random(); - if (changes.Single) - Changes::single(); - if (changes.Consume) - Changes::consume(); - if (changes.Crossfade) - Changes::crossfade(); - if (changes.DBUpdating) - Changes::dbUpdateState(); - if (changes.StatusFlags) - Changes::flags(); - if (changes.Volume) + if (old.empty() || old.currentSongID() != MpdStatus.currentSongID()) + Changes::songID(); + } + if (event & MPD_IDLE_MIXER) Changes::mixer(); - if (changes.Outputs) + if (event & MPD_IDLE_OUTPUT) Changes::outputs(); + if (event & (MPD_IDLE_UPDATE | MPD_IDLE_OPTIONS)) + { + if (event & MPD_IDLE_UPDATE) + Changes::dbUpdateState(!old.empty()); + if (event & MPD_IDLE_OPTIONS) + { + if (old.empty() || old.repeat() != MpdStatus.repeat()) + Changes::repeat(!old.empty()); + if (old.empty() || old.random() != MpdStatus.random()) + Changes::random(!old.empty()); + if (old.empty() || old.single() != MpdStatus.single()) + Changes::single(!old.empty()); + if (old.empty() || old.consume() != MpdStatus.consume()) + Changes::consume(!old.empty()); + if (old.empty() || old.crossfade() != MpdStatus.crossfade()) + Changes::crossfade(!old.empty()); + } + Changes::flags(); + } - if (changes.PlayerState || (changes.ElapsedTime && (!Config.new_design || Mpd.GetState() == MPD::psPlay))) + if (event & MPD_IDLE_PLAYER) wFooter->refresh(); - if (changes.Playlist || changes.Database || changes.PlayerState || changes.SongID) + + if (event & (MPD_IDLE_PLAYLIST | MPD_IDLE_DATABASE | MPD_IDLE_PLAYER)) applyToVisibleWindows(&BaseScreen::refreshWindow); } diff --git a/src/status.h b/src/status.h index 1b6179f..58e882f 100644 --- a/src/status.h +++ b/src/status.h @@ -26,11 +26,11 @@ namespace Status {// -void trace(); - -void handleError(MPD::Connection * , int errorid, const char *msg, void *); +void handleClientError(MPD::ClientError &e); +void handleServerError(MPD::ServerError &e); -void update(MPD::Connection *, MPD::StatusChanges changes, void *); +void trace(); +void update(int event); namespace Changes {// @@ -40,12 +40,12 @@ void database(); void playerState(); void songID(); void elapsedTime(); -void repeat(); -void random(); -void single(); -void consume(); -void crossfade(); -void dbUpdateState(); +void repeat(bool show_msg); +void random(bool show_msg); +void single(bool show_msg); +void consume(bool show_msg); +void crossfade(bool show_msg); +void dbUpdateState(bool show_msg); void flags(); void mixer(); void outputs(); diff --git a/src/statusbar.cpp b/src/statusbar.cpp index cdd7b36..3e0e13b 100644 --- a/src/statusbar.cpp +++ b/src/statusbar.cpp @@ -38,7 +38,7 @@ bool statusbarAllowUnlock = true; void showMessage(int time, const char *format, va_list list) { - if (Global::ShowMessages && statusbarAllowUnlock) + if (statusbarAllowUnlock) { statusbarLockTime = Global::Timer; statusbarLockDelay = time; @@ -122,10 +122,10 @@ void Statusbar::unlock() else progressbarBlockUpdate = false; } - if (!Mpd.isPlaying()) + if (MpdStatus.playerState() == MPD::psStop) { if (Config.new_design) - Progressbar::draw(Mpd.GetElapsedTime(), Mpd.GetTotalTime()); + Progressbar::draw(MpdStatus.elapsedTime(), MpdStatus.totalTime()); else put() << wclrtoeol; wFooter->refresh(); @@ -150,12 +150,12 @@ void Statusbar::tryRedraw() else progressbarBlockUpdate = !statusbarAllowUnlock; - if (Mpd.GetState() != MPD::psPlay && !statusbarBlockUpdate && !progressbarBlockUpdate) + if (MpdStatus.playerState() != MPD::psStop && !statusbarBlockUpdate && !progressbarBlockUpdate) { if (Config.new_design) - Progressbar::draw(Mpd.GetElapsedTime(), Mpd.GetTotalTime()); + Progressbar::draw(MpdStatus.elapsedTime(), MpdStatus.totalTime()); else - put() << wclrtoeol; + Status::Changes::elapsedTime(); wFooter->refresh(); } } @@ -185,7 +185,7 @@ void Statusbar::msg(int time, const char *format, ...) void Statusbar::Helpers::mpd() { - Mpd.OrderDataFetching(); + Status::update(Mpd.noidle()); } bool Statusbar::Helpers::getString(const std::wstring &) diff --git a/src/visualizer.cpp b/src/visualizer.cpp index 5d02e31..24165a5 100644 --- a/src/visualizer.cpp +++ b/src/visualizer.cpp @@ -57,8 +57,6 @@ Visualizer::Visualizer() m_fftw_output = static_cast(fftw_malloc(sizeof(fftw_complex)*m_fftw_results)); m_fftw_plan = fftw_plan_dft_r2c_1d(m_samples, m_fftw_input, m_fftw_output, FFTW_ESTIMATE); # endif // HAVE_FFTW3_H - - FindOutputID(); } void Visualizer::switchTo() -- 2.11.4.GIT