Merge branch 'lenerd/youtube-dl'
This commit is contained in:
103
src/actions.cpp
103
src/actions.cpp
@@ -25,8 +25,17 @@
|
|||||||
#include <boost/filesystem/operations.hpp>
|
#include <boost/filesystem/operations.hpp>
|
||||||
#include <boost/locale/conversion.hpp>
|
#include <boost/locale/conversion.hpp>
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <boost/process/child.hpp>
|
||||||
|
#include <boost/process/io.hpp>
|
||||||
|
#include <boost/process/pipe.hpp>
|
||||||
|
#include <boost/process/search_path.hpp>
|
||||||
|
#include <boost/property_tree/exceptions.hpp>
|
||||||
|
#include <boost/property_tree/json_parser.hpp>
|
||||||
|
#include <boost/property_tree/ptree.hpp>
|
||||||
|
#include <boost/property_tree/ptree_fwd.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <mpd/tag.h>
|
||||||
|
|
||||||
#include "actions.h"
|
#include "actions.h"
|
||||||
#include "charset.h"
|
#include "charset.h"
|
||||||
@@ -2732,6 +2741,99 @@ void ShowServerInfo::run()
|
|||||||
myServerInfo->switchTo();
|
myServerInfo->switchTo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AddYoutubeDLItem::canBeRun()
|
||||||
|
{
|
||||||
|
return myScreen == myPlaylist;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddYoutubeDLItem::run()
|
||||||
|
{
|
||||||
|
using Global::wFooter;
|
||||||
|
namespace bp = boost::process;
|
||||||
|
namespace pt = boost::property_tree;
|
||||||
|
|
||||||
|
std::string url;
|
||||||
|
{
|
||||||
|
Statusbar::ScopedLock slock;
|
||||||
|
Statusbar::put() << "Add via youtube-dl: ";
|
||||||
|
url = wFooter->prompt();
|
||||||
|
}
|
||||||
|
|
||||||
|
// do nothing if no url is given
|
||||||
|
if (url.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// search the youtube-dl executable in the PATH
|
||||||
|
auto ydl_path = bp::search_path("youtube-dl");
|
||||||
|
if (ydl_path.empty()) {
|
||||||
|
Statusbar::print("youtube-dl was not found in PATH");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Statusbar::printf("Calling youtube-dl with '%1%' ...", url);
|
||||||
|
|
||||||
|
// start youtube-dl in a child process
|
||||||
|
// -j: output as JSON, each playlist item on a separate line
|
||||||
|
// -f bestaudio/best: selects the best available audio-only stream, or
|
||||||
|
// alternatively the best audio+video stream
|
||||||
|
bp::ipstream output;
|
||||||
|
bp::child child_process(ydl_path, url, "-j", "-f", "bestaudio/best", "--playlist-end", "100, bp::std_out > output,
|
||||||
|
bp::std_err > bp::null);
|
||||||
|
|
||||||
|
// extract the URL and metadata from a ptree object and add
|
||||||
|
auto add_song = [] (const pt::ptree& ptree) {
|
||||||
|
auto download_url = ptree.get<std::string>("url");
|
||||||
|
auto title = ptree.get_optional<std::string>("title");
|
||||||
|
auto artist = ptree.get_optional<std::string>("creator");
|
||||||
|
if (!artist.has_value()) {
|
||||||
|
artist = ptree.get_optional<std::string>("uploader");
|
||||||
|
}
|
||||||
|
auto album = ptree.get_optional<std::string>("album");
|
||||||
|
auto id = Mpd.AddSong(download_url);
|
||||||
|
if (id == -1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (title.has_value()) {
|
||||||
|
Mpd.AddTag(id, MPD_TAG_TITLE, *title);
|
||||||
|
}
|
||||||
|
if (artist.has_value()) {
|
||||||
|
Mpd.AddTag(id, MPD_TAG_ARTIST, *artist);
|
||||||
|
}
|
||||||
|
if (album.has_value()) {
|
||||||
|
Mpd.AddTag(id, MPD_TAG_ALBUM, *album);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
pt::ptree ptree;
|
||||||
|
unsigned num_songs_added = 0;
|
||||||
|
|
||||||
|
while (std::getline(output, line)) {
|
||||||
|
try {
|
||||||
|
std::istringstream line_stream(line);
|
||||||
|
pt::read_json(line_stream, ptree);
|
||||||
|
num_songs_added += add_song(ptree);
|
||||||
|
} catch (pt::ptree_error &e) {
|
||||||
|
Statusbar::print("An error occurred while parsing the output of youtube-dl");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Statusbar::printf("Added %1% item(s) to playlist", num_songs_added);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child_process.running()) {
|
||||||
|
child_process.terminate();
|
||||||
|
}
|
||||||
|
child_process.wait();
|
||||||
|
|
||||||
|
auto ec = child_process.exit_code();
|
||||||
|
if (ec == 0) {
|
||||||
|
Statusbar::printf("Added %1% item(s) to playlist", num_songs_added);
|
||||||
|
} else {
|
||||||
|
Statusbar::printf("Added %1% item(s) to playlist (youtube-dl exited with exit code %2%)", num_songs_added, ec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -2870,6 +2972,7 @@ void populateActions()
|
|||||||
insert_action(new Actions::ShowVisualizer());
|
insert_action(new Actions::ShowVisualizer());
|
||||||
insert_action(new Actions::ShowClock());
|
insert_action(new Actions::ShowClock());
|
||||||
insert_action(new Actions::ShowServerInfo());
|
insert_action(new Actions::ShowServerInfo());
|
||||||
|
insert_action(new Actions::AddYoutubeDLItem());
|
||||||
for (size_t i = 0; i < AvailableActions.size(); ++i)
|
for (size_t i = 0; i < AvailableActions.size(); ++i)
|
||||||
{
|
{
|
||||||
if (AvailableActions[i] == nullptr)
|
if (AvailableActions[i] == nullptr)
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ enum class Type
|
|||||||
ShowVisualizer,
|
ShowVisualizer,
|
||||||
ShowClock,
|
ShowClock,
|
||||||
ShowServerInfo,
|
ShowServerInfo,
|
||||||
|
AddYoutubeDLItem,
|
||||||
_numberOfActions // needed to dynamically calculate size of action array
|
_numberOfActions // needed to dynamically calculate size of action array
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1425,6 +1426,15 @@ private:
|
|||||||
virtual void run() override;
|
virtual void run() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AddYoutubeDLItem: BaseAction
|
||||||
|
{
|
||||||
|
AddYoutubeDLItem(): BaseAction(Type::AddYoutubeDLItem, "add_youtube-dl_item") { }
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual bool canBeRun() override;
|
||||||
|
virtual void run() override;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // NCMPCPP_ACTIONS_H
|
#endif // NCMPCPP_ACTIONS_H
|
||||||
|
|||||||
@@ -762,6 +762,8 @@ void BindingsConfiguration::generateDefaults()
|
|||||||
bind(k, Actions::Type::SetSelectedItemsPriority);
|
bind(k, Actions::Type::SetSelectedItemsPriority);
|
||||||
if (notBound(k = stringToKey("q")))
|
if (notBound(k = stringToKey("q")))
|
||||||
bind(k, Actions::Type::Quit);
|
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 *BindingsConfiguration::findCommand(const std::string &name)
|
||||||
|
|||||||
@@ -567,6 +567,10 @@ int Connection::AddSong(const Song &s, int pos)
|
|||||||
return AddSong((!s.isFromDatabase() ? "file://" : "") + s.getURI(), pos);
|
return AddSong((!s.isFromDatabase() ? "file://" : "") + s.getURI(), pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Connection::AddTag(int id, mpd_tag_type tag, const std::string &value) {
|
||||||
|
mpd_run_add_tag_id(m_connection.get(), id, tag, value.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
bool Connection::Add(const std::string &path)
|
bool Connection::Add(const std::string &path)
|
||||||
{
|
{
|
||||||
bool result;
|
bool result;
|
||||||
|
|||||||
@@ -552,6 +552,7 @@ struct Connection
|
|||||||
|
|
||||||
int AddSong(const std::string &, int = -1); // returns id of added song
|
int AddSong(const std::string &, int = -1); // returns id of added song
|
||||||
int AddSong(const Song &, int = -1); // returns id of added song
|
int AddSong(const Song &, int = -1); // returns id of added song
|
||||||
|
void AddTag(int id, mpd_tag_type, const std::string &);
|
||||||
bool AddRandomTag(mpd_tag_type, size_t, std::mt19937 &rng);
|
bool AddRandomTag(mpd_tag_type, size_t, std::mt19937 &rng);
|
||||||
bool AddRandomSongs(size_t number, const std::string &random_exclude_pattern, std::mt19937 &rng);
|
bool AddRandomSongs(size_t number, const std::string &random_exclude_pattern, std::mt19937 &rng);
|
||||||
bool Add(const std::string &path);
|
bool Add(const std::string &path);
|
||||||
|
|||||||
@@ -258,6 +258,7 @@ void write_bindings(NC::Scrollpad &w)
|
|||||||
key(w, Type::ReversePlaylist, "Reverse range");
|
key(w, Type::ReversePlaylist, "Reverse range");
|
||||||
key(w, Type::JumpToPlayingSong, "Jump to current song");
|
key(w, Type::JumpToPlayingSong, "Jump to current song");
|
||||||
key(w, Type::TogglePlayingSongCentering, "Toggle playing song centering");
|
key(w, Type::TogglePlayingSongCentering, "Toggle playing song centering");
|
||||||
|
key(w, Type::AddYoutubeDLItem, "Add items via youtube-dl");
|
||||||
|
|
||||||
key_section(w, "Browser");
|
key_section(w, "Browser");
|
||||||
key(w, Type::EnterDirectory, "Enter directory");
|
key(w, Type::EnterDirectory, "Enter directory");
|
||||||
|
|||||||
Reference in New Issue
Block a user