From 9a894487ad4a03e65e108ecefec062206234d92b Mon Sep 17 00:00:00 2001 From: kdeng00 Date: Sun, 15 Sep 2019 00:23:08 -0400 Subject: [PATCH] Almost done with updating metadata of song, just need to handle records that have no related records associated with them --- CMakeLists.txt | 2 + include/database/CoverArtRepository.h | 1 + include/database/SongRepository.h | 5 +- include/manager/AlbumManager.h | 1 + include/manager/ArtistManager.h | 1 + include/manager/CoverArtManager.h | 8 +- include/manager/DirectoryManager.h | 6 +- include/manager/GenreManager.h | 1 + include/manager/SongManager.h | 11 ++ include/manager/YearManager.h | 1 + include/type/PathType.h | 15 +++ include/type/SongChanged.h | 16 +++ src/database/CoverArtRepository.cpp | 45 +++++++ src/database/SongRepository.cpp | 94 +++++++++++++ src/manager/AlbumManager.cpp | 19 +++ src/manager/ArtistManager.cpp | 19 +++ src/manager/CoverArtManager.cpp | 72 ++++++++-- src/manager/DirectoryManager.cpp | 83 ++++++++++-- src/manager/GenreManager.cpp | 19 +++ src/manager/SongManager.cpp | 181 +++++++++++++++++++++++++- src/manager/YearManager.cpp | 18 +++ 21 files changed, 589 insertions(+), 29 deletions(-) create mode 100644 include/type/PathType.h create mode 100644 include/type/SongChanged.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f3c9e42..de08fcd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,7 +65,9 @@ set(HEADERS include/type/ArtistFilter.h include/type/CoverFilter.h include/type/GenreFilter.h + include/type/PathType.h include/type/Scopes.h + include/type/SongChanged.h include/type/SongFilter.h include/type/YearFilter.h ) diff --git a/include/database/CoverArtRepository.h b/include/database/CoverArtRepository.h index 7be4af1..3ec99d2 100644 --- a/include/database/CoverArtRepository.h +++ b/include/database/CoverArtRepository.h @@ -25,6 +25,7 @@ namespace database void deleteRecord(const model::Cover&); void saveRecord(const model::Cover&); + void updateRecord(const model::Cover&); private: std::vector parseRecords(MYSQL_STMT*); diff --git a/include/database/SongRepository.h b/include/database/SongRepository.h index daf5901..24ebdf7 100644 --- a/include/database/SongRepository.h +++ b/include/database/SongRepository.h @@ -20,12 +20,13 @@ namespace database std::vector retrieveRecords(); - bool doesSongExist(const model::Song&, type::SongFilter); - model::Song retrieveRecord(model::Song&, type::SongFilter); + bool doesSongExist(const model::Song&, type::SongFilter); bool deleteRecord(const model::Song&); + void saveRecord(const model::Song&); + void updateRecord(const model::Song&); private: std::vector parseRecords(MYSQL_RES*); // TODO: to be removed std::vector parseRecords(MYSQL_STMT*); diff --git a/include/manager/AlbumManager.h b/include/manager/AlbumManager.h index f72dab9..66fd2cf 100644 --- a/include/manager/AlbumManager.h +++ b/include/manager/AlbumManager.h @@ -14,6 +14,7 @@ namespace manager model::Album saveAlbum(const model::Song&); void deleteAlbum(const model::Song&); + void updateAlbum(model::Song&, const model::Song&); static void printAlbum(const model::Album&); private: diff --git a/include/manager/ArtistManager.h b/include/manager/ArtistManager.h index e65fc5d..e991032 100644 --- a/include/manager/ArtistManager.h +++ b/include/manager/ArtistManager.h @@ -14,6 +14,7 @@ namespace manager model::Artist saveArtist(const model::Song&); void deleteArtist(const model::Song&); + void updateArtist(model::Song&, const model::Song&); static void printArtist(const model::Artist&); private: diff --git a/include/manager/CoverArtManager.h b/include/manager/CoverArtManager.h index f6527de..8f819f8 100644 --- a/include/manager/CoverArtManager.h +++ b/include/manager/CoverArtManager.h @@ -2,6 +2,7 @@ #define COVERARTMANAGER_H_ #include +#include #include "model/Models.h" @@ -13,9 +14,14 @@ namespace manager CoverArtManager(const std::string&); CoverArtManager(const model::BinaryPath& bConf); - model::Cover saveCover(const model::Song&, std::string&, const std::string&); + model::Cover saveCover(const model::Song&, std::string&, + const std::string&); + + std::pair defaultCover(const model::Cover&); void deleteCover(const model::Song&); + void updateCover(const model::Song&); + void updateCoverRecord(const model::Song&); private: model::BinaryPath m_bConf; std::string path; diff --git a/include/manager/DirectoryManager.h b/include/manager/DirectoryManager.h index fc8c518..7ade859 100644 --- a/include/manager/DirectoryManager.h +++ b/include/manager/DirectoryManager.h @@ -7,6 +7,7 @@ #include #include "model/Models.h" +#include "type/PathType.h" namespace manager { @@ -14,17 +15,20 @@ namespace manager { public: static std::string createDirectoryProcess(model::Song, const std::string&); + static std::string createDirectoryProcess(const model::Song&, const model::BinaryPath&, type::PathType); static std::string configPath(std::string_view); static std::string configPath(const model::BinaryPath&); static std::string contentOfPath(const std::string&); + static std::string retrievePathType(type::PathType); static nlohmann::json credentialConfigContent(const model::BinaryPath&); static nlohmann::json databaseConfigContent(const model::BinaryPath&); static nlohmann::json pathConfigContent(const model::BinaryPath&); - void deleteCoverArtFile(const std::string&, const std::string&); static void deleteDirectories(model::Song, const std::string&); + void deleteCoverArtFile(const std::string&, const std::string&); + private: void deleteSong(const model::Song); }; diff --git a/include/manager/GenreManager.h b/include/manager/GenreManager.h index 55e2539..8e91b11 100644 --- a/include/manager/GenreManager.h +++ b/include/manager/GenreManager.h @@ -14,6 +14,7 @@ namespace manager model::Genre saveGenre(const model::Song&); void deleteGenre(const model::Song&); + void updateGenre(model::Song&, const model::Song&); static void printGenre(const model::Genre&); private: diff --git a/include/manager/SongManager.h b/include/manager/SongManager.h index 09f7529..8b550b4 100644 --- a/include/manager/SongManager.h +++ b/include/manager/SongManager.h @@ -2,12 +2,14 @@ #define SONGMANAGER_H_ #include +#include #include #include #include "dto/SongDto.hpp" #include "model/Models.h" +#include "type/SongChanged.h" namespace manager { @@ -17,6 +19,7 @@ namespace manager SongManager(std::string&); SongManager(const model::BinaryPath&); + bool didSongChange(const model::Song&, const model::Song&); bool requiresFilesystemChange(const model::Song&, const model::Song&); @@ -28,9 +31,17 @@ namespace manager static void printSong(const model::Song&); private: + std::map changesInSong(const model::Song&, const model::Song&); + + void assignMiscId(model::Song&, const model::Song&); + void assignMiscFields(std::map&, model::Song&, + const model::Song&); void saveSongTemp(model::Song&); void saveMisc(model::Song&); void deleteMisc(const model::Song&); + void updateMisc(const std::map&, + model::Song&, const model::Song&); + void modifySongOnFilesystem(model::Song&, const model::Song&); model::BinaryPath m_bConf; std::string exe_path; diff --git a/include/manager/YearManager.h b/include/manager/YearManager.h index dbb745d..f10c55f 100644 --- a/include/manager/YearManager.h +++ b/include/manager/YearManager.h @@ -14,6 +14,7 @@ namespace manager model::Year saveYear(const model::Song&); void deleteYear(const model::Song&); + void updateYear(model::Song&, const model::Song&); static void printYear(const model::Year&); private: diff --git a/include/type/PathType.h b/include/type/PathType.h new file mode 100644 index 0000000..798f2f2 --- /dev/null +++ b/include/type/PathType.h @@ -0,0 +1,15 @@ +#ifndef PATHTYPE_H_ +#define PATHTYPE_H_ + +namespace type +{ + enum PathType + { + music = 0, + archive, + temp, + coverArt + }; +} + +#endif diff --git a/include/type/SongChanged.h b/include/type/SongChanged.h new file mode 100644 index 0000000..e5b063c --- /dev/null +++ b/include/type/SongChanged.h @@ -0,0 +1,16 @@ +#ifndef SONGCHANGED_H_ +#define SONGCHANGED_H_ + +namespace type +{ + enum SongChanged + { + title = 0, + artist, + album, + genre, + year + }; +} + +#endif diff --git a/src/database/CoverArtRepository.cpp b/src/database/CoverArtRepository.cpp index 092ba6e..7caf79e 100644 --- a/src/database/CoverArtRepository.cpp +++ b/src/database/CoverArtRepository.cpp @@ -79,6 +79,7 @@ model::Cover database::CoverArtRepository::retrieveRecord(model::Cover& cov, typ return covDb; } + bool database::CoverArtRepository::doesCoverArtExist(const model::Cover& cover, type::CoverFilter filter) { auto conn = setupMysqlConnection(); @@ -130,6 +131,7 @@ bool database::CoverArtRepository::doesCoverArtExist(const model::Cover& cover, return (rowCount > 0) ? true : false; } + // TODO: change to prepared statement void database::CoverArtRepository::deleteRecord(const model::Cover& cov) { @@ -176,6 +178,49 @@ void database::CoverArtRepository::saveRecord(const model::Cover& cov) std::cout << "saved cover art record" << std::endl; } +void database::CoverArtRepository::updateRecord(const model::Cover& cover) +{ + std::stringstream qry; + auto conn = setupMysqlConnection(); + auto stmt = mysql_stmt_init(conn); + + qry << "UPDATE CoverArt SET "; + qry << "SongTitle = ?, "; + qry << "ImagePath = ? "; + qry << "WHERE CoverArtId = ?"; + + MYSQL_BIND params[3]; + memset(params, 0, sizeof(params)); + + auto songTitleLength = cover.songTitle.size(); + auto imagePathLength = cover.imagePath.size(); + + params[0].buffer_type = MYSQL_TYPE_STRING; + params[0].buffer = (char*)cover.songTitle.c_str(); + params[0].length = &songTitleLength; + params[0].is_null = 0; + + params[1].buffer_type = MYSQL_TYPE_STRING; + params[1].buffer = (char*)cover.imagePath.c_str(); + params[1].length = &imagePathLength; + params[1].is_null = 0; + + params[2].buffer_type = MYSQL_TYPE_LONG; + params[2].buffer = (char*)&cover.id; + params[2].length = 0; + params[2].is_null = 0; + + const std::string query = qry.str(); + auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size()); + status = mysql_stmt_bind_param(stmt, params); + status = mysql_stmt_execute(stmt); + + mysql_stmt_close(stmt); + mysql_close(conn); + + std::cout << "updated cover art record" << std::endl; +} + std::vector database::CoverArtRepository::parseRecords(MYSQL_STMT *stmt) { diff --git a/src/database/SongRepository.cpp b/src/database/SongRepository.cpp index 1561843..4039407 100644 --- a/src/database/SongRepository.cpp +++ b/src/database/SongRepository.cpp @@ -31,6 +31,7 @@ std::vector database::SongRepository::retrieveRecords() return songs; } + model::Song database::SongRepository::retrieveRecord(model::Song& song, type::SongFilter filter) { std::stringstream qry; @@ -82,6 +83,7 @@ model::Song database::SongRepository::retrieveRecord(model::Song& song, type::So return song; } + bool database::SongRepository::doesSongExist(const model::Song& song, type::SongFilter filter) { std::cout << "checking to see if song exists" << std::endl; @@ -250,6 +252,98 @@ void database::SongRepository::saveRecord(const model::Song& song) std::cout << "done inserting song record" << std::endl; } +void database::SongRepository::updateRecord(const model::Song& song) +{ + std::cout << "executing query to update record" << std::endl; + auto conn = setupMysqlConnection(); + auto stmt = mysql_stmt_init(conn); + + std::stringstream qry; + qry << "UPDATE Song SET Title = ?, Artist = ?, Album = ?, Genre = ?, "; + qry << "Year = ?, SongPath = ?, CoverArtId = ?, ArtistId = ?, "; + qry << "AlbumId = ?, GenreId = ?, YearId = ? WHERE SongId = ?"; + const auto query = qry.str(); + + auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size()); + + MYSQL_BIND params[12]; + memset(params, 0, sizeof(params)); + + params[0].buffer_type = MYSQL_TYPE_STRING; + params[0].buffer = (char*)song.title.c_str(); + auto titleLength = song.title.size(); + params[0].length = &titleLength; + params[0].is_null = 0; + + params[1].buffer_type = MYSQL_TYPE_STRING; + params[1].buffer = (char*)song.artist.c_str(); + auto artistLength = song.artist.size(); + params[1].length = &artistLength; + params[1].is_null = 0; + + params[2].buffer_type = MYSQL_TYPE_STRING; + params[2].buffer = (char*)song.album.c_str(); + auto albumLength = song.album.size(); + params[2].length = &albumLength; + params[2].is_null = 0; + + params[3].buffer_type = MYSQL_TYPE_STRING; + params[3].buffer = (char*)song.genre.c_str(); + auto genreLength = song.genre.size(); + params[3].length = &genreLength; + params[3].is_null = 0; + + params[4].buffer_type = MYSQL_TYPE_LONG; + params[4].buffer = (char*)&song.year; + params[4].length = 0; + params[4].is_null = 0; + + params[5].buffer_type = MYSQL_TYPE_STRING; + params[5].buffer = (char*)song.songPath.c_str(); + auto pathLength = song.songPath.size(); + params[5].length = &pathLength; + params[5].is_null = 0; + + params[6].buffer_type = MYSQL_TYPE_LONG; + params[6].buffer = (char*)&song.coverArtId; + params[6].length = 0; + params[6].is_null = 0; + + params[7].buffer_type = MYSQL_TYPE_LONG; + params[7].buffer = (char*)&song.artistId; + params[7].length = 0; + params[7].is_null = 0; + + params[8].buffer_type = MYSQL_TYPE_LONG; + params[8].buffer = (char*)&song.albumId; + params[8].length = 0; + params[8].is_null = 0; + + params[9].buffer_type = MYSQL_TYPE_LONG; + params[9].buffer = (char*)&song.genreId; + params[9].length = 0; + params[9].is_null = 0; + + params[10].buffer_type = MYSQL_TYPE_LONG; + params[10].buffer = (char*)&song.yearId; + params[10].length = 0; + params[10].is_null = 0; + + params[11].buffer_type = MYSQL_TYPE_LONG; + params[11].buffer = (char*)&song.id; + params[11].length = 0; + params[11].is_null = 0; + + status = mysql_stmt_bind_param(stmt, params); + status = mysql_stmt_execute(stmt); + + mysql_stmt_close(stmt); + mysql_close(conn); + + std::cout << "updated song record" << std::endl; +} + + // TODO: delete this, not being used std::vector database::SongRepository::parseRecords(MYSQL_RES* results) { diff --git a/src/manager/AlbumManager.cpp b/src/manager/AlbumManager.cpp index f539312..468ea13 100644 --- a/src/manager/AlbumManager.cpp +++ b/src/manager/AlbumManager.cpp @@ -53,6 +53,25 @@ void manager::AlbumManager::deleteAlbum(const model::Song& song) albRepo.deleteAlbum(album, type::AlbumFilter::id); } +void manager::AlbumManager::updateAlbum(model::Song& updatedSong, + const model::Song& currSong) +{ + model::Album album; + album.title = updatedSong.album; + album.year = updatedSong.year; + + database::AlbumRepository albRepo(m_bConf); + if (!albRepo.doesAlbumExists(album, type::AlbumFilter::title)) { + std::cout << "album record does not exist" << std::endl; + albRepo.saveAlbum(album); + } else { + std::cout << "album record already exists" << std::endl; + } + + album = albRepo.retrieveRecord(album, type::AlbumFilter::title); + updatedSong.albumId = album.id; +} + void manager::AlbumManager::printAlbum(const model::Album& album) { std::cout << "\nalbum record" << std::endl; diff --git a/src/manager/ArtistManager.cpp b/src/manager/ArtistManager.cpp index a387dc2..14e78e8 100644 --- a/src/manager/ArtistManager.cpp +++ b/src/manager/ArtistManager.cpp @@ -36,6 +36,7 @@ model::Artist manager::ArtistManager::saveArtist(const model::Song& song) return artist; } + void manager::ArtistManager::deleteArtist(const model::Song& song) { model::Artist artist(song); @@ -53,6 +54,24 @@ void manager::ArtistManager::deleteArtist(const model::Song& song) artRepo.deleteArtist(artist, type::ArtistFilter::id); } +void manager::ArtistManager::updateArtist(model::Song& updatedSong, + const model::Song& currSong) +{ + model::Artist artist; + artist.artist = updatedSong.artist; + + database::ArtistRepository artRepo(m_bConf); + if (!artRepo.doesArtistExist(artist, type::ArtistFilter::artist)) { + std::cout << "artist record does not exist" << std::endl; + artRepo.saveRecord(artist); + } else { + std::cout << "artist record already exists" << std::endl; + } + + artist = artRepo.retrieveRecord(artist, type::ArtistFilter::artist); + updatedSong.artistId = artist.id; +} + void manager::ArtistManager::printArtist(const model::Artist& artist) { std::cout << "\nartist record" << std::endl; diff --git a/src/manager/CoverArtManager.cpp b/src/manager/CoverArtManager.cpp index c182f1a..4810cbc 100644 --- a/src/manager/CoverArtManager.cpp +++ b/src/manager/CoverArtManager.cpp @@ -6,6 +6,7 @@ #include "database/CoverArtRepository.h" #include "manager/DirectoryManager.h" #include "type/CoverFilter.h" +#include "type/PathType.h" #include "utility/MetadataRetriever.h" namespace fs = std::filesystem; @@ -40,6 +41,25 @@ model::Cover manager::CoverArtManager::saveCover(const model::Song& song, std::s } +std::pair manager::CoverArtManager::defaultCover( + const model::Cover& cover) { + + auto paths = manager::DirectoryManager::pathConfigContent(m_bConf); + const auto coverArtPath = + paths[manager::DirectoryManager::retrievePathType( + type::PathType::coverArt)].get(); + + auto stockCoverArtPath = coverArtPath; + stockCoverArtPath.append("CoverArt.png"); + + if (stockCoverArtPath.compare(cover.imagePath) == 0) { + return std::make_pair(true, coverArtPath); + } else { + return std::make_pair(false, coverArtPath); + } +} + + void manager::CoverArtManager::deleteCover(const model::Song& song) { database::CoverArtRepository covRepo(m_bConf); @@ -49,18 +69,54 @@ void manager::CoverArtManager::deleteCover(const model::Song& song) cov = covRepo.retrieveRecord(cov, type::CoverFilter::id); covRepo.deleteRecord(cov); - auto paths = manager::DirectoryManager::pathConfigContent(m_bConf); - - const auto coverArtPath = paths["cover_root_path"].get(); - std::string stockCoverArtPath = coverArtPath; - stockCoverArtPath.append("CoverArt.png"); - - if (stockCoverArtPath.compare(cov.imagePath) != 0) { + auto result = defaultCover(cov); + if (!result.first) { fs::remove(cov.imagePath); std::cout << "deleting cover art" << std::endl; + const auto coverArtPath = result.second; } else { std::cout << "song contains the stock cover art, will not delete" << std::endl; } - manager::DirectoryManager::deleteDirectories(song, coverArtPath); + manager::DirectoryManager::deleteDirectories(song, result.second); +} + +void manager::CoverArtManager::updateCover(const model::Song& updatedSong) +{ + database::CoverArtRepository covRepo(m_bConf); + model::Cover cover(updatedSong.coverArtId); + cover = covRepo.retrieveRecord(cover, type::CoverFilter::id); + + auto result = defaultCover(cover); + + if (result.first) { + return; + } + + auto imagePath = manager::DirectoryManager::createDirectoryProcess( + updatedSong, m_bConf, type::PathType::coverArt); + imagePath.append(updatedSong.title); + imagePath.append(".png"); + + if (cover.imagePath.find(".png") == std::string::npos) { + std::cout << "no file extension on image path" << std::endl; + cover.imagePath.append(".png"); + } + + fs::copy(cover.imagePath, imagePath); + fs::remove(cover.imagePath); +} + +void manager::CoverArtManager::updateCoverRecord(const model::Song& updatedSong) +{ + model::Cover updatedCover(updatedSong); + auto updatedImagePath = manager::DirectoryManager::createDirectoryProcess( + updatedSong, m_bConf, type::PathType::coverArt); + updatedImagePath.append(updatedSong.title); + + updatedCover.imagePath = std::move(updatedImagePath); + updatedCover.imagePath.append(".png"); + + database::CoverArtRepository covRepo(m_bConf); + covRepo.updateRecord(updatedCover); } diff --git a/src/manager/DirectoryManager.cpp b/src/manager/DirectoryManager.cpp index 5f20e94..4569077 100644 --- a/src/manager/DirectoryManager.cpp +++ b/src/manager/DirectoryManager.cpp @@ -7,6 +7,7 @@ namespace fs = std::filesystem; + std::string manager::DirectoryManager::createDirectoryProcess(model::Song song, const std::string& rootPath) { auto currPath = fs::path(rootPath); @@ -37,14 +38,49 @@ std::string manager::DirectoryManager::createDirectoryProcess(model::Song song, return albPath.string() + "/"; } +std::string manager::DirectoryManager::createDirectoryProcess(const model::Song& song, + const model::BinaryPath& bConf, type::PathType pType) +{ + auto path = pathConfigContent(bConf)[retrievePathType(pType)]; + auto rootPath = path.get(); + auto currPath = fs::path(rootPath); + + if (fs::exists(currPath)) { + std::cout << "path exists" << std::endl; + } else { + std::cout << "creating root music path" << std::endl; + fs::create_directory(currPath); + } + + auto artPath = fs::path(currPath.string() + song.artist); + if (fs::exists(artPath)) { + std::cout << "artist path exists" << std::endl; + } else { + std::cout << "creating artist path" << std::endl; + fs::create_directory(artPath); + } + + auto albPath = fs::path(artPath.string() + "/" + song.album); + if (fs::exists(albPath)) { + std::cout << "album path exists" << std::endl; + } else { + std::cout << "creating album path" << std::endl; + fs::create_directory(albPath); + } + + return albPath.string() + "/"; +} + std::string manager::DirectoryManager::configPath(std::string_view path) { return fs::canonical(path).parent_path().string(); } + std::string manager::DirectoryManager::configPath(const model::BinaryPath& bConf) { return fs::canonical(bConf.path).parent_path().string(); } + std::string manager::DirectoryManager::contentOfPath(const std::string& path) { std::fstream a(path, std::ios::in); @@ -55,6 +91,29 @@ std::string manager::DirectoryManager::contentOfPath(const std::string& path) return s.str(); } +std::string manager::DirectoryManager::retrievePathType(type::PathType pType) +{ + std::string path; + switch (pType) { + case type::PathType::music: + path = "root_music_path"; + break; + case type::PathType::archive: + path = "archive_root_path"; + break; + case type::PathType::temp: + path = "temp_root_path"; + break; + case type::PathType::coverArt: + path = "cover_root_path"; + break; + default: + break; + } + + return path; +} + nlohmann::json manager::DirectoryManager::credentialConfigContent(const model::BinaryPath& bConf) { auto path = configPath(bConf); @@ -62,6 +121,7 @@ nlohmann::json manager::DirectoryManager::credentialConfigContent(const model::B return nlohmann::json::parse(contentOfPath(path)); } + nlohmann::json manager::DirectoryManager::databaseConfigContent(const model::BinaryPath& bConf) { auto path = configPath(bConf); @@ -69,6 +129,7 @@ nlohmann::json manager::DirectoryManager::databaseConfigContent(const model::Bin return nlohmann::json::parse(contentOfPath(path)); } + nlohmann::json manager::DirectoryManager::pathConfigContent(const model::BinaryPath& bConf) { auto path = configPath(bConf); @@ -77,16 +138,6 @@ nlohmann::json manager::DirectoryManager::pathConfigContent(const model::BinaryP return nlohmann::json::parse(contentOfPath(path)); } -void manager::DirectoryManager::deleteCoverArtFile(const std::string& covPath, const std::string& stockCoverPath) -{ - if (covPath.compare(stockCoverPath) == 0) { - std::cout << "cover has stock cover art, will not deleted" << std::endl; - } else { - std::cout << "deleting song path" << std::endl; - auto cov = fs::path(covPath); - fs::remove(cov); - } -} void manager::DirectoryManager::deleteDirectories(model::Song song, const std::string& rootPath) { std::cout << "checking for empty directories to delete" << std::endl; @@ -112,6 +163,18 @@ void manager::DirectoryManager::deleteDirectories(model::Song song, const std::s std::cout << "deleted empty directory or directories" << std::endl; } + +void manager::DirectoryManager::deleteCoverArtFile(const std::string& covPath, const std::string& stockCoverPath) +{ + if (covPath.compare(stockCoverPath) == 0) { + std::cout << "cover has stock cover art, will not deleted" << std::endl; + } else { + std::cout << "deleting song path" << std::endl; + auto cov = fs::path(covPath); + fs::remove(cov); + } +} + void manager::DirectoryManager::deleteSong(const model::Song song) { std::cout << "deleting song" << std::endl; diff --git a/src/manager/GenreManager.cpp b/src/manager/GenreManager.cpp index 4cab7f5..65b4e70 100644 --- a/src/manager/GenreManager.cpp +++ b/src/manager/GenreManager.cpp @@ -33,6 +33,7 @@ model::Genre manager::GenreManager::saveGenre(const model::Song& song) return genre; } + void manager::GenreManager::deleteGenre(const model::Song& song) { model::Genre genre(song); @@ -50,6 +51,24 @@ void manager::GenreManager::deleteGenre(const model::Song& song) gnrRepo.deleteRecord(genre, type::GenreFilter::id); } +void manager::GenreManager::updateGenre(model::Song& updatedSong, + const model::Song& currSong) +{ + model::Genre genre; + genre.category = updatedSong.genre; + + database::GenreRepository gnrRepo(m_bConf); + if (!gnrRepo.doesGenreExist(genre, type::GenreFilter::category)) { + std::cout << "genre record does not exist" << std::endl; + gnrRepo.saveRecord(genre); + } else { + std::cout << "genre record already exists" << std::endl; + } + + genre = gnrRepo.retrieveRecord(genre, type::GenreFilter::category); + updatedSong.genreId = genre.id; +} + void manager::GenreManager::printGenre(const model::Genre& genre) { std::cout << "genre record" << std::endl; diff --git a/src/manager/SongManager.cpp b/src/manager/SongManager.cpp index 2da917c..293cc19 100644 --- a/src/manager/SongManager.cpp +++ b/src/manager/SongManager.cpp @@ -16,6 +16,7 @@ #include "manager/DirectoryManager.h" #include "manager/GenreManager.h" #include "manager/YearManager.h" +#include "type/PathType.h" #include "utility/MetadataRetriever.h" namespace fs = std::filesystem; @@ -32,7 +33,21 @@ manager::SongManager::SongManager(const model::BinaryPath& bConf) bool manager::SongManager::didSongChange(const model::Song& updatedSong, const model::Song& currSong) { - // TODO: implement this + if (!updatedSong.title.empty()) { + return true; + } + if (!updatedSong.artist.empty()) { + return true; + } + if (!updatedSong.album.empty()) { + return true; + } + if (updatedSong.genre.empty()) { + return true; + } + if (updatedSong.year != 0) { + return true; + } return false; } @@ -53,6 +68,7 @@ bool manager::SongManager::requiresFilesystemChange(const model::Song& updatedSo return false; } + void manager::SongManager::saveSong(model::Song& song) { saveSongTemp(song); @@ -102,17 +118,27 @@ void manager::SongManager::updateSong(model::Song& updatedSong) OATPP_ASSERT_HTTP(songRepo.doesSongExist(currSong, type::SongFilter::id) , oatpp::web::protocol::http::Status::CODE_404, "song does not exist"); currSong = songRepo.retrieveRecord(currSong, type::SongFilter::id); + if (!didSongChange(updatedSong, currSong)) { + std::cout << "no change to the song" << std::endl; + return; + } + + assignMiscId(updatedSong, currSong); + + auto changes = changesInSong(updatedSong, currSong); + utility::MetadataRetriever meta; meta.updateMetadata(updatedSong, currSong); + assignMiscFields(changes, updatedSong, currSong); - // TODO: - // 1. determine if the song has been updated at all - // 2. depending on what metadata was changed there might be a need to - // change where the song is stored on the filesystem, deleting empty - // directories in the process - // 3. create or delete necessary records if (requiresFilesystemChange(updatedSong, currSong)) { + modifySongOnFilesystem(updatedSong, currSong); } + + printSong(updatedSong); + printSong(currSong); + + updateMisc(changes, updatedSong, currSong); } model::Song manager::SongManager::songDtoConv(dto::SongDto::ObjectWrapper& songDto) @@ -150,6 +176,90 @@ void manager::SongManager::printSong(const model::Song& song) std::cout << "year id: " << song.yearId << std::endl; } + +std::map manager::SongManager::changesInSong( + const model::Song& updatedSong, const model::Song& currSong) +{ + std::map songChanges; + + std::string_view updatedTitle = updatedSong.title; + std::string_view updatedArtist = updatedSong.artist; + std::string_view updatedAlbum = updatedSong.album; + std::string_view updatedGenre = updatedSong.genre; + + songChanges[type::SongChanged::title] = + (currSong.title.compare(updatedTitle) != 0 && + updatedTitle.size() > 0) ? true : false; + + songChanges[type::SongChanged::artist] = + (currSong.artist.compare(updatedArtist) != 0 && + updatedArtist.size() > 0) ? true : false; + + songChanges[type::SongChanged::album] = + (currSong.album.compare(updatedAlbum) != 0 && + updatedAlbum.size() > 0) ? true : false; + + songChanges[type::SongChanged::genre] = + (currSong.genre.compare(updatedGenre) != 0 && + updatedGenre.size() > 0) ? true : false; + + songChanges[type::SongChanged::year] = + (updatedSong.year != 0) ? true : false; + + return songChanges; +} + + +// used to prevent empty values to appear in the updated song +void manager::SongManager::assignMiscFields( + std::map& songChanges, model::Song& updatedSong, + const model::Song& currSong) +{ + std::cout << "assigning miscellanes fields to updated song" << std::endl; + for (auto scIter = songChanges.begin(); scIter != songChanges.end(); ++scIter) { + type::SongChanged key = scIter->first; + bool changed = songChanges.at(key); + + if (!changed) { + switch (key) { + case type::SongChanged::title: + updatedSong.title = currSong.title; + std::cout << "title has not been changed" << std::endl; + break; + case type::SongChanged::artist: + updatedSong.artist = currSong.artist; + std::cout << "artist has not been changed" << std::endl; + break; + case type::SongChanged::album: + updatedSong.album = currSong.album; + std::cout << "album has not been changed" << std::endl; + break; + case type::SongChanged::genre: + updatedSong.genre = currSong.genre; + std::cout << "genre has not been changed" << std::endl; + break; + case::type::SongChanged::year: + updatedSong.year = currSong.year; + std::cout << "year has not been changed" << std::endl; + default: + break; + } + } + } +} + +// used to dump miscellaneous id to the updated song +void manager::SongManager::assignMiscId(model::Song& updatedSong, + const model::Song& currSong) +{ + std::cout << "assigning miscellaneous Id's to updated song" << std::endl; + updatedSong.artistId = currSong.artistId; + updatedSong.albumId = currSong.albumId; + updatedSong.genreId = currSong.genreId; + updatedSong.yearId = currSong.yearId; + updatedSong.coverArtId = currSong.coverArtId; +} + void manager::SongManager::saveSongTemp(model::Song& song) { auto config = manager::DirectoryManager::pathConfigContent(m_bConf); @@ -240,3 +350,60 @@ void manager::SongManager::deleteMisc(const model::Song& song) manager::YearManager yrMgr(m_bConf); yrMgr.deleteYear(song); } + +void manager::SongManager::updateMisc( + const std::map& songChanges, + model::Song& updatedSong, const model::Song& currSong) +{ + auto titleChange = songChanges.at(type::SongChanged::title); + auto artistChange = songChanges.at(type::SongChanged::artist); + auto albumChange = songChanges.at(type::SongChanged::album); + auto genreChange = songChanges.at(type::SongChanged::genre); + auto yearChange = songChanges.at(type::SongChanged::year); + + if (artistChange) { + manager::ArtistManager artMgr(m_bConf); + artMgr.updateArtist(updatedSong, currSong); + } + if (albumChange) { + manager::AlbumManager albMgr(m_bConf); + albMgr.updateAlbum(updatedSong, currSong); + } + if (genreChange) { + manager::GenreManager gnrMgr(m_bConf); + gnrMgr.updateGenre(updatedSong, currSong); + } + if (yearChange) { + manager::YearManager yrMgr(m_bConf); + yrMgr.updateYear(updatedSong, currSong); + } + + // determins to update the cover art record + if (titleChange || artistChange || albumChange) { + manager::CoverArtManager covMgr(m_bConf); + covMgr.updateCoverRecord(updatedSong); + } + + database::SongRepository songRepo(m_bConf); + songRepo.updateRecord(updatedSong); +} + +void manager::SongManager::modifySongOnFilesystem(model::Song& updatedSong, + const model::Song& currSong) +{ + std::cout << "preparing to modify song" << std::endl; + printSong(updatedSong); + auto songPath = manager::DirectoryManager::createDirectoryProcess( + updatedSong, m_bConf, type::PathType::music); + songPath.append(updatedSong.title); + songPath.append(".mp3"); + updatedSong.songPath = std::move(songPath); + + std::cout << "new path " << updatedSong.songPath << std::endl; + + fs::copy(currSong.songPath, updatedSong.songPath); + fs::remove(currSong.songPath); + + manager::CoverArtManager covMgr(m_bConf); + covMgr.updateCover(updatedSong); +} diff --git a/src/manager/YearManager.cpp b/src/manager/YearManager.cpp index 2e715aa..e8bd29f 100644 --- a/src/manager/YearManager.cpp +++ b/src/manager/YearManager.cpp @@ -50,6 +50,24 @@ void manager::YearManager::deleteYear(const model::Song& song) yrRepo.deleteYear(year, type::YearFilter::id); } +void manager::YearManager::updateYear(model::Song& updatedSong, + const model::Song& currSong) +{ + model::Year year; + year.year = updatedSong.year; + + database::YearRepository albRepo(m_bConf); + if (!albRepo.doesYearExist(year, type::YearFilter::year)) { + std::cout << "year record does not exist" << std::endl; + albRepo.saveRecord(year); + } else { + std::cout << "year record already exists" << std::endl; + } + + year = albRepo.retrieveRecord(year, type::YearFilter::year); + updatedSong.yearId = year.id; +} + void manager::YearManager::printYear(const model::Year& year) { std::cout << "\nyear record" << std::endl;