diff --git a/Scripts/MySQL/create_database.sql b/Scripts/MySQL/create_database.sql index fde8f28..929f5c8 100644 --- a/Scripts/MySQL/create_database.sql +++ b/Scripts/MySQL/create_database.sql @@ -13,6 +13,7 @@ CREATE TABLE CoverArt ( CREATE TABLE Album ( AlbumId INT NOT NULL AUTO_INCREMENT, Title TEXT NOT NULL, + Artist TEXT NOT NULL, Year INT NOT NULL, PRIMARY KEY (AlbumId) diff --git a/include/controller/AlbumController.hpp b/include/controller/AlbumController.hpp index 62dafe5..60f08e5 100644 --- a/include/controller/AlbumController.hpp +++ b/include/controller/AlbumController.hpp @@ -19,6 +19,7 @@ #include "database/AlbumRepository.h" #include "dto/AlbumDto.hpp" +#include "dto/conversion/DtoConversions.h" #include "manager/AlbumManager.h" #include "manager/TokenManager.h" #include "model/Models.h" @@ -57,10 +58,7 @@ public: auto albums = oatpp::data::mapping::type::List::createShared(); for (auto& albDb : albsDb) { - auto alb = dto::AlbumDto::createShared(); - alb->id = albDb.id; - alb->title = albDb.title.c_str(); - alb->year = albDb.year; + auto alb = dto::conversion::DtoConversions::toAlbumDto(albDb); albums->pushBack(alb); } @@ -85,10 +83,7 @@ public: std::cout << "album exists" << std::endl; albDb = albRepo.retrieveRecord(albDb, type::AlbumFilter::id); - auto album = dto::AlbumDto::createShared(); - album->id = albDb.id; - album->title = albDb.title.c_str(); - album->year = albDb.year; + auto album = dto::conversion::DtoConversions::toAlbumDto(albDb); return createDtoResponse(Status::CODE_200, album); } diff --git a/include/database/AlbumRepository.h b/include/database/AlbumRepository.h index f831e02..45c9432 100644 --- a/include/database/AlbumRepository.h +++ b/include/database/AlbumRepository.h @@ -1,6 +1,7 @@ #ifndef ALBUMREPOSITORY_H_ #define ALBUMREPOSITORY_H_ +#include #include #include @@ -30,9 +31,13 @@ namespace database std::pair parseRecordWithSongCount(MYSQL_STMT*); - // TODO: after parseRecord(MYSQL_STMT*) is implemented remove - // parseRecord(MYSQL_RES*) - model::Album parseRecord(MYSQL_RES*); + std::shared_ptr valueBind(model::Album&, + std::tuple&); + std::shared_ptr valueBindWithSongCount(model::Album&, + std::tuple&, int&); + + std::tuple metadataBuffer(); + model::Album parseRecord(MYSQL_STMT*); }; } diff --git a/include/database/SongRepository.h b/include/database/SongRepository.h index 9ac1d73..946bd9e 100644 --- a/include/database/SongRepository.h +++ b/include/database/SongRepository.h @@ -16,7 +16,6 @@ namespace database class SongRepository : public BaseRepository { public: - SongRepository(const std::string&); SongRepository(const model::BinaryPath&); std::vector retrieveRecords(); @@ -30,9 +29,9 @@ namespace database void updateRecord(const model::Song&); private: std::shared_ptr valueBind(model::Song&, - std::tuple&); + std::tuple&); - std::tuple metadataBuffer(); + std::tuple metadataBuffer(); std::vector parseRecords(MYSQL_STMT*); diff --git a/include/dto/AlbumDto.hpp b/include/dto/AlbumDto.hpp index 56e6b21..93c6dba 100644 --- a/include/dto/AlbumDto.hpp +++ b/include/dto/AlbumDto.hpp @@ -14,6 +14,7 @@ namespace dto DTO_FIELD(Int32, id); DTO_FIELD(String, title); + DTO_FIELD(String, artist); DTO_FIELD(Int32, year); }; diff --git a/include/dto/SongDto.hpp b/include/dto/SongDto.hpp index 48e181c..6ddd32f 100644 --- a/include/dto/SongDto.hpp +++ b/include/dto/SongDto.hpp @@ -17,6 +17,7 @@ namespace dto DTO_FIELD(Int32, id); DTO_FIELD(String, title); DTO_FIELD(String, artist); + DTO_FIELD(String, album_artist); DTO_FIELD(String, album); DTO_FIELD(String, genre); DTO_FIELD(Int32, track); diff --git a/include/dto/conversion/DtoConversions.h b/include/dto/conversion/DtoConversions.h index 5f4554f..d22b9d1 100644 --- a/include/dto/conversion/DtoConversions.h +++ b/include/dto/conversion/DtoConversions.h @@ -1,6 +1,7 @@ #ifndef DTOCONVERSIONS_H_ #define DTOCONVERSIONS_H_ +#include "dto/AlbumDto.hpp" #include "dto/LoginResultDto.hpp" #include "dto/SongDto.hpp" #include "model/Models.h" @@ -13,6 +14,8 @@ namespace dto { namespace conversion { static dto::RegisterResultDto::ObjectWrapper toRegisterResultDto( const model::RegisterResult&); + static dto::AlbumDto::ObjectWrapper toAlbumDto(const model::Album&); + static dto::SongDto::ObjectWrapper toSongDto(const model::Song&); static model::Song toSong(dto::SongDto::ObjectWrapper&); diff --git a/include/model/Models.h b/include/model/Models.h index 0e80dfb..0fbdc98 100644 --- a/include/model/Models.h +++ b/include/model/Models.h @@ -5,14 +5,24 @@ #include namespace model { -struct Song { +class Song { +public: Song() = default; Song(const int id) : id(id) { } + Song(const int id, const std::string& title, const std::string& artist, + const std::string& album, const std::string& albumArtist, + const std::string& genre, const int year, const int duration, + const int track, const int disc, const std::string songPath) : + id(id), title(title), artist(artist), album(album), + albumArtist(albumArtist), genre(genre), year(year), + duration(duration), track(track), disc(disc), + songPath(songPath) { } int id; std::string title; std::string artist; std::string album; + std::string albumArtist; std::string genre; int year; int duration; @@ -27,42 +37,35 @@ struct Song { int yearId; }; -struct Artist { +class Artist { +public: Artist() = default; - Artist(const Song& song) - { - id = song.artistId; - artist = song.artist; - } + Artist(const Song& song) : id(song.artistId), artist(song.artist) { } Artist(const int id) : id(id) { } int id; std::string artist; }; -struct Album { +class Album { +public: Album() = default; - Album(const Song& song) - { - id = song.albumId; - title = song.album; - year = song.year; - } + Album(const Song& song) : + id(song.albumId), title(song.album),artist(song.artist), year(song.year) { } Album(const int id) : id(id) { } int id; std::string title; + std::string artist; int year; std::vector songs; }; -struct Genre { +class Genre { +public: Genre() = default; - Genre(const Song& song) - { - id = song.genreId; - category = song.genre; - } + Genre(const Song& song) : + id(song.genreId), category(song.genre) { } Genre(const int id) : id(id) { } int id; @@ -70,26 +73,22 @@ struct Genre { }; -struct Year { +class Year { +public: Year() = default; - Year(const Song& song) - { - id = song.yearId; - year = song.year; - } + Year(const Song& song) : + id(song.yearId), year(song.year) { } Year(const int id) : id(id) { } int id; int year; }; -struct Cover { +class Cover { +public: Cover() = default; - Cover(const Song& song) - { - id = song.coverArtId; - songTitle = song.title; - } + Cover(const Song& song) : + id(song.coverArtId), songTitle(song.title) { } Cover(const int id) : id(id) { } int id; @@ -163,11 +162,12 @@ struct DatabaseConnection { std::string database; }; -struct BinaryPath { +class BinaryPath { +public: BinaryPath() = default; - BinaryPath(const char *p) : path(std::move(p)) { } - BinaryPath(std::string& p) : path(p) { } + BinaryPath(const char *p) : path(p) { } BinaryPath(const std::string& p) : path(p) { } + BinaryPath(const std::string&& p) : path(p) { } std::string path; }; diff --git a/include/utility/MetadataRetriever.h b/include/utility/MetadataRetriever.h index ebc539e..42093ea 100644 --- a/include/utility/MetadataRetriever.h +++ b/include/utility/MetadataRetriever.h @@ -10,7 +10,7 @@ namespace utility { class MetadataRetriever { public: - model::Song retrieveMetadata(std::string&); + model::Song retrieveMetadata(model::Song&); model::Cover updateCoverArt(const model::Song&, model::Cover&, const std::string&); model::Cover applyStockCoverArt(const model::Song&, model::Cover&, const std::string&); diff --git a/src/database/AlbumRepository.cpp b/src/database/AlbumRepository.cpp index d2c6687..a5ea1cb 100644 --- a/src/database/AlbumRepository.cpp +++ b/src/database/AlbumRepository.cpp @@ -38,7 +38,7 @@ std::pair AlbumRepository::retrieveRecordWithSongCount(model: auto conn = setupMysqlConnection(); auto stmt = mysql_stmt_init(conn); - MYSQL_BIND params[4]; + MYSQL_BIND params[1]; std::memset(params, 0, sizeof(params)); qry << "SELECT alb.*, COUNT(*) AS SongCount FROM Album alb LEFT JOIN "; @@ -181,11 +181,11 @@ void AlbumRepository::saveAlbum(const model::Album& album) auto conn = setupMysqlConnection(); MYSQL_STMT *stmt = mysql_stmt_init(conn); - const std::string query = "INSERT INTO Album(Title, Year) VALUES(?, ?)"; + const std::string query = "INSERT INTO Album(Title, Artist, Year) VALUES(?, ?, ?)"; auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size()); - MYSQL_BIND params[2]; + MYSQL_BIND params[3]; memset(params, 0, sizeof(params)); params[0].buffer_type = MYSQL_TYPE_STRING; @@ -194,11 +194,17 @@ void AlbumRepository::saveAlbum(const model::Album& album) params[0].length= &titleLength; params[0].is_null = 0; - params[1].buffer_type = MYSQL_TYPE_LONG; - params[1].buffer = (char*)&album.year; - params[1].length = 0; + params[1].buffer_type = MYSQL_TYPE_STRING; + params[1].buffer = (char*)album.artist.c_str(); + auto artistLength = album.artist.size(); + params[1].length = &artistLength; params[1].is_null = 0; + params[2].buffer_type = MYSQL_TYPE_LONG; + params[2].buffer = (char*)&album.year; + params[2].length = 0; + params[2].is_null = 0; + status = mysql_stmt_bind_param(stmt, params); status = mysql_stmt_execute(stmt); @@ -259,32 +265,9 @@ std::vector AlbumRepository::parseRecords(MYSQL_STMT* stmt) for (auto status = 0; status == 0; status = mysql_stmt_next_result(stmt)) { if (mysql_stmt_field_count(stmt) > 0) { model::Album alb; - auto res = mysql_stmt_result_metadata(stmt); - auto fields = mysql_fetch_fields(res); - const auto strLen = 1024; - - MYSQL_BIND val[valAmt]; - memset(val, 0, sizeof(val)); - - char title[strLen]; - - val[0].buffer_type = MYSQL_TYPE_LONG; - val[0].buffer = (char*)&alb.id; - val[0].length = &len[0]; - val[0].is_null = &nullRes[0]; - - val[1].buffer_type = MYSQL_TYPE_STRING; - val[1].buffer = (char*)title; - val[1].buffer_length = strLen; - val[1].length = &len[1]; - val[1].is_null = &nullRes[1]; - - val[2].buffer_type = MYSQL_TYPE_LONG; - val[2].buffer = (char*)&alb.year; - val[2].length = &len[2]; - val[2].is_null = &nullRes[2]; - - status = mysql_stmt_bind_result(stmt, val); + auto metaBuff = metadataBuffer(); + auto bindedValues = valueBind(alb, metaBuff); + status = mysql_stmt_bind_result(stmt, bindedValues.get()); while (true) { std::cout << "fetching statement result" << std::endl; @@ -294,7 +277,8 @@ std::vector AlbumRepository::parseRecords(MYSQL_STMT* stmt) break; } - alb.title = title; + alb.title = std::get<0>(metaBuff); + alb.artist = std::get<1>(metaBuff); albums.push_back(std::move(alb)); } } @@ -304,31 +288,6 @@ std::vector AlbumRepository::parseRecords(MYSQL_STMT* stmt) return albums; } -// TODO: check to see if this is not used, if not then remove it -model::Album AlbumRepository::parseRecord(MYSQL_RES* results) -{ - std::cout << "parsing album record" << std::endl; - model::Album album; - - auto fieldNum = mysql_num_fields(results); - auto row = mysql_fetch_row(results); - - for (auto i = 0; i != fieldNum; ++i) { - const std::string field(mysql_fetch_field(results)->name); - - if (field.compare("AlbumId") == 0) { - album.id = std::stoi(row[i]); - } - if (field.compare("Title") == 0) { - album.title = row[i]; - } - if (field.compare("Year") == 0) { - album.year = std::stoi(row[i]); - } - } - - return album; -} std::pair AlbumRepository::parseRecordWithSongCount(MYSQL_STMT *stmt) { @@ -337,54 +296,20 @@ std::pair AlbumRepository::parseRecordWithSongCount(MYSQL_STM auto rowCount = mysql_stmt_num_rows(stmt); model::Album album; + auto metaBuff = metadataBuffer(); int songCount = 0; + auto val = valueBindWithSongCount(album, metaBuff, songCount); if (rowCount == 0) { std::cout << "no results" << std::endl; return std::make_pair(album, songCount); } - if (mysql_stmt_field_count(stmt) == 0) { - std::cout << "field count is 0, must be an incorrect query" << std::endl; - return std::make_pair(model::Album(), 0); - } - - auto res = mysql_stmt_result_metadata(stmt); - constexpr auto valAmt = 4; - constexpr auto strLen = 1024; - unsigned long len[valAmt]; - my_bool nullRes[valAmt]; - - MYSQL_BIND val[valAmt]; - std::memset(val, 0, sizeof(val)); - - char title[strLen]; - - val[0].buffer_type = MYSQL_TYPE_LONG; - val[0].buffer = (char*)&album.id; - val[0].length = &len[0]; - val[0].is_null = &nullRes[0]; - - val[1].buffer_type = MYSQL_TYPE_STRING; - val[1].buffer = (char*)title; - val[1].buffer_length = strLen; - val[1].length = &len[1]; - val[1].is_null = &nullRes[1]; - - val[2].buffer_type = MYSQL_TYPE_LONG; - val[2].buffer = (char*)&album.year; - val[2].length = &len[2]; - val[2].is_null = &nullRes[2]; - - val[3].buffer_type = MYSQL_TYPE_LONG; - val[3].buffer = (char*)&songCount; - val[3].length = &len[3]; - val[3].is_null = &nullRes[3]; - - auto status = mysql_stmt_bind_result(stmt, val); + auto status = mysql_stmt_bind_result(stmt, val.get()); status = mysql_stmt_fetch(stmt); - album.title = std::move(title); + album.title = std::get<0>(metaBuff); + album.artist = std::get<1>(metaBuff); std::cout << "done parsing album record with song count" << std::endl; @@ -393,6 +318,69 @@ std::pair AlbumRepository::parseRecordWithSongCount(MYSQL_STM return albWSC; } + +std::shared_ptr AlbumRepository::valueBind(model::Album& album, + std::tuple& metadata) +{ + constexpr auto wordLen = 1024; + constexpr auto valueCount = 4; + std::shared_ptr values((MYSQL_BIND*) std::calloc(valueCount, sizeof(MYSQL_BIND))); + + values.get()[0].buffer_type = MYSQL_TYPE_LONG; + values.get()[0].buffer = (char*)&album.id; + + values.get()[1].buffer_type = MYSQL_TYPE_STRING; + values.get()[1].buffer = (char*)std::get<0>(metadata); + values.get()[1].buffer_length = wordLen; + + values.get()[2].buffer_type = MYSQL_TYPE_STRING; + values.get()[2].buffer = (char*)std::get<1>(metadata); + values.get()[2].buffer_length = wordLen; + + values.get()[3].buffer_type = MYSQL_TYPE_LONG; + values.get()[3].buffer = (char*)&album.year; + + return values; +} + +std::shared_ptr AlbumRepository::valueBindWithSongCount(model::Album& album, + std::tuple& metadata, int& songCount) +{ + constexpr auto wordLen = 1024; + constexpr auto valueCount = 5; + std::shared_ptr values((MYSQL_BIND*) std::calloc(valueCount, sizeof(MYSQL_BIND))); + + values.get()[0].buffer_type = MYSQL_TYPE_LONG; + values.get()[0].buffer = (char*)&album.id; + + values.get()[1].buffer_type = MYSQL_TYPE_STRING; + values.get()[1].buffer = (char*)std::get<0>(metadata); + values.get()[1].buffer_length = wordLen; + + values.get()[2].buffer_type = MYSQL_TYPE_STRING; + values.get()[2].buffer = (char*)std::get<1>(metadata); + values.get()[2].buffer_length = wordLen; + + values.get()[3].buffer_type = MYSQL_TYPE_LONG; + values.get()[3].buffer = (char*)&album.year; + + values.get()[4].buffer_type = MYSQL_TYPE_LONG; + values.get()[4].buffer = (char*)&songCount; + + return values; +} + + +std::tuple AlbumRepository::metadataBuffer() +{ + constexpr auto wordLen = 1024; + char title[wordLen]; + char artist[wordLen]; + + return std::make_tuple(title, artist); +} + + model::Album AlbumRepository::parseRecord(MYSQL_STMT *stmt) { std::cout << "parsing album record" << std::endl; @@ -400,49 +388,13 @@ model::Album AlbumRepository::parseRecord(MYSQL_STMT *stmt) auto rows = mysql_stmt_num_rows(stmt); model::Album album; - auto status = 0; - auto time = 0; - auto valAmt = 3; - unsigned long len[valAmt]; - my_bool nullRes[valAmt]; - - while (status == 0) { - if (mysql_stmt_field_count(stmt) > 0) { - auto res = mysql_stmt_result_metadata(stmt); - auto fields = mysql_fetch_fields(res); - auto strLen = 1024; - - MYSQL_BIND val[valAmt]; - memset(val, 0, sizeof(val)); - - char title[strLen]; - - val[0].buffer_type = MYSQL_TYPE_LONG; - val[0].buffer = (char*)&album.id; - val[0].length = &len[0]; - val[0].is_null = &nullRes[0]; - - val[1].buffer_type = MYSQL_TYPE_STRING; - val[1].buffer = (char*)title; - val[1].buffer_length = strLen; - val[1].length = &len[1]; - val[1].is_null = &nullRes[1]; - - val[2].buffer_type = MYSQL_TYPE_LONG; - val[2].buffer = (char*)&album.year; - val[2].length = &len[2]; - val[2].is_null = &nullRes[2]; - - status = mysql_stmt_bind_result(stmt, val); - mysql_stmt_store_result(stmt); - - status = mysql_stmt_fetch(stmt); - - album.title = title; - } - - status = mysql_stmt_next_result(stmt); - } + auto metaBuff = metadataBuffer(); + auto bindedValues = valueBind(album, metaBuff); + auto status = mysql_stmt_bind_result(stmt, bindedValues.get()); + status = mysql_stmt_fetch(stmt); + + album.title = std::get<0>(metaBuff); + album.artist = std::get<1>(metaBuff); std::cout << "done parsing album record" << std::endl; diff --git a/src/database/SongRepository.cpp b/src/database/SongRepository.cpp index 473fe60..680094a 100644 --- a/src/database/SongRepository.cpp +++ b/src/database/SongRepository.cpp @@ -9,9 +9,6 @@ namespace database { -SongRepository::SongRepository(const std::string& path) : BaseRepository(path) -{ } - SongRepository::SongRepository(const model::BinaryPath& bConf) : BaseRepository(bConf) { } @@ -20,7 +17,10 @@ std::vector SongRepository::retrieveRecords() { auto conn = setupMysqlConnection(); auto stmt = mysql_stmt_init(conn); - const std::string query = "SELECT * FROM Song"; + std::stringstream qry; + qry << "SELECT sng.*, alb.Artist AS AlbumArtist FROM Song sng "; + qry << "LEFT JOIN Album alb ON sng.AlbumId=alb.AlbumId"; + const auto query = qry.str(); ::mysql_stmt_prepare(stmt, query.c_str(), query.size()); ::mysql_stmt_execute(stmt); @@ -52,7 +52,8 @@ model::Song SongRepository::retrieveRecord(model::Song& song, type::SongFilter f MYSQL_BIND params[valueFilterCount]; memset(params, 0, sizeof(params)); - qry << "SELECT * FROM Song WHERE "; + qry << "SELECT sng.*, alb.Artist AS AlbumArtist FROM Song sng "; + qry << "LEFT JOIN Album alb ON sng.AlbumId=alb.AlbumId WHERE "; auto titleLength = song.title.size(); auto artistLength = song.artist.size(); @@ -92,7 +93,7 @@ model::Song SongRepository::retrieveRecord(model::Song& song, type::SongFilter f qry << " LIMIT 1"; - const std::string query = qry.str(); + const auto 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); @@ -393,10 +394,10 @@ void SongRepository::updateRecord(const model::Song& song) std::shared_ptr SongRepository::valueBind(model::Song& song, - std::tuple& metadata) + std::tuple& metadata) { constexpr auto strLen = 1024; - constexpr auto valueCount = 15; + constexpr auto valueCount = 16; std::shared_ptr values((MYSQL_BIND*) std::calloc(valueCount, sizeof(MYSQL_BIND))); unsigned long len[valueCount]; my_bool nullRes[valueCount]; @@ -452,11 +453,15 @@ std::shared_ptr SongRepository::valueBind(model::Song& song, values.get()[14].buffer_type = MYSQL_TYPE_LONG; values.get()[14].buffer = (char*)&song.yearId; + values.get()[15].buffer_type = MYSQL_TYPE_STRING; + values.get()[15].buffer = (char*)std::get<5>(metadata); + values.get()[15].buffer_length = strLen; + return values; } -std::tuple SongRepository::metadataBuffer() +std::tuple SongRepository::metadataBuffer() { constexpr auto length = 1024; char title[length]; @@ -464,8 +469,9 @@ std::tuple SongRepository::metadataBuffer() char album[length]; char genre[length]; char path[length]; + char albumArtist[length]; - return std::make_tuple(title, artist, album, genre, path); + return std::make_tuple(title, artist, album, genre, path, albumArtist); } @@ -501,6 +507,7 @@ std::vector SongRepository::parseRecords(MYSQL_STMT *stmt) song.album = std::get<2>(metaBuff); song.genre = std::get<3>(metaBuff); song.songPath = std::get<4>(metaBuff); + song.albumArtist = std::get<5>(metaBuff); songs.push_back(song); } @@ -526,6 +533,7 @@ model::Song SongRepository::parseRecord(MYSQL_STMT *stmt) song.album = std::get<2>(metaBuff); song.genre = std::get<3>(metaBuff); song.songPath = std::get<4>(metaBuff); + song.albumArtist = std::get<5>(metaBuff); std::cout << "done parsing record" << std::endl; diff --git a/src/dto/conversion/DtoConversions.cpp b/src/dto/conversion/DtoConversions.cpp index edb7f3f..cac2230 100644 --- a/src/dto/conversion/DtoConversions.cpp +++ b/src/dto/conversion/DtoConversions.cpp @@ -24,12 +24,24 @@ namespace dto { namespace conversion { } + dto::AlbumDto::ObjectWrapper DtoConversions::toAlbumDto(const model::Album& album) { + auto result = dto::AlbumDto::createShared(); + result->id = (album.id != 0) ? album.id : 0; + result->title = (!album.title.empty()) ? album.title.c_str() : ""; + result->artist = (!album.artist.empty()) ? album.artist.c_str() : ""; + result->year = (album.year != 0) ? album.year : 0; + + return result; + } + + dto::SongDto::ObjectWrapper DtoConversions::toSongDto(const model::Song& song) { auto result = dto::SongDto::createShared(); result->id = (song.id != 0) ? song.id : 0; result->title = (!song.title.empty()) ? song.title.c_str() : ""; result->album = (!song.album.empty()) ? song.album.c_str() : ""; result->artist = (!song.artist.empty()) ? song.artist.c_str() : ""; + result->album_artist = (!song.albumArtist.empty()) ? song.albumArtist.c_str() : ""; result->genre = (!song.genre.empty()) ? song.genre.c_str() : ""; result->duration = (song.duration != 0) ? song.duration : 0; result->year = (song.year != 0) ? song.year : 0; @@ -47,6 +59,8 @@ namespace dto { namespace conversion { song.title = (songDto->title == nullptr) ? "" : songDto->title->c_str(); song.album = (songDto->album == nullptr) ? "" : songDto->album->c_str(); song.artist = (songDto->artist == nullptr) ? "" : songDto->artist->c_str(); + song.albumArtist = (songDto->album_artist == nullptr) ? + "" : songDto->album_artist->c_str(); song.genre = (songDto->genre == nullptr) ? "" : songDto->genre->c_str(); song.year = (songDto->year.getPtr() == nullptr) ? 0 : songDto->year->getValue(); song.track = (songDto->track.getPtr() == nullptr) ? 0 : songDto->track->getValue(); diff --git a/src/manager/AlbumManager.cpp b/src/manager/AlbumManager.cpp index c694931..2ea1951 100644 --- a/src/manager/AlbumManager.cpp +++ b/src/manager/AlbumManager.cpp @@ -20,11 +20,10 @@ model::Album AlbumManager::retrieveAlbum(model::Album& album) model::Album AlbumManager::saveAlbum(const model::Song& song) { - model::Album album; - album.title = song.album; - album.year = song.year; + model::Album album(song); database::AlbumRepository albRepo(m_bConf); + // TODO: check for existence with the title and the artist if (!albRepo.doesAlbumExists(album, type::AlbumFilter::title)) { albRepo.saveAlbum(album); } else { @@ -55,9 +54,7 @@ void AlbumManager::deleteAlbum(const model::Song& song) void AlbumManager::updateAlbum(model::Song& updatedSong, const model::Song& currSong) { - model::Album album; - album.title = updatedSong.album; - album.year = updatedSong.year; + model::Album album(updatedSong); database::AlbumRepository albRepo(m_bConf); if (!albRepo.doesAlbumExists(album, type::AlbumFilter::title)) { diff --git a/src/manager/DirectoryManager.cpp b/src/manager/DirectoryManager.cpp index c8dbeeb..f7f89b3 100644 --- a/src/manager/DirectoryManager.cpp +++ b/src/manager/DirectoryManager.cpp @@ -52,7 +52,7 @@ std::string DirectoryManager::createDirectoryProcess(const model::Song& song, fs::create_directory(currPath); } - auto artPath = fs::path(currPath.string() + song.artist); + auto artPath = fs::path(currPath.string() + song.albumArtist); if (fs::exists(artPath)) { std::cout << "artist path exists" << std::endl; } else { @@ -143,7 +143,7 @@ nlohmann::json DirectoryManager::pathConfigContent(const model::BinaryPath& bCon void DirectoryManager::deleteDirectories(model::Song song, const std::string& rootPath) { std::cout << "checking for empty directories to delete" << std::endl; - const std::string art(rootPath + std::string("/") + song.artist); + const std::string art(rootPath + std::string("/") + song.albumArtist); const std::string alb(art + "/" + song.album); auto albPath = fs::path(alb); diff --git a/src/manager/SongManager.cpp b/src/manager/SongManager.cpp index 2a769ea..a904ad9 100644 --- a/src/manager/SongManager.cpp +++ b/src/manager/SongManager.cpp @@ -128,7 +128,7 @@ void SongManager::saveSong(model::Song& song) saveSongTemp(song); utility::MetadataRetriever meta; auto data = std::move(song.data); - song = meta.retrieveMetadata(song.songPath); + song = meta.retrieveMetadata(song); song.data = std::move(data); saveMisc(song); @@ -146,6 +146,7 @@ void SongManager::printSong(const model::Song& song) std::cout << "\nsong" << std::endl; std::cout << "title: " << song.title << std::endl; std::cout << "artist: " << song.artist << std::endl; + std::cout << "album artist: " << song.albumArtist << std::endl; std::cout << "album: " << song.album << std::endl; std::cout << "genre: " << song.genre << std::endl; std::cout << "duration: " << song.duration << std::endl; diff --git a/src/utility/MetadataRetriever.cpp b/src/utility/MetadataRetriever.cpp index 18a8502..7ccd679 100644 --- a/src/utility/MetadataRetriever.cpp +++ b/src/utility/MetadataRetriever.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -18,31 +19,43 @@ namespace fs = std::filesystem; namespace utility { -model::Song MetadataRetriever::retrieveMetadata(std::string& songPath) +model::Song MetadataRetriever::retrieveMetadata(model::Song& song) { - TagLib::FileRef file(songPath.c_str()); - model::Song song; - song.title = file.tag()->title().toCString(); - song.artist = file.tag()->artist().toCString(); - song.album = file.tag()->album().toCString(); - song.genre = file.tag()->genre().toCString(); - song.year = file.tag()->year(); - song.track = file.tag()->track(); - song.duration = file.audioProperties()->lengthInSeconds(); - song.songPath = songPath; - - // TODO: Move over to this eventually since at the moment - // I am only targetting mp3 files - // Used to retrieve disc - TagLib::MPEG::File sameFile(songPath.c_str()); + TagLib::MPEG::File sameFile(song.songPath.c_str()); auto tag = sameFile.ID3v2Tag(); + song.title = tag->title().toCString(); + song.artist = tag->artist().toCString(); + song.album = tag->album().toCString(); + song.genre = tag->genre().toCString(); + song.year = tag->year(); + song.track = tag->track(); + song.duration = sameFile.audioProperties()->lengthInSeconds(); - auto frame = tag->frameList("TPOS"); - if (frame.isEmpty()) { - song.disc = 1; - // TODO: set default disc to 1 if none found + constexpr auto id3DiscName = "TPOS"; + constexpr auto id3AlbumArtistName = "TPE2"; + + auto discFrame = tag->frameList(id3DiscName); + auto albumArtistFrame = tag->frameList(id3AlbumArtistName); + if (discFrame.isEmpty()) { + constexpr auto discDefaultVal = "1"; + TagLib::ID3v2::TextIdentificationFrame *emptyFrame = + new TagLib::ID3v2::TextIdentificationFrame(id3DiscName); + tag->addFrame(emptyFrame); + emptyFrame->setText(discDefaultVal); + song.disc = std::atoi(discDefaultVal); + sameFile.save(); } else { - song.disc = std::stoi(frame.front()->toString().toCString()); + song.disc = std::stoi(discFrame.front()->toString().toCString()); + } + + if (albumArtistFrame.isEmpty()) { + TagLib::ID3v2::TextIdentificationFrame *emptyFrame = + new TagLib::ID3v2::TextIdentificationFrame(id3DiscName); + tag->addFrame(emptyFrame); + emptyFrame->setText(song.artist.c_str()); + sameFile.save(); + } else { + song.albumArtist = albumArtistFrame.front()->toString().toCString(); } @@ -155,7 +168,8 @@ bool MetadataRetriever::songContainsCoverArt(const model::Song& song) void MetadataRetriever::updateMetadata(model::Song& sngUpdated, const model::Song& sngOld) { std::cout<<"updating metadata"< 0) { file.tag()->setTitle(sngUpdated.title); @@ -163,6 +177,20 @@ void MetadataRetriever::updateMetadata(model::Song& sngUpdated, const model::Son if (sngUpdated.artist.size() > 0) { file.tag()->setArtist(sngUpdated.artist); } + if (sngUpdated.albumArtist.size() > 0) { + constexpr auto frameId = "TPE2"; + auto albumArtistFrame = tag->frameList(frameId); + if (albumArtistFrame.isEmpty()) { + TagLib::ID3v2::TextIdentificationFrame *frame = + new TagLib::ID3v2::TextIdentificationFrame(frameId); + frame->setText(sngUpdated.albumArtist.c_str()); + tag->addFrame(frame); + } else { + albumArtistFrame.front()->setText(sngUpdated.albumArtist.c_str()); + } + + file.save(); + } if (sngUpdated.album.size() > 0) { file.tag()->setAlbum(sngUpdated.album); }