diff --git a/CMakeLists.txt b/CMakeLists.txt index 90d9780..8d0f617 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,11 +16,14 @@ target_include_directories(YTMusicDL26 PUBLIC include(cmake/CPM.cmake) -CPMAddPackage("gh:nlohmann/json#v3.12.0") +# CPMAddPackage( +# NAME id3v2lib +# GIT_REPOSITORY https://github.com/larsbs/id3v2lib.git +# GIT_TAG master +# ) -target_link_libraries(YTMusicDL26 PUBLIC - nlohmann_json::nlohmann_json -) +# target_link_libraries(YTMusicDL26 PUBLIC +# ) file(COPY ${CMAKE_SOURCE_DIR}/bin DESTINATION ${CMAKE_BINARY_DIR}) diff --git a/bin/linux/ffmpeg b/bin/linux/ffmpeg new file mode 100755 index 0000000..c194831 Binary files /dev/null and b/bin/linux/ffmpeg differ diff --git a/src/main.cpp b/src/main.cpp index d489641..2950391 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,27 +5,34 @@ #include #include #include +#include #include #include -// 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 { +auto ytdlp(const std::string& args) -> bool { system(std::format("bin/linux/yt-dlp {}", args).c_str()); return true; } +auto metadata(const std::string& file, const std::string& artist, const std::string& album, size_t index) { + std::string name = std::filesystem::path(file).filename().string(); + + system(std::format("bin/linux/ffmpeg " + "-hide_banner -loglevel error " + "-i \"{}\" -c copy " + "-metadata title=\"{}\" " + "-metadata artist=\"{}\" " + "-metadata album=\"{}\" " + "-metadata track=\"{}\" " + "\"{}/{}_{}.mp3\"", + file, name, artist, album, index, TMP_DOWNLOAD_FOLDER, index, name) + .c_str()); +} + auto cleanTmpDownloadFolder() -> bool { for (const auto& entry : std::filesystem::directory_iterator(TMP_DOWNLOAD_FOLDER)) { std::filesystem::remove_all(entry.path()); @@ -61,6 +68,41 @@ auto urlsFromPlaylist(const std::string& url) -> std::vector { return urls; } +auto dataFromYtdlpPlaylist(const std::string& playlistUrl, 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 playlistTitle(const std::string& playlistUrl) { + std::string s = dataFromYtdlpPlaylist(playlistUrl, "playlist_title"); + s = std::regex_replace(s, std::regex("Album - "), ""); + return s; +} + +auto channelName(const std::string& playlistUrl) { + std::string s = dataFromYtdlpPlaylist(playlistUrl, "channel"); + s = std::regex_replace(s, std::regex(" - Topic"), ""); + return s; +} + auto downloadVideo(const std::string& url) -> bool { if (url == "") { printf("ERROR: Cannot download video with empty url\n"); @@ -80,7 +122,7 @@ auto downloadVideo(const std::string& url) -> bool { return true; } -auto downloadPlaylist(const std::string& playlistUrl) -> bool { +auto downloadPlaylist(std::string playlistUrl) -> bool { std::vector videoUrls = urlsFromPlaylist(playlistUrl); if (videoUrls.empty()) { @@ -93,6 +135,8 @@ auto downloadPlaylist(const std::string& playlistUrl) -> bool { 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) { @@ -116,11 +160,31 @@ auto downloadPlaylist(const std::string& playlistUrl) -> bool { return true; } +auto writeAlbumMetadataToTmpFolder(const std::string& playlistUrl) -> bool { + std::string artist = channelName(playlistUrl); + std::string album = playlistTitle(playlistUrl); + printf("Starting to write metadata for %s by %s.\n", album.c_str(), artist.c_str()); + + size_t index = 1; + for (const auto& entry : std::filesystem::directory_iterator(TMP_DOWNLOAD_FOLDER)) { + 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 writing metadata for %s by %s.\n", album.c_str(), artist.c_str()); + + 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::string url = "https://music.youtube.com/playlist?list=OLAK5uy_kwG9Ln_fS_R-Oa8F-JIgs8PPBoRgDe8ok"; std::filesystem::create_directories(TMP_DOWNLOAD_FOLDER); + url = std::regex_replace(url, std::regex("music.youtube"), "www.youtube"); downloadPlaylist(url); + writeAlbumMetadataToTmpFolder(url); }