Repository navigation
Support for adding audio streams via youtube-dl #379
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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" | ||
|
|
@@ -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, | ||
| 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()) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does it try to add multiple things from the database?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, but why the output of |
||
| 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 { | ||
|
|
@@ -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) | ||
|
|
||
There was a problem hiding this comment.
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.