Align yt-dlp to the boost removal changes
Some checks failed
CI / build ubuntu-24.04 (push) Has been cancelled
CI / build ubuntu-22.04 (push) Has been cancelled

This commit is contained in:
Salastil
2025-11-30 22:13:06 -05:00
parent 900939f4ad
commit b36a41d4e9

View File

@@ -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);
} }
} }