Align yt-dlp to the boost removal changes
This commit is contained in:
244
src/actions.cpp
244
src/actions.cpp
@@ -20,22 +20,16 @@
|
|||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||||
#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 <mpd/tag.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
#include "actions.h"
|
#include "actions.h"
|
||||||
#include "charset.h"
|
#include "charset.h"
|
||||||
@@ -2765,13 +2759,11 @@ bool AddYoutubeDLItem::canBeRun()
|
|||||||
void AddYoutubeDLItem::run()
|
void AddYoutubeDLItem::run()
|
||||||
{
|
{
|
||||||
using Global::wFooter;
|
using Global::wFooter;
|
||||||
namespace bp = boost::process;
|
|
||||||
namespace pt = boost::property_tree;
|
|
||||||
|
|
||||||
std::string url;
|
std::string url;
|
||||||
{
|
{
|
||||||
Statusbar::ScopedLock slock;
|
Statusbar::ScopedLock slock;
|
||||||
Statusbar::put() << "Add via youtube-dl: ";
|
Statusbar::put() << "Add via youtube-dl/yt-dlp: ";
|
||||||
url = wFooter->prompt();
|
url = wFooter->prompt();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2779,74 +2771,192 @@ void AddYoutubeDLItem::run()
|
|||||||
if (url.empty())
|
if (url.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// search the youtube-dl executable in the PATH
|
// Try to find yt-dlp first, fall back to youtube-dl
|
||||||
auto ydl_path = bp::search_path("youtube-dl");
|
std::string ydl_executable;
|
||||||
if (ydl_path.empty()) {
|
{
|
||||||
Statusbar::print("youtube-dl was not found in PATH");
|
FILE *which_pipe = popen("which yt-dlp 2>/dev/null", "r");
|
||||||
|
if (which_pipe)
|
||||||
|
{
|
||||||
|
char path[256];
|
||||||
|
if (fgets(path, sizeof(path), which_pipe) != nullptr)
|
||||||
|
{
|
||||||
|
// Remove trailing newline
|
||||||
|
size_t len = strlen(path);
|
||||||
|
if (len > 0 && path[len-1] == '\n')
|
||||||
|
path[len-1] = '\0';
|
||||||
|
ydl_executable = path;
|
||||||
|
}
|
||||||
|
pclose(which_pipe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to youtube-dl if yt-dlp not found
|
||||||
|
if (ydl_executable.empty())
|
||||||
|
{
|
||||||
|
FILE *which_pipe = popen("which youtube-dl 2>/dev/null", "r");
|
||||||
|
if (which_pipe)
|
||||||
|
{
|
||||||
|
char path[256];
|
||||||
|
if (fgets(path, sizeof(path), which_pipe) != nullptr)
|
||||||
|
{
|
||||||
|
size_t len = strlen(path);
|
||||||
|
if (len > 0 && path[len-1] == '\n')
|
||||||
|
path[len-1] = '\0';
|
||||||
|
ydl_executable = path;
|
||||||
|
}
|
||||||
|
pclose(which_pipe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ydl_executable.empty())
|
||||||
|
{
|
||||||
|
Statusbar::print("Neither yt-dlp nor youtube-dl was found in PATH");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Statusbar::printf("Calling youtube-dl with '%1%' ...", url);
|
Statusbar::printf("Fetching from '%s'...", url.c_str());
|
||||||
|
|
||||||
// start youtube-dl in a child process
|
// Build command with proper shell escaping
|
||||||
// -j: output as JSON, each playlist item on a separate line
|
std::string escaped_url = url;
|
||||||
// -f bestaudio/best: selects the best available audio-only stream, or
|
// Simple shell escape - replace single quotes with '\''
|
||||||
// alternatively the best audio+video stream
|
size_t pos = 0;
|
||||||
bp::ipstream output;
|
while ((pos = escaped_url.find("'", pos)) != std::string::npos)
|
||||||
bp::child child_process(ydl_path, url, "-j", "-f", "bestaudio/best", "--playlist-end", "100", bp::std_out > output,
|
{
|
||||||
bp::std_err > bp::null);
|
escaped_url.replace(pos, 1, "'\\''");
|
||||||
|
pos += 4;
|
||||||
|
}
|
||||||
|
|
||||||
// extract the URL and metadata from a ptree object and add
|
// Use --flat-playlist to get all items, then process each one
|
||||||
auto add_song = [] (const pt::ptree& ptree) {
|
// First get list of video IDs/URLs from playlist
|
||||||
auto download_url = ptree.get<std::string>("url");
|
std::string command = ydl_executable + " --flat-playlist --get-id " +
|
||||||
auto title = ptree.get_optional<std::string>("title");
|
"--playlist-end 100 " +
|
||||||
auto artist = ptree.get_optional<std::string>("creator");
|
"'" + escaped_url + "' 2>/dev/null";
|
||||||
if (!artist.has_value()) {
|
|
||||||
artist = ptree.get_optional<std::string>("uploader");
|
FILE *pipe = popen(command.c_str(), "r");
|
||||||
}
|
if (!pipe)
|
||||||
auto album = ptree.get_optional<std::string>("album");
|
{
|
||||||
auto id = Mpd.AddSong(download_url);
|
Statusbar::print("Failed to execute downloader");
|
||||||
if (id == -1) {
|
return;
|
||||||
return 0;
|
}
|
||||||
}
|
|
||||||
if (title.has_value()) {
|
std::vector<std::string> video_ids;
|
||||||
Mpd.AddTag(id, MPD_TAG_TITLE, *title);
|
char line[4096];
|
||||||
}
|
|
||||||
if (artist.has_value()) {
|
// Collect all video IDs
|
||||||
Mpd.AddTag(id, MPD_TAG_ARTIST, *artist);
|
while (fgets(line, sizeof(line), pipe) != nullptr)
|
||||||
}
|
{
|
||||||
if (album.has_value()) {
|
size_t len = strlen(line);
|
||||||
Mpd.AddTag(id, MPD_TAG_ALBUM, *album);
|
while (len > 0 && (line[len-1] == '\n' || line[len-1] == '\r'))
|
||||||
}
|
{
|
||||||
return 1;
|
line[--len] = '\0';
|
||||||
};
|
}
|
||||||
|
|
||||||
|
if (len > 0)
|
||||||
|
video_ids.push_back(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
pclose(pipe);
|
||||||
|
|
||||||
|
if (video_ids.empty())
|
||||||
|
{
|
||||||
|
Statusbar::print("No items found in playlist");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Statusbar::printf("Found %u items, fetching URLs...",
|
||||||
|
static_cast<unsigned>(video_ids.size()));
|
||||||
|
|
||||||
std::string line;
|
|
||||||
pt::ptree ptree;
|
|
||||||
unsigned num_songs_added = 0;
|
unsigned num_songs_added = 0;
|
||||||
|
|
||||||
while (std::getline(output, line)) {
|
// Now process each video to get its title and stream URL
|
||||||
try {
|
for (const auto &video_id : video_ids)
|
||||||
std::istringstream line_stream(line);
|
{
|
||||||
pt::read_json(line_stream, ptree);
|
// Build URL for this specific video
|
||||||
num_songs_added += add_song(ptree);
|
std::string video_url;
|
||||||
} catch (pt::ptree_error &e) {
|
if (video_id.find("http") == 0)
|
||||||
Statusbar::print("An error occurred while parsing the output of youtube-dl");
|
{
|
||||||
continue;
|
video_url = video_id;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Assume it's a YouTube ID
|
||||||
|
video_url = "https://www.youtube.com/watch?v=" + video_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape the video URL
|
||||||
|
std::string escaped_video_url = video_url;
|
||||||
|
pos = 0;
|
||||||
|
while ((pos = escaped_video_url.find("'", pos)) != std::string::npos)
|
||||||
|
{
|
||||||
|
escaped_video_url.replace(pos, 1, "'\\''");
|
||||||
|
pos += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get title and URL for this specific video
|
||||||
|
std::string video_command = ydl_executable + " --get-title --get-url " +
|
||||||
|
"-f bestaudio/best " +
|
||||||
|
"'" + escaped_video_url + "' 2>/dev/null";
|
||||||
|
|
||||||
|
FILE *video_pipe = popen(video_command.c_str(), "r");
|
||||||
|
if (!video_pipe)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string title;
|
||||||
|
std::string stream_url;
|
||||||
|
int line_num = 0;
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), video_pipe) != nullptr)
|
||||||
|
{
|
||||||
|
size_t len = strlen(line);
|
||||||
|
while (len > 0 && (line[len-1] == '\n' || line[len-1] == '\r'))
|
||||||
|
{
|
||||||
|
line[--len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (line_num == 0)
|
||||||
|
title = line;
|
||||||
|
else if (line_num == 1)
|
||||||
|
stream_url = line;
|
||||||
|
|
||||||
|
++line_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
pclose(video_pipe);
|
||||||
|
|
||||||
|
// Add to MPD if we got both title and URL
|
||||||
|
if (!stream_url.empty() &&
|
||||||
|
(stream_url.find("http://") == 0 || stream_url.find("https://") == 0))
|
||||||
|
{
|
||||||
|
int id = Mpd.AddSong(stream_url);
|
||||||
|
if (id != -1)
|
||||||
|
{
|
||||||
|
if (!title.empty())
|
||||||
|
Mpd.AddTag(id, MPD_TAG_TITLE, title);
|
||||||
|
|
||||||
|
++num_songs_added;
|
||||||
|
|
||||||
|
std::string display = title.empty() ? video_id : title;
|
||||||
|
if (display.length() > 50)
|
||||||
|
display = display.substr(0, 47) + "...";
|
||||||
|
|
||||||
|
Statusbar::printf("Added: %s (%u/%u)",
|
||||||
|
display.c_str(),
|
||||||
|
num_songs_added,
|
||||||
|
static_cast<unsigned>(video_ids.size()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Statusbar::printf("Added %1% item(s) to playlist", num_songs_added);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (child_process.running()) {
|
if (num_songs_added > 0)
|
||||||
child_process.terminate();
|
{
|
||||||
|
Statusbar::printf("Successfully added %u item(s) to playlist", num_songs_added);
|
||||||
}
|
}
|
||||||
child_process.wait();
|
else
|
||||||
|
{
|
||||||
auto ec = child_process.exit_code();
|
Statusbar::print("Failed to add any items");
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user