Compare commits
5
Commits
fe6dc020ca
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43da2c7ac1 | ||
|
|
92d11b5fbb | ||
|
|
28c4318b00 | ||
|
|
ce457fbd17 | ||
|
|
aed13dc18f |
+3
-7
@@ -20,6 +20,9 @@ target_include_directories(YTMusicDL26 PUBLIC
|
|||||||
include(cmake/CPM.cmake)
|
include(cmake/CPM.cmake)
|
||||||
|
|
||||||
set(CURL_USE_LIBPSL OFF CACHE BOOL "" FORCE)
|
set(CURL_USE_LIBPSL OFF CACHE BOOL "" FORCE)
|
||||||
|
set(CURL_USE_SCHANNEL ON)
|
||||||
|
|
||||||
|
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
CPMAddPackage("gh:nlohmann/json#v3.12.0")
|
CPMAddPackage("gh:nlohmann/json#v3.12.0")
|
||||||
CPMAddPackage("gh:curl/curl#curl-8_21_0")
|
CPMAddPackage("gh:curl/curl#curl-8_21_0")
|
||||||
@@ -38,13 +41,6 @@ if (WIN32)
|
|||||||
-static-libstdc++
|
-static-libstdc++
|
||||||
-static
|
-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 ()
|
endif ()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
set(CMAKE_SYSTEM_NAME Windows)
|
||||||
|
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||||
|
|
||||||
|
set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
|
||||||
|
set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
|
||||||
|
|
||||||
|
set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)
|
||||||
|
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
class Content {
|
||||||
|
public:
|
||||||
|
virtual ~Content() = default;
|
||||||
|
virtual auto rip(const std::string& musicFolder, std::function<void(int)> status) -> bool = 0;
|
||||||
|
virtual auto info(std::string& artist, std::string& title) -> void = 0;
|
||||||
|
};
|
||||||
+48
-7
@@ -1,15 +1,21 @@
|
|||||||
|
#include <cstdio>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <queue>
|
||||||
|
#include <regex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
|
#include "content.hpp"
|
||||||
|
#include "playlist.hpp"
|
||||||
#include "utils.hpp"
|
#include "utils.hpp"
|
||||||
|
#include "video.hpp"
|
||||||
|
|
||||||
auto main(int argc, char* argv[]) -> int {
|
auto main(int argc, char* argv[]) -> int {
|
||||||
std::vector<std::string> urls;
|
std::queue<std::string> urls;
|
||||||
|
|
||||||
printf(
|
printf(
|
||||||
"Welcome to YTMusicDL26! You can use playlists or video from youtube or youtube music (youtube music is "
|
"Welcome to YTMusicDL26! You can use playlists or videos from youtube or youtube music (youtube music is "
|
||||||
"better).\n");
|
"better).\n");
|
||||||
printf("Type \"end\" when your finished.\n");
|
printf("Type \"end\" when your finished.\n");
|
||||||
|
|
||||||
@@ -23,15 +29,50 @@ auto main(int argc, char* argv[]) -> int {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
urls.push_back(input);
|
urls.push(input);
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Starting rips...\n");
|
|
||||||
|
|
||||||
std::string musicDir = musicFolder();
|
std::string musicDir = musicFolder();
|
||||||
std::filesystem::create_directories(TMP_DOWNLOAD_FOLDER);
|
std::filesystem::create_directories(TMP_DOWNLOAD_FOLDER);
|
||||||
for (const std::string& url : urls) {
|
|
||||||
autoDetermineRip(musicDir, url);
|
printf("\n");
|
||||||
|
|
||||||
|
while (!urls.empty()) {
|
||||||
|
const std::string& currentUrl = urls.front();
|
||||||
|
|
||||||
|
Content* content = nullptr;
|
||||||
|
if (std::regex_search(currentUrl, std::regex("youtube.com/playlist"))) {
|
||||||
|
content = new Playlist(currentUrl);
|
||||||
|
} else if (std::regex_search(currentUrl, std::regex("youtube.com/watch"))) {
|
||||||
|
content = new Video(currentUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content == nullptr) {
|
||||||
|
printf("ERROR: Failed to create content object\n");
|
||||||
|
urls.pop();
|
||||||
|
delete content;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!content->rip(musicDir, [&content, &urls](int percent) {
|
||||||
|
if (percent == 0) {
|
||||||
|
printf("\r\033[KGathering info... (this may take a while)");
|
||||||
|
fflush(stdout);
|
||||||
|
} else {
|
||||||
|
std::string artist;
|
||||||
|
std::string album;
|
||||||
|
content->info(artist, album);
|
||||||
|
printf("\r\033[K%s by %s at %d%%", album.c_str(), artist.c_str(), percent);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
})) {
|
||||||
|
printf("Failed to rip content\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
urls.pop();
|
||||||
|
delete content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+48
-60
@@ -21,33 +21,48 @@ Playlist::Playlist(const std::string& playlistUrl) {
|
|||||||
this->playlistUrl = std::regex_replace(playlistUrl, std::regex("music.youtube"), "www.youtube");
|
this->playlistUrl = std::regex_replace(playlistUrl, std::regex("music.youtube"), "www.youtube");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Playlist::rip(const std::string& musicFolder) -> bool {
|
auto Playlist::rip(const std::string& musicFolder, std::function<void(int)> status) -> bool {
|
||||||
printf("Writting to data.json\n");
|
status(0);
|
||||||
json data = writeAndReturnDataJson(playlistUrl);
|
|
||||||
|
|
||||||
if (!download()) {
|
data = writeAndReturnDataJson(playlistUrl);
|
||||||
|
|
||||||
|
// Get the name of the artist and album from data.json
|
||||||
|
artist = data["entries"][0]["uploader"];
|
||||||
|
artist = std::regex_replace(artist, std::regex(" - Topic"), "");
|
||||||
|
album = data["title"];
|
||||||
|
album = std::regex_replace(album, std::regex("Album - "), "");
|
||||||
|
|
||||||
|
status(30);
|
||||||
|
|
||||||
|
// Download the content as mp3
|
||||||
|
if (!download(status)) {
|
||||||
printf("ERROR: Failed to download playlsit\n");
|
printf("ERROR: Failed to download playlsit\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!writeMetadata(data)) {
|
status(80);
|
||||||
printf("ERROR: Failed to write metatdata for playlsit\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string artist;
|
std::string coverUrl = data["thumbnails"][1]["url"];
|
||||||
std::string album;
|
writeMetadataToTmpFolder(artist, album, coverUrl);
|
||||||
aristAndAlbumName(data, artist, album);
|
|
||||||
|
|
||||||
|
status(95);
|
||||||
|
|
||||||
|
// Move the files into the users Music folder
|
||||||
if (!moveAndSortTmpFilesToFolder(musicFolder, artist, album)) {
|
if (!moveAndSortTmpFilesToFolder(musicFolder, artist, album)) {
|
||||||
printf("ERROR: Failed to move files to music folder\n");
|
printf("ERROR: Failed to move files to music folder\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
status(100);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Playlist::download() -> bool {
|
auto Playlist::info(std::string& artist, std::string& title) -> void {
|
||||||
|
artist = this->artist != "" ? this->artist : "Finding...";
|
||||||
|
title = this->album != "" ? this->album : "Finding...";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Playlist::download(std::function<void(int)> status) -> bool {
|
||||||
std::vector<std::string> videoUrls = urlsFromPlaylist(playlistUrl);
|
std::vector<std::string> videoUrls = urlsFromPlaylist(playlistUrl);
|
||||||
|
|
||||||
if (videoUrls.empty()) {
|
if (videoUrls.empty()) {
|
||||||
@@ -60,66 +75,39 @@ auto Playlist::download() -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
playlistUrl = std::regex_replace(playlistUrl, std::regex("music.youtube"), "www.youtube");
|
if (!forEachConcurrent(videoUrls, status, [this](const std::string& url) { return downloadVideo(url); })) {
|
||||||
|
printf("ERROR: Failed to download playlist currently\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int count = 0;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < videoUrls.size(); i += MAX_THREADS) {
|
auto Playlist::forEachConcurrent(const std::vector<std::string>& urls, std::function<void(int)> status,
|
||||||
std::vector<std::future<void>> futures;
|
std::function<bool(const std::string&)> onStart) -> bool {
|
||||||
|
int percent = 30;
|
||||||
|
|
||||||
size_t end = std::min(i + MAX_THREADS, videoUrls.size());
|
for (size_t i = 0; i < urls.size(); i += MAX_THREADS) {
|
||||||
|
std::vector<std::future<bool>> futures;
|
||||||
|
|
||||||
|
size_t end = std::min(i + MAX_THREADS, urls.size());
|
||||||
|
|
||||||
for (size_t j = i; j < end; ++j) {
|
for (size_t j = i; j < end; ++j) {
|
||||||
futures.push_back(std::async(std::launch::async, [&videoUrls, j]() { downloadVideo(videoUrls[j]); }));
|
std::string url = urls[j];
|
||||||
|
futures.push_back(std::async(std::launch::async, [&onStart, url]() { return onStart(url); }));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& future : futures) {
|
for (std::future<bool>& future : futures) {
|
||||||
future.get();
|
if (!future.get()) {
|
||||||
count++;
|
printf("ERROR: Failed to download video from playlist concurrently\n");
|
||||||
printf("Finished [%d/%d] in playlist %s\n", count, (int)videoUrls.size(), playlistUrl.c_str());
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Finished downloading playlist.\n");
|
percent += 50 / urls.size();
|
||||||
|
status(percent);
|
||||||
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().string()).filename().string() == "cover.jpg") continue;
|
|
||||||
|
|
||||||
metadata(entry.path().string(), artist, album, index);
|
|
||||||
printf("Finished writting metadata for %s.\n", entry.path().string().c_str());
|
|
||||||
std::filesystem::remove(entry.path().string());
|
|
||||||
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-6
@@ -1,19 +1,27 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <nlohmann/json_fwd.hpp>
|
#include <functional>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class Playlist {
|
#include "content.hpp"
|
||||||
|
|
||||||
|
class Playlist : public Content {
|
||||||
std::string playlistUrl;
|
std::string playlistUrl;
|
||||||
|
|
||||||
|
std::string artist = "";
|
||||||
|
std::string album = "";
|
||||||
|
nlohmann::json data;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Playlist(const std::string& playlistUrl);
|
Playlist(const std::string& playlistUrl);
|
||||||
auto rip(const std::string& musicFolder) -> bool;
|
auto rip(const std::string& musicFolder, std::function<void(int)> status) -> bool;
|
||||||
|
auto info(std::string& artist, std::string& title) -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
auto download() -> bool;
|
auto download(std::function<void(int)> status) -> bool;
|
||||||
auto writeMetadata(const nlohmann::json& data) -> bool;
|
auto forEachConcurrent(const std::vector<std::string>& urls, std::function<void(int)> status,
|
||||||
auto aristAndAlbumName(const nlohmann::json& data, std::string& artist, std::string& album) -> bool;
|
std::function<bool(const std::string&)> onStart) -> bool;
|
||||||
auto urlsFromPlaylist(const std::string& url) -> std::vector<std::string>;
|
auto urlsFromPlaylist(const std::string& url) -> std::vector<std::string>;
|
||||||
};
|
};
|
||||||
|
|||||||
+20
-20
@@ -12,8 +12,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "playlist.hpp"
|
|
||||||
#include "video.hpp"
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
@@ -26,6 +24,7 @@ auto ytdlp(const std::string& args) -> bool {
|
|||||||
auto metadata(const std::string& file, const std::string& artist, const std::string& album, size_t index) -> void {
|
auto metadata(const std::string& file, const std::string& artist, const std::string& album, size_t index) -> void {
|
||||||
std::string name = std::filesystem::path(file).filename().string();
|
std::string name = std::filesystem::path(file).filename().string();
|
||||||
name = std::regex_replace(name, std::regex(".mp3"), "");
|
name = std::regex_replace(name, std::regex(".mp3"), "");
|
||||||
|
name.erase(0, 1); // Remove leading underscore
|
||||||
|
|
||||||
system(std::format("{} "
|
system(std::format("{} "
|
||||||
"-hide_banner -loglevel error "
|
"-hide_banner -loglevel error "
|
||||||
@@ -39,9 +38,8 @@ auto metadata(const std::string& file, const std::string& artist, const std::str
|
|||||||
"-metadata artist=\"{}\" "
|
"-metadata artist=\"{}\" "
|
||||||
"-metadata album=\"{}\" "
|
"-metadata album=\"{}\" "
|
||||||
"-metadata track=\"{}\" "
|
"-metadata track=\"{}\" "
|
||||||
"\"{}/{}_{}.mp3\"",
|
"\"{}/{}.mp3\"",
|
||||||
BIN_FFMPEG, file, TMP_DOWNLOAD_FOLDER, name, artist, album, index, TMP_DOWNLOAD_FOLDER, index,
|
BIN_FFMPEG, file, TMP_DOWNLOAD_FOLDER, name, artist, album, index, TMP_DOWNLOAD_FOLDER, name)
|
||||||
name)
|
|
||||||
.c_str());
|
.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +58,8 @@ auto downloadImageToTmpFolder(std::string url) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
url = std::regex_replace(url, std::regex("https"), "http");
|
// TODO Breaks album covers but this is needed for windows
|
||||||
|
// url = std::regex_replace(url, std::regex("https"), "http");
|
||||||
auto outputPath = std::filesystem::path(TMP_DOWNLOAD_FOLDER) / "cover.jpg";
|
auto outputPath = std::filesystem::path(TMP_DOWNLOAD_FOLDER) / "cover.jpg";
|
||||||
FILE* file = std::fopen(outputPath.string().c_str(), "wb");
|
FILE* file = std::fopen(outputPath.string().c_str(), "wb");
|
||||||
if (!file) {
|
if (!file) {
|
||||||
@@ -98,11 +97,10 @@ auto downloadVideo(const std::string& url) -> bool {
|
|||||||
"--quiet "
|
"--quiet "
|
||||||
"-f \"bestaudio/best\" "
|
"-f \"bestaudio/best\" "
|
||||||
"-x --audio-format mp3 "
|
"-x --audio-format mp3 "
|
||||||
"-o \"{}/%(title)s.%(ext)s\" "
|
"-o \"{}/_%(title)s.%(ext)s\" "
|
||||||
"{}",
|
"{}",
|
||||||
BIN_DENO, TMP_DOWNLOAD_FOLDER, url));
|
BIN_DENO, TMP_DOWNLOAD_FOLDER, url));
|
||||||
|
|
||||||
printf("Finished downloading video %s.\n", url.c_str());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,18 +139,6 @@ auto moveAndSortTmpFilesToFolder(const std::string& folder, const std::string& a
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto autoDetermineRip(const std::string& musicFolder, const std::string& url) -> bool {
|
|
||||||
if (std::regex_search(url, std::regex("youtube.com/playlist"))) {
|
|
||||||
return Playlist(url).rip(musicFolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (std::regex_search(url, std::regex("youtube.com/watch"))) {
|
|
||||||
return Video(url).rip(musicFolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto musicFolder() -> std::string {
|
auto musicFolder() -> std::string {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
const char* userProfile = std::getenv("USERPROFILE");
|
const char* userProfile = std::getenv("USERPROFILE");
|
||||||
@@ -164,3 +150,17 @@ auto musicFolder() -> std::string {
|
|||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto writeMetadataToTmpFolder(const std::string& artist, const std::string& album, const std::string& coverUrl)
|
||||||
|
-> void {
|
||||||
|
downloadImageToTmpFolder(coverUrl);
|
||||||
|
|
||||||
|
size_t index = 1;
|
||||||
|
for (const auto& entry : std::filesystem::directory_iterator(TMP_DOWNLOAD_FOLDER)) {
|
||||||
|
if (std::filesystem::path(entry.path().string()).filename().string() == "cover.jpg") continue;
|
||||||
|
|
||||||
|
metadata(entry.path().string(), artist, album, index);
|
||||||
|
std::filesystem::remove(entry.path().string());
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+2
-2
@@ -17,6 +17,6 @@ auto writeAndReturnDataJson(const std::string& url) -> nlohmann::json;
|
|||||||
auto moveAndSortTmpFilesToFolder(const std::string& folder, const std::string& artist, const std::string& album)
|
auto moveAndSortTmpFilesToFolder(const std::string& folder, const std::string& artist, const std::string& album)
|
||||||
-> bool;
|
-> bool;
|
||||||
|
|
||||||
auto autoDetermineRip(const std::string& musicFolder, const std::string& url) -> bool;
|
|
||||||
|
|
||||||
auto musicFolder() -> std::string;
|
auto musicFolder() -> std::string;
|
||||||
|
|
||||||
|
auto writeMetadataToTmpFolder(const std::string& artist, const std::string& album, const std::string& coverUrl) -> void;
|
||||||
|
|||||||
+22
-58
@@ -1,18 +1,9 @@
|
|||||||
#include "video.hpp"
|
#include "video.hpp"
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <format>
|
|
||||||
#include <fstream>
|
|
||||||
#include <future>
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "config.hpp"
|
|
||||||
#include "utils.hpp"
|
#include "utils.hpp"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
@@ -21,32 +12,44 @@ Video::Video(const std::string& videoUrl) {
|
|||||||
this->videoUrl = std::regex_replace(videoUrl, std::regex("music.youtube"), "www.youtube");
|
this->videoUrl = std::regex_replace(videoUrl, std::regex("music.youtube"), "www.youtube");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Video::rip(const std::string& musicFolder) -> bool {
|
auto Video::rip(const std::string& musicFolder, std::function<void(int)> status) -> bool {
|
||||||
printf("Writting to data.json\n");
|
status(0);
|
||||||
json data = writeAndReturnDataJson(videoUrl);
|
|
||||||
|
data = writeAndReturnDataJson(videoUrl);
|
||||||
|
|
||||||
|
artist = data["uploader"];
|
||||||
|
artist = std::regex_replace(artist, std::regex(" - Topic"), "");
|
||||||
|
album = data["title"];
|
||||||
|
album = std::regex_replace(album, std::regex("Album - "), "");
|
||||||
|
|
||||||
|
status(50);
|
||||||
|
|
||||||
if (!download()) {
|
if (!download()) {
|
||||||
printf("ERROR: Failed to download playlsit\n");
|
printf("ERROR: Failed to download playlsit\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!writeMetadata(data)) {
|
status(80);
|
||||||
printf("ERROR: Failed to write metatdata for playlsit\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string artist;
|
std::string coverUrl = data["thumbnails"][1]["url"];
|
||||||
std::string album;
|
writeMetadataToTmpFolder(artist, album, coverUrl);
|
||||||
aristAndAlbumName(data, artist, album);
|
|
||||||
|
status(95);
|
||||||
|
|
||||||
if (!moveAndSortTmpFilesToFolder(musicFolder, artist, album)) {
|
if (!moveAndSortTmpFilesToFolder(musicFolder, artist, album)) {
|
||||||
printf("ERROR: Failed to move files to music folder\n");
|
printf("ERROR: Failed to move files to music folder\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
status(100);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto Video::info(std::string& artist, std::string& title) -> void {
|
||||||
|
artist = this->artist != "" ? this->artist : "Finding...";
|
||||||
|
title = this->album != "" ? this->album : "Finding...";
|
||||||
|
}
|
||||||
|
|
||||||
auto Video::download() -> bool {
|
auto Video::download() -> bool {
|
||||||
if (videoUrl == "") {
|
if (videoUrl == "") {
|
||||||
printf("ERROR: Tried to download empty video, %s\n", videoUrl.c_str());
|
printf("ERROR: Tried to download empty video, %s\n", videoUrl.c_str());
|
||||||
@@ -63,44 +66,5 @@ auto Video::download() -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Finished downloading video %s\n", videoUrl.c_str());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Video::writeMetadata(const nlohmann::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().string()).filename().string() == "cover.jpg") continue;
|
|
||||||
|
|
||||||
metadata(entry.path().string(), artist, album, index);
|
|
||||||
printf("Finished writting metadata for %s.\n", entry.path().string().c_str());
|
|
||||||
std::filesystem::remove(entry.path().string());
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Finished writting metadata for %s by %s.\n", album.c_str(), artist.c_str());
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Video::aristAndAlbumName(const nlohmann::json& data, std::string& artist, std::string& album) -> bool {
|
|
||||||
artist = data["uploader"];
|
|
||||||
artist = std::regex_replace(artist, std::regex(" - Topic"), "");
|
|
||||||
|
|
||||||
album = data["title"];
|
|
||||||
album = std::regex_replace(album, std::regex("Album - "), "");
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-5
@@ -1,17 +1,22 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <nlohmann/json_fwd.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class Video {
|
#include "content.hpp"
|
||||||
|
|
||||||
|
class Video : public Content {
|
||||||
std::string videoUrl;
|
std::string videoUrl;
|
||||||
|
|
||||||
|
std::string artist = "";
|
||||||
|
std::string album = "";
|
||||||
|
nlohmann::json data;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Video(const std::string& videoUrl);
|
Video(const std::string& videoUrl);
|
||||||
auto rip(const std::string& musicFolder) -> bool;
|
auto rip(const std::string& musicFolder, std::function<void(int)> status) -> bool;
|
||||||
|
auto info(std::string& artist, std::string& title) -> void;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
auto download() -> bool;
|
auto download() -> bool;
|
||||||
auto writeMetadata(const nlohmann::json& data) -> bool;
|
|
||||||
auto aristAndAlbumName(const nlohmann::json& data, std::string& artist, std::string& album) -> bool;
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user