OOP structure

This commit is contained in:
2026-08-20 10:27:29 -06:00
parent 01b51efa63
commit a33cab8326
7 changed files with 303 additions and 238 deletions
+161
View File
@@ -0,0 +1,161 @@
#include "playlist.hpp"
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <format>
#include <fstream>
#include <future>
#include <nlohmann/json.hpp>
#include <regex>
#include <string>
#include <vector>
#include "config.hpp"
#include "utils.hpp"
using json = nlohmann::json;
Playlist::Playlist(const std::string& playlistUrl) : playlistUrl(playlistUrl) {}
auto Playlist::download() -> 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;
}
playlistUrl = std::regex_replace(playlistUrl, std::regex("music.youtube"), "www.youtube");
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 Playlist::writeMetadata() -> bool {
std::string artist = channelNamePlaylist();
std::string album = playlistTitle();
printf("Starting to write metadata for %s by %s.\n", album.c_str(), artist.c_str());
printf("Finding album cover.\n");
std::filesystem::remove("tmp/data.json");
ytdlp(
std::format("--js-runtimes \"deno:bin/linux/deno\" "
"--dump-single-json "
"\"{}\" "
"> tmp/data.json",
playlistUrl));
std::ifstream f("tmp/data.json");
if (!f) {
printf("ERROR: Couldn't open data.json\n");
return false;
}
json data = json::parse(f);
std::string coverUrl = data["thumbnails"][1]["url"];
downloadImageToTmpFolder(coverUrl);
printf("Writting data to files.\n");
size_t index = 1;
for (const auto& entry : std::filesystem::directory_iterator(TMP_DOWNLOAD_FOLDER)) {
if (std::filesystem::path(entry.path()).filename().string() == "cover.jpg") continue;
metadata(entry.path(), artist, album, index);
printf("Finished writting metadata for %s.\n", entry.path().c_str());
std::filesystem::remove(entry.path());
index++;
}
printf("Finished writting metadata for %s by %s.\n", album.c_str(), artist.c_str());
return true;
}
auto Playlist::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 Playlist::dataFromYtdlpPlaylist(const std::string& data) -> std::string {
ytdlp(
std::format("--quiet "
"--no-warnings "
"--flat-playlist "
"--playlist-items 1 "
"--print-to-file \"%({})s\" data.txt \"{}\"",
data, playlistUrl));
std::ifstream f("data.txt");
if (!f.is_open()) {
printf("ERROR: Couldn't open data.txt\n");
}
std::string s;
std::string line;
while (std::getline(f, line)) {
s = line;
}
return s;
};
auto Playlist::playlistTitle() -> std::string {
std::string s = dataFromYtdlpPlaylist("playlist_title");
s = std::regex_replace(s, std::regex("Album - "), "");
return s;
}
auto Playlist::channelNamePlaylist() -> std::string {
std::string s = dataFromYtdlpPlaylist("channel");
s = std::regex_replace(s, std::regex(" - Topic"), "");
return s;
}