152 lines
4.0 KiB
C++
152 lines
4.0 KiB
C++
#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::rip(const std::string& musicFolder) -> bool {
|
|
printf("Writting to data.json\n");
|
|
json data = writeAndReturnDataJson(playlistUrl);
|
|
|
|
if (!download()) {
|
|
printf("ERROR: Failed to download playlsit\n");
|
|
return false;
|
|
}
|
|
|
|
if (!writeMetadata(data)) {
|
|
printf("ERROR: Failed to write metatdata for playlsit\n");
|
|
return false;
|
|
}
|
|
|
|
std::string artist;
|
|
std::string album;
|
|
aristAndAlbumName(data, artist, album);
|
|
|
|
if (!moveAndSortTmpFilesToFolder(musicFolder, artist, album)) {
|
|
printf("ERROR: Failed to move files to music folder\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
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(const json& data) -> bool {
|
|
std::string artist;
|
|
std::string album;
|
|
aristAndAlbumName(data, artist, album);
|
|
|
|
printf("Starting to write metadata for %s by %s.\n", album.c_str(), artist.c_str());
|
|
|
|
printf("Finding album cover.\n");
|
|
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::aristAndAlbumName(const nlohmann::json& data, std::string& artist, std::string& album) -> bool {
|
|
artist = data["entries"][0]["uploader"];
|
|
artist = std::regex_replace(artist, std::regex(" - Topic"), "");
|
|
|
|
album = data["title"];
|
|
album = std::regex_replace(album, std::regex("Album - "), "");
|
|
|
|
return true;
|
|
}
|
|
|
|
auto Playlist::urlsFromPlaylist(const std::string& url) -> std::vector<std::string> {
|
|
std::vector<std::string> urls;
|
|
|
|
std::string urlsFileLocation = "tmp/urls.txt";
|
|
|
|
std::filesystem::remove(urlsFileLocation);
|
|
ytdlp(
|
|
std::format("--js-runtimes \"deno:bin/linux/deno\" "
|
|
"--quiet "
|
|
"--flat-playlist "
|
|
"--print-to-file \"%(webpage_url)s\" "
|
|
"{} \"{}\"",
|
|
urlsFileLocation, url));
|
|
|
|
std::ifstream f(urlsFileLocation);
|
|
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;
|
|
}
|