Working album download

This commit is contained in:
2026-08-19 19:45:19 -06:00
commit f21f51091d
8 changed files with 1539 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
build
.cache
+42
View File
@@ -0,0 +1,42 @@
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
project(YTMusicDL26)
add_executable(YTMusicDL26
src/main.cpp
)
target_include_directories(YTMusicDL26 PUBLIC
src/
include/
)
include(cmake/CPM.cmake)
CPMAddPackage("gh:nlohmann/json#v3.12.0")
target_link_libraries(YTMusicDL26 PUBLIC
nlohmann_json::nlohmann_json
)
file(COPY ${CMAKE_SOURCE_DIR}/bin DESTINATION ${CMAKE_BINARY_DIR})
if (WIN32)
target_link_options(YTMusicDL26 PRIVATE
-static-libgcc
-static-libstdc++
-static
)
add_custom_command(TARGET YTMusicDL26 POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_RUNTIME_DLLS:YTMusicDL26>
$<TARGET_FILE_DIR:YTMusicDL26>
COMMAND_EXPAND_LISTS
)
endif ()
Executable
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
+1363
View File
File diff suppressed because it is too large Load Diff
+6
View File
@@ -0,0 +1,6 @@
mkdir build
cd build
cmake -GNinja ..
ninja
./YTMusicDL26
cd ..
+126
View File
@@ -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);
}