Working album download
This commit is contained in:
+126
@@ -0,0 +1,126 @@
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <future>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// This may come in handy later
|
||||
// `yt-dlp` supports the following audio codecs:
|
||||
// `mp3`: MP3 (MPEG Audio Layer 3)
|
||||
// `m4a`: MPEG-4 Audio
|
||||
// `ogg`: Ogg Vorbis
|
||||
// `flac`: FLAC (Free Lossless Audio Codec)
|
||||
// `opus`: Opus
|
||||
// `aac`: AAC (Advanced Audio Coding)
|
||||
|
||||
#define TMP_DOWNLOAD_FOLDER "tmp/download"
|
||||
#define MAX_THREADS 4
|
||||
|
||||
// This runs in shell, if put through a website interface it would allow for injection
|
||||
auto ytdlp(std::string args) -> bool {
|
||||
system(std::format("bin/linux/yt-dlp {}", args).c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
auto cleanTmpDownloadFolder() -> bool {
|
||||
for (const auto& entry : std::filesystem::directory_iterator(TMP_DOWNLOAD_FOLDER)) {
|
||||
std::filesystem::remove_all(entry.path());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto urlsFromPlaylist(const std::string& url) -> std::vector<std::string> {
|
||||
std::vector<std::string> urls;
|
||||
|
||||
std::filesystem::remove("urls.txt");
|
||||
ytdlp(
|
||||
std::format("--js-runtimes \"deno:bin/linux/deno\" "
|
||||
"--quiet "
|
||||
"--flat-playlist "
|
||||
"--print-to-file \"%(webpage_url)s\" "
|
||||
"urls.txt \"{}\"",
|
||||
url));
|
||||
|
||||
std::ifstream f("urls.txt");
|
||||
if (!f.is_open()) {
|
||||
printf("ERROR: Couldn't open urls.txt\n");
|
||||
return urls;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while (std::getline(f, line)) {
|
||||
urls.push_back(line);
|
||||
}
|
||||
|
||||
if (urls.empty()) printf("ERROR: Couldn't find any urls for playlist %s\n", url.c_str());
|
||||
return urls;
|
||||
}
|
||||
|
||||
auto downloadVideo(const std::string& url) -> bool {
|
||||
if (url == "") {
|
||||
printf("ERROR: Cannot download video with empty url\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
ytdlp(
|
||||
std::format("--js-runtimes \"deno:bin/linux/deno\" "
|
||||
"--quiet "
|
||||
"-f \"bestaudio/best\" "
|
||||
"-x --audio-format mp3 "
|
||||
"-o \"{}/%(title)s.%(ext)s\" "
|
||||
"{}",
|
||||
TMP_DOWNLOAD_FOLDER, url));
|
||||
|
||||
printf("Finished downloading video %s.\n", url.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
auto downloadPlaylist(const std::string& playlistUrl) -> bool {
|
||||
std::vector<std::string> videoUrls = urlsFromPlaylist(playlistUrl);
|
||||
|
||||
if (videoUrls.empty()) {
|
||||
printf("ERROR: Tried to download empty playlist, %s\n", playlistUrl.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!cleanTmpDownloadFolder()) {
|
||||
printf("ERROR: Playlist download couldn't clean tmp download folder\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (size_t i = 0; i < videoUrls.size(); i += MAX_THREADS) {
|
||||
std::vector<std::future<void>> futures;
|
||||
|
||||
size_t end = std::min(i + MAX_THREADS, videoUrls.size());
|
||||
|
||||
for (size_t j = i; j < end; ++j) {
|
||||
futures.push_back(std::async(std::launch::async, [&videoUrls, j]() { downloadVideo(videoUrls[j]); }));
|
||||
}
|
||||
|
||||
for (auto& future : futures) {
|
||||
future.get();
|
||||
count++;
|
||||
printf("Finished [%d/%d] in playlist %s\n", count, (int)videoUrls.size(), playlistUrl.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
printf("Finished downloading playlist.\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto main(int argc, char* argv[]) -> int {
|
||||
auto musicDir = "/home/ethanscharlie/Downloads/tmp";
|
||||
auto url = "https://music.youtube.com/playlist?list=OLAK5uy_kwG9Ln_fS_R-Oa8F-JIgs8PPBoRgDe8ok";
|
||||
|
||||
std::filesystem::create_directories(TMP_DOWNLOAD_FOLDER);
|
||||
|
||||
downloadPlaylist(url);
|
||||
}
|
||||
Reference in New Issue
Block a user