Working album download
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
build
|
||||
.cache
|
||||
@@ -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
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
+1363
File diff suppressed because it is too large
Load Diff
+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