Files
ncmpcpp/src/bindings.cpp
Lennart Braun 523b7d4135 Add action to add streams via youtube-dl
youtube-dl [0] is a "command-line program to download videos from
YouTube.com" and many other websites, e.g. media libraries of public
broadcasters.

This commit adds a new action to ncmpcpp's playlist view: Pressing 'D',
prompts for the URL of a website.  The input is passed to youtube-dl to
retrieve the URL of the audio stream and available metadata.  If
successful, the URL is added to the MPD playlist.

[0]: https://github.com/ytdl-org/youtube-dl
2021-04-22 21:03:32 +02:00

792 lines
23 KiB
C++

/***************************************************************************
* Copyright (C) 2008-2021 by Andrzej Rybczak *
* andrzej@rybczak.net *
* *
* 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 <boost/algorithm/string/trim.hpp>
#include <fstream>
#include <iostream>
#include "global.h"
#include "bindings.h"
#include "utility/string.h"
#include "utility/wide_string.h"
BindingsConfiguration Bindings;
namespace {
void warning(const char *msg)
{
std::cerr << "WARNING: " << msg << "\n";
}
NC::Key::Type stringToKey(const std::string &s);
NC::Key::Type stringToSpecialKey(const std::string &s)
{
NC::Key::Type result = NC::Key::None;
if (!s.compare(0, 4, "ctrl") && s.length() == 6 && s[4] == '-')
{
if (s[5] >= 'a' && s[5] <= 'z')
result = NC::Key::Ctrl_A + (s[5] - 'a');
else if (s[5] == '[')
result = NC::Key::Ctrl_LeftBracket;
else if (s[5] == '\\')
result = NC::Key::Ctrl_Backslash;
else if (s[5] == ']')
result = NC::Key::Ctrl_RightBracket;
else if (s[5] == '^')
result = NC::Key::Ctrl_Caret;
else if (s[5] == '_')
result = NC::Key::Ctrl_Underscore;
}
else if (!s.compare(0, 3, "alt") && s.length() > 3 && s[3] == '-')
{
result = NC::Key::Alt | stringToKey(s.substr(4));
}
else if (!s.compare(0, 4, "ctrl") && s.length() > 4 && s[4] == '-')
{
result = NC::Key::Ctrl | stringToKey(s.substr(5));
}
else if (!s.compare(0, 5, "shift") && s.length() > 5 && s[5] == '-')
{
result = NC::Key::Shift | stringToKey(s.substr(6));
}
else if (!s.compare("escape"))
result = NC::Key::Escape;
else if (!s.compare("mouse"))
result = NC::Key::Mouse;
else if (!s.compare("up"))
result = NC::Key::Up;
else if (!s.compare("down"))
result = NC::Key::Down;
else if (!s.compare("page_up"))
result = NC::Key::PageUp;
else if (!s.compare("page_down"))
result = NC::Key::PageDown;
else if (!s.compare("home"))
result = NC::Key::Home;
else if (!s.compare("end"))
result = NC::Key::End;
else if (!s.compare("space"))
result = NC::Key::Space;
else if (!s.compare("enter"))
result = NC::Key::Enter;
else if (!s.compare("insert"))
result = NC::Key::Insert;
else if (!s.compare("delete"))
result = NC::Key::Delete;
else if (!s.compare("left"))
result = NC::Key::Left;
else if (!s.compare("right"))
result = NC::Key::Right;
else if (!s.compare("tab"))
result = NC::Key::Tab;
else if ((s.length() == 2 || s.length() == 3) && s[0] == 'f')
{
int n = atoi(s.c_str() + 1);
if (n >= 1 && n <= 12)
result = NC::Key::F1 + n - 1;
}
else if (!s.compare("backspace"))
result = NC::Key::Backspace;
return result;
}
NC::Key::Type stringToKey(const std::string &s)
{
NC::Key::Type result = stringToSpecialKey(s);
if (result == NC::Key::None)
{
std::wstring ws = ToWString(s);
if (ws.length() == 1)
result = ws[0];
}
return result;
}
template <typename F>
std::shared_ptr<Actions::BaseAction> parseActionLine(const std::string &line, F error)
{
std::shared_ptr<Actions::BaseAction> result;
size_t i = 0;
for (; i < line.size() && !isspace(line[i]); ++i) { }
if (i == line.size()) // only action name
{
if (line == "set_visualizer_sample_multiplier")
{
warning("action 'set_visualizer_sample_multiplier' is deprecated"
" and will be removed in 0.9");
result = Actions::get_(Actions::Type::Dummy);
}
else
result = Actions::get_(line);
}
else // there is something else
{
std::string action_name = line.substr(0, i);
if (action_name == "push_character")
{
// push single character into input queue
std::string arg = getEnclosedString(line, '"', '"', 0);
NC::Key::Type k = stringToSpecialKey(arg);
if (k != NC::Key::None)
result = std::static_pointer_cast<Actions::BaseAction>(
std::make_shared<Actions::PushCharacters>(
&Global::wFooter,
std::vector<NC::Key::Type>{k}));
else
error() << "invalid character passed to push_character: '" << arg << "'\n";
}
else if (action_name == "push_characters")
{
// push sequence of characters into input queue
std::string arg = getEnclosedString(line, '"', '"', 0);
if (!arg.empty())
{
// if char is signed, erase 1s from char -> int conversion
for (auto it = arg.begin(); it != arg.end(); ++it)
*it &= 0xff;
result = std::static_pointer_cast<Actions::BaseAction>(
std::make_shared<Actions::PushCharacters>(
&Global::wFooter,
std::vector<NC::Key::Type>{arg.begin(), arg.end()}));
}
else
error() << "empty argument passed to push_characters\n";
}
else if (action_name == "require_screen")
{
// require screen of given type
std::string arg = getEnclosedString(line, '"', '"', 0);
ScreenType screen_type = stringToScreenType(arg);
if (screen_type != ScreenType::Unknown)
result = std::static_pointer_cast<Actions::BaseAction>(
std::make_shared<Actions::RequireScreen>(screen_type));
else
error() << "unknown screen passed to require_screen: '" << arg << "'\n";
}
else if (action_name == "require_runnable")
{
// require that given action is runnable
std::string arg = getEnclosedString(line, '"', '"', 0);
auto action = Actions::get_(arg);
if (action)
result = std::static_pointer_cast<Actions::BaseAction>(
std::make_shared<Actions::RequireRunnable>(action));
else
error() << "unknown action passed to require_runnable: '" << arg << "'\n";
}
else if (action_name == "run_external_command")
{
std::string command = getEnclosedString(line, '"', '"', 0);
if (!command.empty())
result = std::static_pointer_cast<Actions::BaseAction>(
std::make_shared<Actions::RunExternalCommand>(std::move(command)));
else
error() << "empty command passed to run_external_command\n";
}
else if (action_name == "run_external_console_command")
{
std::string command = getEnclosedString(line, '"', '"', 0);
if (!command.empty())
result = std::static_pointer_cast<Actions::BaseAction>(
std::make_shared<Actions::RunExternalConsoleCommand>(std::move(command)));
else
error() << "empty command passed to run_external_console_command\n";
}
}
return result;
}
}
NC::Key::Type readKey(NC::Window &w)
{
NC::Key::Type result = NC::Key::None;
std::string tmp;
NC::Key::Type input;
bool alt_pressed = false;
while (true)
{
input = w.readKey();
if (input == NC::Key::None)
break;
if (input & NC::Key::Alt)
{
// Complete the key and reapply the mask at the end.
alt_pressed = true;
input &= ~NC::Key::Alt;
}
if (input > 255) // NC special character
{
result = input;
break;
}
else
{
wchar_t wc;
tmp += input;
size_t conv_res = mbrtowc(&wc, tmp.c_str(), MB_CUR_MAX, 0);
if (conv_res == size_t(-1)) // incomplete multibyte character
continue;
else if (conv_res == size_t(-2)) // garbage character sequence
break;
else // character complete
{
result = wc;
break;
}
}
}
if (alt_pressed)
result |= NC::Key::Alt;
return result;
}
std::wstring keyToWString(const NC::Key::Type key)
{
std::wstring result;
if (key == NC::Key::Tab)
result += L"Tab";
else if (key == NC::Key::Enter)
result += L"Enter";
else if (key == NC::Key::Escape)
result += L"Escape";
else if (key >= NC::Key::Ctrl_A && key <= NC::Key::Ctrl_Z)
{
result += L"Ctrl-";
result += 'A' + (key - NC::Key::Ctrl_A);
}
else if (key == NC::Key::Ctrl_LeftBracket)
result += L"Ctrl-[";
else if (key == NC::Key::Ctrl_Backslash)
result += L"Ctrl-\\";
else if (key == NC::Key::Ctrl_RightBracket)
result += L"Ctrl-]";
else if (key == NC::Key::Ctrl_Caret)
result += L"Ctrl-^";
else if (key == NC::Key::Ctrl_Underscore)
result += L"Ctrl-_";
else if (key & NC::Key::Alt)
{
result += L"Alt-";
result += keyToWString(key & ~NC::Key::Alt);
}
else if (key & NC::Key::Ctrl)
{
result += L"Ctrl-";
result += keyToWString(key & ~NC::Key::Ctrl);
}
else if (key & NC::Key::Shift)
{
result += L"Shift-";
result += keyToWString(key & ~NC::Key::Shift);
}
else if (key == NC::Key::Space)
result += L"Space";
else if (key == NC::Key::Backspace)
result += L"Backspace";
else if (key == NC::Key::Insert)
result += L"Insert";
else if (key == NC::Key::Delete)
result += L"Delete";
else if (key == NC::Key::Home)
result += L"Home";
else if (key == NC::Key::End)
result += L"End";
else if (key == NC::Key::PageUp)
result += L"PageUp";
else if (key == NC::Key::PageDown)
result += L"PageDown";
else if (key == NC::Key::Up)
result += L"Up";
else if (key == NC::Key::Down)
result += L"Down";
else if (key == NC::Key::Left)
result += L"Left";
else if (key == NC::Key::Right)
result += L"Right";
else if (key == NC::Key::EoF)
result += L"EoF";
else if (key >= NC::Key::F1 && key <= NC::Key::F9)
{
result += L"F";
result += '1' + (key - NC::Key::F1);
}
else if (key >= NC::Key::F10 && key <= NC::Key::F12)
{
result += L"F1";
result += '0' + (key - NC::Key::F10);
}
else
result += std::wstring(1, key);
return result;
}
bool BindingsConfiguration::read(const std::string &file)
{
enum class InProgress { None, Command, Key };
bool result = true;
std::ifstream f(file);
if (!f.is_open())
return result;
// shared variables
InProgress in_progress = InProgress::None;
size_t line_no = 0;
std::string line;
Binding::ActionChain actions;
// def_key specific variables
NC::Key::Type key = NC::Key::None;
std::string strkey;
// def_command specific variables
bool cmd_immediate = false;
std::string cmd_name;
auto error = [&]() -> std::ostream & {
std::cerr << file << ":" << line_no << ": error: ";
in_progress = InProgress::None;
result = false;
return std::cerr;
};
auto bind_in_progress = [&]() -> bool {
if (in_progress == InProgress::Command)
{
if (!actions.empty())
{
m_commands.insert(std::make_pair(cmd_name, Command(std::move(actions), cmd_immediate)));
actions.clear();
return true;
}
else
{
error() << "definition of command '" << cmd_name << "' cannot be empty\n";
return false;
}
}
else if (in_progress == InProgress::Key)
{
if (!actions.empty())
{
bind(key, actions);
actions.clear();
return true;
}
else
{
error() << "definition of key '" << strkey << "' cannot be empty\n";
return false;
}
}
return true;
};
const char def_command[] = "def_command";
const char def_key[] = "def_key";
while (!f.eof() && ++line_no)
{
getline(f, line);
if (line.empty() || line[0] == '#')
continue;
// beginning of command definition
if (!line.compare(0, const_strlen(def_command), def_command))
{
if (!bind_in_progress())
break;
in_progress = InProgress::Command;
cmd_name = getEnclosedString(line, '"', '"', 0);
if (cmd_name.empty())
{
error() << "command must have non-empty name\n";
break;
}
if (m_commands.find(cmd_name) != m_commands.end())
{
error() << "redefinition of command '" << cmd_name << "'\n";
break;
}
std::string cmd_type = getEnclosedString(line, '[', ']', 0);
if (cmd_type == "immediate")
cmd_immediate = true;
else if (cmd_type == "deferred")
cmd_immediate = false;
else
{
error() << "invalid type of command: '" << cmd_type << "'\n";
break;
}
}
// beginning of key definition
else if (!line.compare(0, const_strlen(def_key), def_key))
{
if (!bind_in_progress())
break;
in_progress = InProgress::Key;
strkey = getEnclosedString(line, '"', '"', 0);
key = stringToKey(strkey);
if (key == NC::Key::None)
{
error() << "invalid key: '" << strkey << "'\n";
break;
}
}
else if (isspace(line[0])) // name of action to be bound
{
boost::trim(line);
auto action = parseActionLine(line, error);
if (action)
actions.push_back(action);
else
{
error() << "unknown action: '" << line << "'\n";
break;
}
}
else
{
error() << "invalid line: '" << line << "'\n";
break;
}
}
bind_in_progress();
f.close();
return result;
}
bool BindingsConfiguration::read(const std::vector<std::string> &binding_paths)
{
return std::all_of(
binding_paths.begin(),
binding_paths.end(),
[&](const std::string &binding_path) {
return read(binding_path);
}
);
}
void BindingsConfiguration::generateDefaults()
{
NC::Key::Type k = NC::Key::None;
bind(NC::Key::EoF, Actions::Type::Quit);
if (notBound(k = stringToKey("mouse")))
bind(k, Actions::Type::MouseEvent);
if (notBound(k = stringToKey("up")))
bind(k, Actions::Type::ScrollUp);
if (notBound(k = stringToKey("shift-up")))
bind(k, Binding::ActionChain({Actions::get_(Actions::Type::SelectItem), Actions::get_(Actions::Type::ScrollUp)}));
if (notBound(k = stringToKey("down")))
bind(k, Actions::Type::ScrollDown);
if (notBound(k = stringToKey("shift-down")))
bind(k, Binding::ActionChain({Actions::get_(Actions::Type::SelectItem), Actions::get_(Actions::Type::ScrollDown)}));
if (notBound(k = stringToKey("[")))
bind(k, Actions::Type::ScrollUpAlbum);
if (notBound(k = stringToKey("]")))
bind(k, Actions::Type::ScrollDownAlbum);
if (notBound(k = stringToKey("{")))
bind(k, Actions::Type::ScrollUpArtist);
if (notBound(k = stringToKey("}")))
bind(k, Actions::Type::ScrollDownArtist);
if (notBound(k = stringToKey("page_up")))
bind(k, Actions::Type::PageUp);
if (notBound(k = stringToKey("page_down")))
bind(k, Actions::Type::PageDown);
if (notBound(k = stringToKey("home")))
bind(k, Actions::Type::MoveHome);
if (notBound(k = stringToKey("end")))
bind(k, Actions::Type::MoveEnd);
if (notBound(k = stringToKey("insert")))
bind(k, Actions::Type::SelectItem);
if (notBound(k = stringToKey("enter")))
{
bind(k, Actions::Type::EnterDirectory);
bind(k, Actions::Type::ToggleOutput);
bind(k, Actions::Type::RunAction);
bind(k, Actions::Type::PlayItem);
}
if (notBound(k = stringToKey("space")))
{
bind(k, Actions::Type::AddItemToPlaylist);
bind(k, Actions::Type::ToggleLyricsUpdateOnSongChange);
bind(k, Actions::Type::ToggleVisualizationType);
}
if (notBound(k = stringToKey("delete")))
{
bind(k, Actions::Type::DeletePlaylistItems);
bind(k, Actions::Type::DeleteBrowserItems);
bind(k, Actions::Type::DeleteStoredPlaylist);
}
if (notBound(k = stringToKey("right")))
{
bind(k, Actions::Type::NextColumn);
bind(k, Actions::Type::SlaveScreen);
bind(k, Actions::Type::VolumeUp);
}
if (notBound(k = stringToKey("+")))
bind(k, Actions::Type::VolumeUp);
if (notBound(k = stringToKey("left")))
{
bind(k, Actions::Type::PreviousColumn);
bind(k, Actions::Type::MasterScreen);
bind(k, Actions::Type::VolumeDown);
}
if (notBound(k = stringToKey("-")))
bind(k, Actions::Type::VolumeDown);
if (notBound(k = stringToKey(":")))
bind(k, Actions::Type::ExecuteCommand);
if (notBound(k = stringToKey("tab")))
bind(k, Actions::Type::NextScreen);
if (notBound(k = stringToKey("shift-tab")))
bind(k, Actions::Type::PreviousScreen);
if (notBound(k = stringToKey("f1")))
bind(k, Actions::Type::ShowHelp);
if (notBound(k = stringToKey("1")))
bind(k, Actions::Type::ShowPlaylist);
if (notBound(k = stringToKey("2")))
{
bind(k, Actions::Type::ShowBrowser);
bind(k, Actions::Type::ChangeBrowseMode);
}
if (notBound(k = stringToKey("3")))
{
bind(k, Actions::Type::ShowSearchEngine);
bind(k, Actions::Type::ResetSearchEngine);
}
if (notBound(k = stringToKey("4")))
{
bind(k, Actions::Type::ShowMediaLibrary);
bind(k, Actions::Type::ToggleMediaLibraryColumnsMode);
}
if (notBound(k = stringToKey("5")))
bind(k, Actions::Type::ShowPlaylistEditor);
if (notBound(k = stringToKey("6")))
bind(k, Actions::Type::ShowTagEditor);
if (notBound(k = stringToKey("7")))
bind(k, Actions::Type::ShowOutputs);
if (notBound(k = stringToKey("8")))
bind(k, Actions::Type::ShowVisualizer);
if (notBound(k = stringToKey("=")))
bind(k, Actions::Type::ShowClock);
if (notBound(k = stringToKey("@")))
bind(k, Actions::Type::ShowServerInfo);
if (notBound(k = stringToKey("s")))
bind(k, Actions::Type::Stop);
if (notBound(k = stringToKey("p")))
bind(k, Actions::Type::Pause);
if (notBound(k = stringToKey(">")))
bind(k, Actions::Type::Next);
if (notBound(k = stringToKey("<")))
bind(k, Actions::Type::Previous);
if (notBound(k = stringToKey("ctrl-h")))
{
bind(k, Actions::Type::JumpToParentDirectory);
bind(k, Actions::Type::ReplaySong);
}
if (notBound(k = stringToKey("backspace")))
{
bind(k, Actions::Type::JumpToParentDirectory);
bind(k, Actions::Type::ReplaySong);
bind(k, Actions::Type::Play);
}
if (notBound(k = stringToKey("f")))
bind(k, Actions::Type::SeekForward);
if (notBound(k = stringToKey("b")))
bind(k, Actions::Type::SeekBackward);
if (notBound(k = stringToKey("r")))
bind(k, Actions::Type::ToggleRepeat);
if (notBound(k = stringToKey("z")))
bind(k, Actions::Type::ToggleRandom);
if (notBound(k = stringToKey("y")))
{
bind(k, Actions::Type::SaveTagChanges);
bind(k, Actions::Type::StartSearching);
bind(k, Actions::Type::ToggleSingle);
}
if (notBound(k = stringToKey("R")))
bind(k, Actions::Type::ToggleConsume);
if (notBound(k = stringToKey("Y")))
bind(k, Actions::Type::ToggleReplayGainMode);
if (notBound(k = stringToKey("T")))
bind(k, Actions::Type::ToggleAddMode);
if (notBound(k = stringToKey("|")))
bind(k, Actions::Type::ToggleMouse);
if (notBound(k = stringToKey("#")))
bind(k, Actions::Type::ToggleBitrateVisibility);
if (notBound(k = stringToKey("Z")))
bind(k, Actions::Type::Shuffle);
if (notBound(k = stringToKey("x")))
bind(k, Actions::Type::ToggleCrossfade);
if (notBound(k = stringToKey("X")))
bind(k, Actions::Type::SetCrossfade);
if (notBound(k = stringToKey("u")))
bind(k, Actions::Type::UpdateDatabase);
if (notBound(k = stringToKey("ctrl-s")))
{
bind(k, Actions::Type::SortPlaylist);
bind(k, Actions::Type::ToggleBrowserSortMode);
bind(k, Actions::Type::ToggleMediaLibrarySortMode);
}
if (notBound(k = stringToKey("ctrl-r")))
bind(k, Actions::Type::ReversePlaylist);
if (notBound(k = stringToKey("ctrl-f")))
bind(k, Actions::Type::ApplyFilter);
if (notBound(k = stringToKey("ctrl-_")))
bind(k, Actions::Type::SelectFoundItems);
if (notBound(k = stringToKey("/")))
{
bind(k, Actions::Type::Find);
bind(k, Actions::Type::FindItemForward);
}
if (notBound(k = stringToKey("?")))
{
bind(k, Actions::Type::Find);
bind(k, Actions::Type::FindItemBackward);
}
if (notBound(k = stringToKey(".")))
bind(k, Actions::Type::NextFoundItem);
if (notBound(k = stringToKey(",")))
bind(k, Actions::Type::PreviousFoundItem);
if (notBound(k = stringToKey("w")))
bind(k, Actions::Type::ToggleFindMode);
if (notBound(k = stringToKey("e")))
{
bind(k, Actions::Type::EditSong);
bind(k, Actions::Type::EditLibraryTag);
bind(k, Actions::Type::EditLibraryAlbum);
bind(k, Actions::Type::EditDirectoryName);
bind(k, Actions::Type::EditPlaylistName);
bind(k, Actions::Type::EditLyrics);
}
if (notBound(k = stringToKey("i")))
bind(k, Actions::Type::ShowSongInfo);
if (notBound(k = stringToKey("I")))
bind(k, Actions::Type::ShowArtistInfo);
if (notBound(k = stringToKey("g")))
bind(k, Actions::Type::JumpToPositionInSong);
if (notBound(k = stringToKey("l")))
bind(k, Actions::Type::ShowLyrics);
if (notBound(k = stringToKey("ctrl-v")))
bind(k, Actions::Type::SelectRange);
if (notBound(k = stringToKey("v")))
bind(k, Actions::Type::ReverseSelection);
if (notBound(k = stringToKey("V")))
bind(k, Actions::Type::RemoveSelection);
if (notBound(k = stringToKey("B")))
bind(k, Actions::Type::SelectAlbum);
if (notBound(k = stringToKey("a")))
bind(k, Actions::Type::AddSelectedItems);
if (notBound(k = stringToKey("c")))
{
bind(k, Actions::Type::ClearPlaylist);
bind(k, Actions::Type::ClearMainPlaylist);
}
if (notBound(k = stringToKey("C")))
{
bind(k, Actions::Type::CropPlaylist);
bind(k, Actions::Type::CropMainPlaylist);
}
if (notBound(k = stringToKey("m")))
{
bind(k, Actions::Type::MoveSortOrderUp);
bind(k, Actions::Type::MoveSelectedItemsUp);
}
if (notBound(k = stringToKey("n")))
{
bind(k, Actions::Type::MoveSortOrderDown);
bind(k, Actions::Type::MoveSelectedItemsDown);
}
if (notBound(k = stringToKey("M")))
bind(k, Actions::Type::MoveSelectedItemsTo);
if (notBound(k = stringToKey("A")))
bind(k, Actions::Type::Add);
if (notBound(k = stringToKey("S")))
bind(k, Actions::Type::SavePlaylist);
if (notBound(k = stringToKey("o")))
bind(k, Actions::Type::JumpToPlayingSong);
if (notBound(k = stringToKey("G")))
{
bind(k, Actions::Type::JumpToBrowser);
bind(k, Actions::Type::JumpToPlaylistEditor);
}
if (notBound(k = stringToKey("~")))
bind(k, Actions::Type::JumpToMediaLibrary);
if (notBound(k = stringToKey("E")))
bind(k, Actions::Type::JumpToTagEditor);
if (notBound(k = stringToKey("U")))
bind(k, Actions::Type::TogglePlayingSongCentering);
if (notBound(k = stringToKey("P")))
bind(k, Actions::Type::ToggleDisplayMode);
if (notBound(k = stringToKey("\\")))
bind(k, Actions::Type::ToggleInterface);
if (notBound(k = stringToKey("!")))
bind(k, Actions::Type::ToggleSeparatorsBetweenAlbums);
if (notBound(k = stringToKey("L")))
bind(k, Actions::Type::ToggleLyricsFetcher);
if (notBound(k = stringToKey("F")))
bind(k, Actions::Type::FetchLyricsInBackground);
if (notBound(k = stringToKey("alt-l")))
bind(k, Actions::Type::ToggleFetchingLyricsInBackground);
if (notBound(k = stringToKey("ctrl-l")))
bind(k, Actions::Type::ToggleScreenLock);
if (notBound(k = stringToKey("`")))
{
bind(k, Actions::Type::ToggleLibraryTagType);
bind(k, Actions::Type::RefetchLyrics);
bind(k, Actions::Type::AddRandomItems);
}
if (notBound(k = stringToKey("ctrl-p")))
bind(k, Actions::Type::SetSelectedItemsPriority);
if (notBound(k = stringToKey("q")))
bind(k, Actions::Type::Quit);
if (notBound(k = stringToKey("D")))
bind(k, Actions::Type::AddYoutubeDLItem);
}
const Command *BindingsConfiguration::findCommand(const std::string &name)
{
const Command *ptr = nullptr;
auto it = m_commands.find(name);
if (it != m_commands.end())
ptr = &it->second;
return ptr;
}
BindingsConfiguration::BindingIteratorPair BindingsConfiguration::get(const NC::Key::Type &k)
{
std::pair<BindingIterator, BindingIterator> result;
auto it = m_bindings.find(k);
if (it != m_bindings.end()) {
result.first = it->second.begin();
result.second = it->second.end();
} else {
auto list_end = m_bindings.begin()->second.end();
result.first = list_end;
result.second = list_end;
}
return result;
}