Merge branch 'lenerd/youtube-dl'

This commit is contained in:
Salastil
2023-08-27 03:25:28 -04:00
6 changed files with 121 additions and 0 deletions

View File

@@ -25,8 +25,17 @@
#include <boost/filesystem/operations.hpp>
#include <boost/locale/conversion.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 <iostream>
#include <mpd/tag.h>
#include "actions.h"
#include "charset.h"
@@ -2732,6 +2741,99 @@ void ShowServerInfo::run()
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 {
@@ -2870,6 +2972,7 @@ void populateActions()
insert_action(new Actions::ShowVisualizer());
insert_action(new Actions::ShowClock());
insert_action(new Actions::ShowServerInfo());
insert_action(new Actions::AddYoutubeDLItem());
for (size_t i = 0; i < AvailableActions.size(); ++i)
{
if (AvailableActions[i] == nullptr)

View File

@@ -164,6 +164,7 @@ enum class Type
ShowVisualizer,
ShowClock,
ShowServerInfo,
AddYoutubeDLItem,
_numberOfActions // needed to dynamically calculate size of action array
};
@@ -1425,6 +1426,15 @@ private:
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

View File

@@ -762,6 +762,8 @@ void BindingsConfiguration::generateDefaults()
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)

View File

@@ -567,6 +567,10 @@ int Connection::AddSong(const Song &s, int 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 result;

View File

@@ -552,6 +552,7 @@ struct Connection
int AddSong(const std::string &, 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 AddRandomSongs(size_t number, const std::string &random_exclude_pattern, std::mt19937 &rng);
bool Add(const std::string &path);

View File

@@ -258,6 +258,7 @@ void write_bindings(NC::Scrollpad &w)
key(w, Type::ReversePlaylist, "Reverse range");
key(w, Type::JumpToPlayingSong, "Jump to current song");
key(w, Type::TogglePlayingSongCentering, "Toggle playing song centering");
key(w, Type::AddYoutubeDLItem, "Add items via youtube-dl");
key_section(w, "Browser");
key(w, Type::EnterDirectory, "Enter directory");