Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions src/actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -2746,6 +2755,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", bp::std_out > output,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bp::child child_process(ydl_path, url, "-j", "-f", "bestaudio/best","--playlist-end", "100" , bp::std_o ++ut > output,

Just a quality of life change here, when adding autogenerated playlist it will add 1000 items to ncmpcpp. Adding in --playlist end 100 will trim that number down to a more sensible 100. The videos are only good for something like 3-6 hours before they need to expire and need to be regenerated so it is effectively impossible to listen to the full 1000 item playlist anyway and it takes a good bit of time for it to generate such a huge playlist.

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()) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does it try to add multiple things from the database?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

youtube-dl has support for playlists and outputs one line of JSON per title. Each of those is parsed and added to the MPD playlist.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but why the output of youtube-dl is used to add items from the database to queue? It seems unexpected that it would do anything else than add youtube links to queue.

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 {
Expand Down Expand Up @@ -2884,6 +2986,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)
Expand Down
10 changes: 10 additions & 0 deletions src/actions.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ enum class Type
ShowVisualizer,
ShowClock,
ShowServerInfo,
AddYoutubeDLItem,
_numberOfActions // needed to dynamically calculate size of action array
};

Expand Down Expand Up @@ -1425,6 +1426,15 @@ struct ShowServerInfo: BaseAction
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
2 changes: 2 additions & 0 deletions src/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions src/mpdpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/mpdpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/screens/help.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down