Compare commits

...

19 Commits

Author SHA1 Message Date
kdeng00 a1edbeb164 Fixed issue of the configuration verification test failing 2020-02-15 17:37:51 -05:00
kdeng00 437a6b1453 Fixed issue with special characters not being recorded properly 2020-02-15 17:32:52 -05:00
Kun Deng f2d0947b36 Merge pull request #63 from kdeng00/dist_change
API is functional again after the dependency updates
2020-02-02 20:33:48 -05:00
kdeng00 b067882c09 API is functional again after the dependency updates 2020-02-02 20:32:09 -05:00
kdeng00 57f76ee4a4 api is functional after the dependency updates 2020-02-01 20:58:48 -05:00
kdeng00 e846989a7f Not finished updating dependencies. oatpp has changed 2020-01-30 19:30:21 -05:00
kdeng00 91ae3a908e Updated json dependency 2020-01-29 19:12:23 -05:00
kdeng00 a970ba207e updating dependencies and code refactoring for the new changes 2020-01-28 21:03:56 -05:00
kdeng00 979dd66842 Made it easier to build the bcrypt library 2019-11-17 13:55:01 -05:00
kdeng00 69f5a37eb3 Code cleanup 2019-11-17 13:33:58 -05:00
kdeng00 ca1fcb801d Added functionality to check if the song already exists when uploading 2019-11-13 20:22:29 -05:00
kdeng00 3b0f199dca forgout about one file 2019-11-12 23:03:42 -05:00
kdeng00 41ae06e19a Fixed bug when song upload HTTP endpoint returns a response of the uploading song with errors 2019-11-12 22:51:07 -05:00
kdeng00 9b4a0db67a Added albumArtist field memeber to Song model. Automatically apply disc value 1 for song's metadata that have an empty disc field. Switched from TagLib::FileRef to TagLib::MPEG::File 2019-11-04 22:26:03 -05:00
kdeng00 c7bbdfe581 Code clean up 2019-11-03 13:03:32 -05:00
Kun Deng 01faa60699 Update README.md 2019-10-26 10:31:46 -04:00
amazing-username 8714d76a2a Changes to response returns when song does not exist. The cause of a segmentation fault 2019-10-26 14:23:42 +00:00
kdeng00 e764bc45de When song is uploaded the response returned is info about the song and a response 2019-10-22 20:30:43 -04:00
kdeng00 5fefc451d5 Adding coverart id field to Song dto object to return the coverart id that the song is associated with so it is easier to get the cover art 2019-10-21 21:54:50 -04:00
83 changed files with 4702 additions and 5028 deletions
+1 -1
+1 -1
+10 -7
View File
@@ -80,6 +80,7 @@ set(HEADERS
include/type/Scopes.h include/type/Scopes.h
include/type/SongChanged.h include/type/SongChanged.h
include/type/SongFilter.h include/type/SongFilter.h
include/type/SongUpload.h
include/type/UserFilter.h include/type/UserFilter.h
include/type/YearFilter.h include/type/YearFilter.h
include/verify/Initialization.h include/verify/Initialization.h
@@ -122,21 +123,23 @@ set(JWT_CPP_INCLUDE
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/jwt-cpp/include ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/jwt-cpp/include
) )
set (ORM_DIR
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/ormpp
)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup() conan_basic_setup()
find_library(BCRYPT bcrypt ${BCRYPTLIB}) #find_library(BCRYPT bcrypt ${BCRYPTLIB})
include_directories(include ${CPR_INCLUDE_DIRS} ${TAGLIB} ${TAGLIB_HEADERS} ${BCRYPTLIB}/) include_directories(include ${CPR_INCLUDE_DIRS} ${TAGLIB} ${TAGLIB_HEADERS} ${BCRYPTLIB})
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/cpr) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/cpr)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/taglib) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/taglib)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/oatpp) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/oatpp)
#add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/libbcrypt)
add_library(bcrypt STATIC IMPORTED)
set_target_properties(bcrypt PROPERTIES IMPORTED_LOCATION
"${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/libbcrypt/bcrypt.a"
)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/appsettings.json ${CMAKE_BINARY_DIR}/bin/appsettings.json COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/appsettings.json ${CMAKE_BINARY_DIR}/bin/appsettings.json COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/authcredentials.json ${CMAKE_BINARY_DIR}/bin/authcredentials.json COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/authcredentials.json ${CMAKE_BINARY_DIR}/bin/authcredentials.json COPYONLY)
@@ -146,4 +149,4 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Images/Stock/CoverArt.png ${CMAKE_BIN
add_executable(icarus ${SOURCES} ${HEADERS}) add_executable(icarus ${SOURCES} ${HEADERS})
target_include_directories(icarus PUBLIC ${JWT_CPP_INCLUDE}) target_include_directories(icarus PUBLIC ${JWT_CPP_INCLUDE})
target_link_libraries(icarus "-lstdc++fs" tag oatpp mysqlclient ${CONAN_LIBS} ${CPR_LIBRARIES} ${BCRYPT}) target_link_libraries(icarus "-lstdc++fs" tag oatpp mysqlclient ${CONAN_LIBS} ${CPR_LIBRARIES} bcrypt)
+3 -10
View File
@@ -53,7 +53,7 @@ Replace [domain] with the domain name of the created tenant. This can be found i
"client_secret": "Icankeepasecret" "client_secret": "Icankeepasecret"
``` ```
For the sake of this section, I will not go over configuring the API to accept the signing algorithm since it has already been configured in the [Startip](Startup.cs).cs file. Click on permissions to create the permissions for the API. Click on permissions to create the permissions for the API.
<h1 align "center"> <h1 align "center">
<img src="Images/Configuration/configure_api.png" width=100%> <img src="Images/Configuration/configure_api.png" width=100%>
</h1> </h1>
@@ -130,6 +130,7 @@ The only requirement of the User is that the user should have full permissions t
Prior to starting the API, the database must be created. The following tables are required: Prior to starting the API, the database must be created. The following tables are required:
* User * User
* Salt
* Song * Song
* Album * Album
* Artist * Artist
@@ -150,13 +151,11 @@ From this point the database has been successfully created. Metadata and song fi
git clone --recursive https://github.com/kdeng00/icarus git clone --recursive https://github.com/kdeng00/icarus
cd 3rdparty/libbcrypt/ cd 3rdparty/libbcrypt/
make make
cp bcrypt.a libbcrypt.a
cp bcrypt.o libbcrypt.o
cd ../.. cd ../..
mkdir build mkdir build
cd build cd build
conan install .. conan install ..
cmake .. cmake .. -DCMAKE_BUILD_TYPE=RELEASE -DUSE_SYSTEM_CURL=ON -DBUILD_CPR_TESTS=OFF -DOATPP_BUILD_TESTS=OFF
make make
bin/icarus bin/icarus
``` ```
@@ -166,16 +165,10 @@ Runs the server on localhost port 5002
Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on the code of conduct, and the process for submitting pull requests to the project. Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on the code of conduct, and the process for submitting pull requests to the project.
## Versioning
* [v0.1](https://github.com/amazing-username/Icarus/releases/tag/v0.1)
## Authors ## Authors
* **Kun Deng** - [amazing-username](https://github.com/amazing-username) * **Kun Deng** - [amazing-username](https://github.com/amazing-username)
See also the list of [contributors](https://github.com/amazing-username/Icarus/graphs/contributors) who participated in this project.
## License ## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
+1
View File
@@ -13,6 +13,7 @@ CREATE TABLE CoverArt (
CREATE TABLE Album ( CREATE TABLE Album (
AlbumId INT NOT NULL AUTO_INCREMENT, AlbumId INT NOT NULL AUTO_INCREMENT,
Title TEXT NOT NULL, Title TEXT NOT NULL,
Artist TEXT NOT NULL,
Year INT NOT NULL, Year INT NOT NULL,
PRIMARY KEY (AlbumId) PRIMARY KEY (AlbumId)
+1 -1
View File
@@ -1,5 +1,5 @@
[requires] [requires]
jsonformoderncpp/3.7.0@vthiery/stable jsonformoderncpp/3.7.3@vthiery/stable
[generators] [generators]
cmake cmake
+14 -13
View File
@@ -4,23 +4,24 @@
#include <string> #include <string>
#include "oatpp/core/data/stream/FileStream.hpp" #include "oatpp/core/data/stream/FileStream.hpp"
#include "oatpp/web/protocol/http/outgoing/ChunkedBody.hpp" #include "oatpp/core/data/stream/Stream.hpp"
#include "oatpp/core/async/Coroutine.hpp"
//#include "oatpp/web/protocol/http/outgoing/ChunkedBody.hpp"
namespace callback { namespace callback {
class StreamCallback : public oatpp::data::stream::ReadCallback class StreamCallback : public oatpp::data::stream::ReadCallback {
{ public:
public: StreamCallback();
StreamCallback(); StreamCallback(const std::string&);
StreamCallback(const std::string&);
oatpp::data::v_io_size read(void*, oatpp::data::v_io_size); oatpp::v_io_size read(void*, v_buff_size, oatpp::async::Action&);
private: private:
std::string m_songPath; std::string m_songPath;
long m_bytesRead; long m_bytesRead;
long m_counter; long m_counter;
long m_fileSize; long m_fileSize;
}; };
} }
#endif #endif
+22 -19
View File
@@ -9,29 +9,32 @@
#include "oatpp/web/server/HttpConnectionHandler.hpp" #include "oatpp/web/server/HttpConnectionHandler.hpp"
namespace component { namespace component {
class AppComponent {
public:
OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::network::ServerConnectionProvider>,
serverConnectionProvider)([&] {
return oatpp::network::server::SimpleTCPConnectionProvider::
createShared(appPort());
}());
class AppComponent OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::web::server::HttpRouter>, httpRouter)([] {
{ return oatpp::web::server::HttpRouter::createShared();
public: }());
OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::network::ServerConnectionProvider>, serverConnectionProvider)([] {
return oatpp::network::server::SimpleTCPConnectionProvider::createShared(5002);
}());
OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::web::server::HttpRouter>, httpRouter)([] { OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::network::server::ConnectionHandler>,
return oatpp::web::server::HttpRouter::createShared(); serverConnectionHandler)([] {
}()); OATPP_COMPONENT(std::shared_ptr<oatpp::web::server::HttpRouter>, router);
OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::network::server::ConnectionHandler>, serverConnectionHandler)([] { return oatpp::web::server::HttpConnectionHandler::createShared(router);
OATPP_COMPONENT(std::shared_ptr<oatpp::web::server::HttpRouter>, router); }());
return oatpp::web::server::HttpConnectionHandler::createShared(router); OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::data::mapping::ObjectMapper>,
}()); apiObjectMapper)([] {
return oatpp::parser::json::mapping::ObjectMapper::createShared();
OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::data::mapping::ObjectMapper>, apiObjectMapper)([] { }());
return oatpp::parser::json::mapping::ObjectMapper::createShared(); private:
}()); constexpr int appPort() noexcept { return 5002; }
private: };
};
} }
#endif #endif
+49 -57
View File
@@ -19,6 +19,7 @@
#include "database/AlbumRepository.h" #include "database/AlbumRepository.h"
#include "dto/AlbumDto.hpp" #include "dto/AlbumDto.hpp"
#include "dto/conversion/DtoConversions.h"
#include "manager/AlbumManager.h" #include "manager/AlbumManager.h"
#include "manager/TokenManager.h" #include "manager/TokenManager.h"
#include "model/Models.h" #include "model/Models.h"
@@ -28,75 +29,66 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace controller { namespace controller {
class AlbumController : public oatpp::web::server::api::ApiController class AlbumController : public oatpp::web::server::api::ApiController {
{ public:
public: AlbumController(const model::BinaryPath& bConf,
AlbumController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper))
: oatpp::web::server::api::ApiController(objectMapper), m_exe_path(p) : oatpp::web::server::api::ApiController(objectMapper), m_bConf(bConf) { }
{ }
AlbumController(const model::BinaryPath& bConf, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) #include OATPP_CODEGEN_BEGIN(ApiController)
: oatpp::web::server::api::ApiController(objectMapper), m_bConf(bConf)
{ }
#include OATPP_CODEGEN_BEGIN(ApiController) // endpoint for retrieving all album records in json format
ENDPOINT("GET", "/api/v1/album", albumRecords,
REQUEST(std::shared_ptr<IncomingRequest>, request)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth,
type::Scope::retrieveAlbum), Status::CODE_403, "Not allowed");
// endpoint for retrieving all album records in json format std::cout << "starting process of retrieving album\n";
ENDPOINT("GET", "/api/v1/album", albumRecords, database::AlbumRepository albRepo(m_bConf);
REQUEST(std::shared_ptr<IncomingRequest>, request)) auto albsDb = albRepo.retrieveRecords();
{ auto albums = oatpp::data::mapping::type::
auto authHeader = request->getHeader("Authorization"); List<dto::AlbumDto::ObjectWrapper>::createShared();
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveAlbum), Status::CODE_403, "Not allowed");
std::cout << "starting process of retrieving album" << std::endl; for (auto& albDb : albsDb) {
database::AlbumRepository albRepo(m_bConf); auto alb = dto::conversion::DtoConversions::toAlbumDto(albDb);
auto albsDb = albRepo.retrieveRecords();
auto albums = oatpp::data::mapping::type::List<dto::AlbumDto::ObjectWrapper>::createShared();
for (auto& albDb : albsDb) { albums->pushBack(alb);
auto alb = dto::AlbumDto::createShared(); }
alb->id = albDb.id;
alb->title = albDb.title.c_str();
alb->year = albDb.year;
albums->pushBack(alb); return createDtoResponse(Status::CODE_200, albums);
} }
return createDtoResponse(Status::CODE_200, albums); // endpoint for retrieving single album record by the album id in json format
} ENDPOINT("GET", "/api/v1/album/{id}", albumRecord,
REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth,
type::Scope::retrieveAlbum), Status::CODE_403, "Not allowed");
// endpoint for retrieving single album record by the album id in json format database::AlbumRepository albRepo(m_bConf);
ENDPOINT("GET", "/api/v1/album/{id}", albumRecord, model::Album albDb(id);
REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveAlbum), Status::CODE_403, "Not allowed");
database::AlbumRepository albRepo(m_bConf); OATPP_ASSERT_HTTP(albRepo.doesAlbumExists(albDb,
model::Album albDb(id); type::AlbumFilter::id) , Status::CODE_403, "album does not exist");
OATPP_ASSERT_HTTP(albRepo.doesAlbumExists(albDb, type::AlbumFilter::id) , Status::CODE_403, "album does not exist"); std::cout << "album exists\n";
albDb = albRepo.retrieveRecord(albDb, type::AlbumFilter::id);
std::cout << "album exists" << std::endl; auto album = dto::conversion::DtoConversions::toAlbumDto(albDb);
albDb = albRepo.retrieveRecord(albDb, type::AlbumFilter::id);
auto album = dto::AlbumDto::createShared(); return createDtoResponse(Status::CODE_200, album);
album->id = albDb.id; }
album->title = albDb.title.c_str();
album->year = albDb.year;
return createDtoResponse(Status::CODE_200, album); #include OATPP_CODEGEN_END(ApiController)
} private:
model::BinaryPath m_bConf;
#include OATPP_CODEGEN_END(ApiController) };
private:
std::string m_exe_path;
model::BinaryPath m_bConf;
};
} }
#endif #endif
+52 -55
View File
@@ -28,73 +28,70 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace controller { namespace controller {
class ArtistController : public oatpp::web::server::api::ApiController class ArtistController : public oatpp::web::server::api::ApiController {
{ public:
public: ArtistController(const model::BinaryPath& bConf,
ArtistController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper))
: oatpp::web::server::api::ApiController(objectMapper), m_exe_path(p) : oatpp::web::server::api::ApiController(objectMapper), m_bConf(bConf) { }
{ }
ArtistController(const model::BinaryPath& bConf, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) #include OATPP_CODEGEN_BEGIN(ApiController)
: oatpp::web::server::api::ApiController(objectMapper), m_bConf(bConf)
{ }
#include OATPP_CODEGEN_BEGIN(ApiController) // endpoint for retrieving all artist records in json format
ENDPOINT("GET", "/api/v1/artist", artistRecords,
REQUEST(std::shared_ptr<IncomingRequest>, request)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth,
type::Scope::retrieveArtist), Status::CODE_403, "Not allowed");
// endpoint for retrieving all artist records in json format std::cout << "starting process of retrieving artist\n";
ENDPOINT("GET", "/api/v1/artist", artistRecords, database::ArtistRepository artRepo(m_bConf);
REQUEST(std::shared_ptr<IncomingRequest>, request)) auto artsDb = artRepo.retrieveRecords();
{ auto artists = oatpp::data::mapping::type::
auto authHeader = request->getHeader("Authorization"); List<dto::ArtistDto::ObjectWrapper>::createShared();
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveArtist), Status::CODE_403, "Not allowed");
std::cout << "starting process of retrieving artist" << std::endl; for (auto& artDb : artsDb) {
database::ArtistRepository artRepo(m_bConf); auto art = dto::ArtistDto::createShared();
auto artsDb = artRepo.retrieveRecords(); art->id = artDb.id;
auto artists = oatpp::data::mapping::type::List<dto::ArtistDto::ObjectWrapper>::createShared(); art->artist = artDb.artist.c_str();
for (auto& artDb : artsDb) { artists->pushBack(art);
auto art = dto::ArtistDto::createShared(); }
art->id = artDb.id;
art->artist = artDb.artist.c_str();
artists->pushBack(art); return createDtoResponse(Status::CODE_200, artists);
} }
return createDtoResponse(Status::CODE_200, artists); // endpoint for retrieving single artist record by the artist id in json format
} ENDPOINT("GET", "/api/v1/artist/{id}", artistRecord,
REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth,
type::Scope::retrieveArtist), Status::CODE_403, "Not allowed");
// endpoint for retrieving single artist record by the artist id in json format database::ArtistRepository artRepo(m_bConf);
ENDPOINT("GET", "/api/v1/artist/{id}", artistRecord, model::Artist artDb(id);
REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveArtist), Status::CODE_403, "Not allowed");
database::ArtistRepository artRepo(m_bConf); OATPP_ASSERT_HTTP(artRepo.doesArtistExist(artDb,
model::Artist artDb(id); type::ArtistFilter::id) , Status::CODE_403, "artist does not exist");
OATPP_ASSERT_HTTP(artRepo.doesArtistExist(artDb, type::ArtistFilter::id) , Status::CODE_403, "artist does not exist"); std::cout << "artist exist\n";
artDb = artRepo.retrieveRecord(artDb, type::ArtistFilter::id);
std::cout << "artist exist" << std::endl; auto artist = dto::ArtistDto::createShared();
artDb = artRepo.retrieveRecord(artDb, type::ArtistFilter::id); artist->id = artDb.id;
artist->artist = artDb.artist.c_str();
auto artist = dto::ArtistDto::createShared(); return createDtoResponse(Status::CODE_200, artist);
artist->id = artDb.id; }
artist->artist = artDb.artist.c_str();
return createDtoResponse(Status::CODE_200, artist); #include OATPP_CODEGEN_END(ApiController)
} private:
model::BinaryPath m_bConf;
#include OATPP_CODEGEN_END(ApiController) };
private:
std::string m_exe_path;
model::BinaryPath m_bConf;
};
} }
#endif #endif
+71 -73
View File
@@ -27,95 +27,93 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace controller { namespace controller {
class CoverArtController : public oatpp::web::server::api::ApiController class CoverArtController : public oatpp::web::server::api::ApiController {
{ public:
public: CoverArtController(const model::BinaryPath& bConf,
CoverArtController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper))
: oatpp::web::server::api::ApiController(objectMapper), m_exe_path(p) : oatpp::web::server::api::ApiController(objectMapper), m_bConf(bConf) { }
{ }
CoverArtController(const model::BinaryPath& bConf, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) #include OATPP_CODEGEN_BEGIN(ApiController)
: oatpp::web::server::api::ApiController(objectMapper), m_bConf(bConf)
{ }
#include OATPP_CODEGEN_BEGIN(ApiController) // endpoint for retrieving all cover art records in json format
ENDPOINT("GET", "/api/v1/coverart", coverArtRecords,
REQUEST(std::shared_ptr<IncomingRequest>, request)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth,
type::Scope::downloadCoverArt), Status::CODE_403, "Not allowed");
// endpoint for retrieving all cover art records in json format std::cout << "starting process of retrieving cover art\n";
ENDPOINT("GET", "/api/v1/coverart", coverArtRecords, database::CoverArtRepository covRepo(m_bConf);
REQUEST(std::shared_ptr<IncomingRequest>, request)) auto covsDb = covRepo.retrieveRecords();
{ auto coverArts = oatpp::data::mapping::type::
auto authHeader = request->getHeader("Authorization"); List<dto::CoverArtDto::ObjectWrapper>::createShared();
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::downloadCoverArt), Status::CODE_403, "Not allowed");
std::cout << "starting process of retrieving cover art" << std::endl; for (auto& covDb : covsDb) {
database::CoverArtRepository covRepo(m_bConf); auto cov = dto::CoverArtDto::createShared();
auto covsDb = covRepo.retrieveRecords(); cov->id = covDb.id;
auto coverArts = oatpp::data::mapping::type::List<dto::CoverArtDto::ObjectWrapper>::createShared(); cov->songTitle = covDb.songTitle.c_str();
for (auto& covDb : covsDb) { coverArts->pushBack(cov);
auto cov = dto::CoverArtDto::createShared(); }
cov->id = covDb.id;
cov->songTitle = covDb.songTitle.c_str();
coverArts->pushBack(cov); return createDtoResponse(Status::CODE_200, coverArts);
} }
return createDtoResponse(Status::CODE_200, coverArts); // endpoint for retrieving single cover art record by the cover art id in json format
} ENDPOINT("GET", "/api/v1/coverart/{id}", coverArtRecord,
REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth,
type::Scope::downloadCoverArt), Status::CODE_403, "Not allowed");
// endpoint for retrieving single cover art record by the cover art id in json format database::CoverArtRepository covRepo(m_bConf);
ENDPOINT("GET", "/api/v1/coverart/{id}", coverArtRecord, model::Cover covDb;
REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) { covDb.id = id;
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::downloadCoverArt), Status::CODE_403, "Not allowed");
database::CoverArtRepository covRepo(m_bConf); OATPP_ASSERT_HTTP(covRepo.doesCoverArtExist(covDb,
model::Cover covDb; type::CoverFilter::id) , Status::CODE_403, "song does not exist");
covDb.id = id;
OATPP_ASSERT_HTTP(covRepo.doesCoverArtExist(covDb, type::CoverFilter::id) , Status::CODE_403, "song does not exist"); std::cout << "cover art exists\n";
covDb = covRepo.retrieveRecord(covDb, type::CoverFilter::id);
std::cout << "cover art exists" << std::endl; auto coverArt = dto::CoverArtDto::createShared();
covDb = covRepo.retrieveRecord(covDb, type::CoverFilter::id); coverArt->id = covDb.id;
coverArt->songTitle = covDb.songTitle.c_str();
auto coverArt = dto::CoverArtDto::createShared(); return createDtoResponse(Status::CODE_200, coverArt);
coverArt->id = covDb.id; }
coverArt->songTitle = covDb.songTitle.c_str();
return createDtoResponse(Status::CODE_200, coverArt); ENDPOINT("GET", "/api/v1/coverart/download/{id}", downloadCoverArt,
} REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth,
type::Scope::downloadCoverArt), Status::CODE_403, "Not allowed");
ENDPOINT("GET", "/api/v1/coverart/download/{id}", downloadCoverArt, database::CoverArtRepository covRepo(m_bConf);
REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) { model::Cover covDb;
auto authHeader = request->getHeader("Authorization"); covDb.id = id;
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope"); covDb = covRepo.retrieveRecord(covDb, type::CoverFilter::id);
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::downloadCoverArt), Status::CODE_403, "Not allowed");
database::CoverArtRepository covRepo(m_bConf); auto rawCover = oatpp::base::StrBuffer::loadFromFile(covDb.imagePath.c_str());
model::Cover covDb; auto response = createResponse(Status::CODE_200, rawCover);
covDb.id = id;
covDb = covRepo.retrieveRecord(covDb, type::CoverFilter::id); response->putHeader(Header::CONTENT_TYPE, "image/*");
auto rawCover = oatpp::base::StrBuffer::loadFromFile(covDb.imagePath.c_str()); return response;
auto response = createResponse(Status::CODE_200, rawCover); }
response->putHeader(Header::CONTENT_TYPE, "image/*");
return response; #include OATPP_CODEGEN_END(ApiController)
} private:
model::BinaryPath m_bConf;
#include OATPP_CODEGEN_END(ApiController) };
private:
std::string m_exe_path;
model::BinaryPath m_bConf;
};
} }
#endif #endif
+52 -55
View File
@@ -28,73 +28,70 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace controller { namespace controller {
class GenreController : public oatpp::web::server::api::ApiController class GenreController : public oatpp::web::server::api::ApiController {
{ public:
public: GenreController(const model::BinaryPath& bConf,
GenreController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper))
: oatpp::web::server::api::ApiController(objectMapper), m_exe_path(p) : oatpp::web::server::api::ApiController(objectMapper), m_bConf(bConf) { }
{ }
GenreController(const model::BinaryPath& bConf, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) #include OATPP_CODEGEN_BEGIN(ApiController)
: oatpp::web::server::api::ApiController(objectMapper), m_bConf(bConf)
{ }
#include OATPP_CODEGEN_BEGIN(ApiController) // endpoint for retrieving all genre records in json format
ENDPOINT("GET", "/api/v1/genre", genreRecords,
REQUEST(std::shared_ptr<IncomingRequest>, request)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth,
type::Scope::retrieveGenre), Status::CODE_403, "Not allowed");
// endpoint for retrieving all genre records in json format std::cout << "starting process of retrieving genre\n";
ENDPOINT("GET", "/api/v1/genre", genreRecords, database::GenreRepository gnrRepo(m_bConf);
REQUEST(std::shared_ptr<IncomingRequest>, request)) auto gnrsDb = gnrRepo.retrieveRecords();
{ auto genres = oatpp::data::mapping::type::
auto authHeader = request->getHeader("Authorization"); List<dto::GenreDto::ObjectWrapper>::createShared();
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveGenre), Status::CODE_403, "Not allowed");
std::cout << "starting process of retrieving genre" << std::endl; for (auto& gnrDb : gnrsDb) {
database::GenreRepository gnrRepo(m_bConf); auto gnr = dto::GenreDto::createShared();
auto gnrsDb = gnrRepo.retrieveRecords(); gnr->id = gnrDb.id;
auto genres = oatpp::data::mapping::type::List<dto::GenreDto::ObjectWrapper>::createShared(); gnr->category = gnrDb.category.c_str();
for (auto& gnrDb : gnrsDb) { genres->pushBack(gnr);
auto gnr = dto::GenreDto::createShared(); }
gnr->id = gnrDb.id;
gnr->category = gnrDb.category.c_str();
genres->pushBack(gnr); return createDtoResponse(Status::CODE_200, genres);
} }
return createDtoResponse(Status::CODE_200, genres); // endpoint for retrieving single genre record by the genre id in json format
} ENDPOINT("GET", "/api/v1/genre/{id}", genreRecord,
REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth,
type::Scope::retrieveGenre), Status::CODE_403, "Not allowed");
// endpoint for retrieving single genre record by the genre id in json format database::GenreRepository gnrRepo(m_bConf);
ENDPOINT("GET", "/api/v1/genre/{id}", genreRecord, model::Genre gnrDb(id);
REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveGenre), Status::CODE_403, "Not allowed");
database::GenreRepository gnrRepo(m_bConf); OATPP_ASSERT_HTTP(gnrRepo.doesGenreExist(gnrDb,
model::Genre gnrDb(id); type::GenreFilter::id) , Status::CODE_403, "genre does not exist");
OATPP_ASSERT_HTTP(gnrRepo.doesGenreExist(gnrDb, type::GenreFilter::id) , Status::CODE_403, "genre does not exist"); std::cout << "genre exist\n";
gnrDb = gnrRepo.retrieveRecord(gnrDb, type::GenreFilter::id);
std::cout << "genre exist" << std::endl; auto genre = dto::GenreDto::createShared();
gnrDb = gnrRepo.retrieveRecord(gnrDb, type::GenreFilter::id); genre->id = gnrDb.id;
genre->category= gnrDb.category.c_str();
auto genre = dto::GenreDto::createShared(); return createDtoResponse(Status::CODE_200, genre);
genre->id = gnrDb.id; }
genre->category= gnrDb.category.c_str();
return createDtoResponse(Status::CODE_200, genre); #include OATPP_CODEGEN_END(ApiController)
} private:
model::BinaryPath m_bConf;
#include OATPP_CODEGEN_END(ApiController) };
private:
std::string m_exe_path;
model::BinaryPath m_bConf;
};
} }
#endif #endif
+27 -31
View File
@@ -16,46 +16,42 @@
#include "model/Models.h" #include "model/Models.h"
namespace controller { namespace controller {
class LoginController : public oatpp::web::server::api::ApiController { class LoginController : public oatpp::web::server::api::ApiController {
public: public:
LoginController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) LoginController(const model::BinaryPath& bConf,
: oatpp::web::server::api::ApiController(objectMapper), exe_path(p) OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper))
{ } :oatpp::web::server::api::ApiController(objectMapper), m_bConf(bConf) { }
LoginController(const model::BinaryPath& bConf, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper))
:oatpp::web::server::api::ApiController(objectMapper), m_bConf(bConf)
{ }
#include OATPP_CODEGEN_BEGIN(ApiController) #include OATPP_CODEGEN_BEGIN(ApiController)
ENDPOINT("POST", "/api/v1/login", data, BODY_DTO(dto::UserDto::ObjectWrapper, usr)) { ENDPOINT("POST", "/api/v1/login", data, BODY_DTO(dto::UserDto::ObjectWrapper, usr)) {
OATPP_LOGI("icarus", "logging in"); OATPP_LOGI("icarus", "logging in");
manager::UserManager usrMgr(m_bConf); manager::UserManager usrMgr(m_bConf);
auto user = dto::conversion::DtoConversions::toUser(usr); auto user = dto::conversion::DtoConversions::toUser(usr);
if (!usrMgr.doesUserExist(user) || !usrMgr.validatePassword(user)) { if (!usrMgr.doesUserExist(user) || !usrMgr.validatePassword(user)) {
auto logRes = dto::LoginResultDto::createShared(); auto logRes = dto::LoginResultDto::createShared();
logRes->message = "invalid credentials"; logRes->message = "invalid credentials";
std::cout << "user does not exist" << std::endl; std::cout << "user does not exist\n";
return createDtoResponse(Status::CODE_401, logRes); return createDtoResponse(Status::CODE_401, logRes);
} }
std::cout << "user exists" << std::endl; std::cout << "user exists\n";
manager::TokenManager tok; manager::TokenManager tok;
auto token = tok.retrieveToken(m_bConf); auto token = tok.retrieveToken(m_bConf);
auto logRes = dto::conversion::DtoConversions::toLoginResultDto(user, token); auto logRes = dto::conversion::DtoConversions::toLoginResultDto(user, token);
logRes->message = "Successful"; logRes->message = "Successful";
return createDtoResponse(Status::CODE_200, logRes); return createDtoResponse(Status::CODE_200, logRes);
} }
#include OATPP_CODEGEN_END(ApiController) #include OATPP_CODEGEN_END(ApiController)
private: private:
std::string exe_path; model::BinaryPath m_bConf;
model::BinaryPath m_bConf; };
};
} }
#endif #endif
+5 -2
View File
@@ -17,8 +17,8 @@ namespace controller {
class RegisterController : public oatpp::web::server::api::ApiController { class RegisterController : public oatpp::web::server::api::ApiController {
public: public:
RegisterController(const model::BinaryPath& bConf, RegisterController(const model::BinaryPath& bConf,
OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) : OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) :
oatpp::web::server::api::ApiController(objectMapper), m_bConf(bConf) { } oatpp::web::server::api::ApiController(objectMapper), m_bConf(bConf) { }
#include OATPP_CODEGEN_BEGIN(ApiController) #include OATPP_CODEGEN_BEGIN(ApiController)
@@ -26,6 +26,9 @@ namespace controller {
BODY_DTO(dto::UserDto::ObjectWrapper, usr)) { BODY_DTO(dto::UserDto::ObjectWrapper, usr)) {
manager::UserManager usrMgr(m_bConf); manager::UserManager usrMgr(m_bConf);
auto user = dto::conversion::DtoConversions::toUser(usr); auto user = dto::conversion::DtoConversions::toUser(usr);
if (usrMgr.doesUserExist(user)) {
return createResponse(Status::CODE_401, "user already exists");
}
auto res = usrMgr.registerUser(user); auto res = usrMgr.registerUser(user);
auto registerResult = dto::conversion::DtoConversions::toRegisterResultDto(res); auto registerResult = dto::conversion::DtoConversions::toRegisterResultDto(res);
+177 -157
View File
@@ -9,227 +9,247 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <oatpp/core/async/Coroutine.hpp>
#include "oatpp/core/data/stream/ChunkedBuffer.hpp" #include "oatpp/core/data/stream/ChunkedBuffer.hpp"
#include "oatpp/core/data/stream/FileStream.hpp" #include "oatpp/core/data/stream/FileStream.hpp"
#include "oatpp/core/base/StrBuffer.hpp"
#include "oatpp/core/macro/codegen.hpp" #include "oatpp/core/macro/codegen.hpp"
#include "oatpp/core/macro/component.hpp" #include "oatpp/core/macro/component.hpp"
#include "oatpp/web/mime/multipart/InMemoryPartReader.hpp" #include "oatpp/web/mime/multipart/InMemoryPartReader.hpp"
#include "oatpp/web/mime/multipart/Reader.hpp" #include "oatpp/web/mime/multipart/Reader.hpp"
#include "oatpp/web/protocol/http/outgoing/ChunkedBody.hpp" #include "oatpp/web/protocol/http/outgoing/StreamingBody.hpp"
#include "oatpp/web/server/api/ApiController.hpp" #include "oatpp/web/server/api/ApiController.hpp"
#include "callback/StreamCallback.h" #include "callback/StreamCallback.h"
#include "database/SongRepository.h" #include "database/SongRepository.h"
#include "dto/SongDto.hpp" #include "dto/SongDto.hpp"
#include "dto/conversion/DtoConversions.h"
#include "manager/SongManager.h" #include "manager/SongManager.h"
#include "manager/TokenManager.h" #include "manager/TokenManager.h"
#include "model/Models.h" #include "model/Models.h"
#include "type/Scopes.h" #include "type/Scopes.h"
#include "type/SongFilter.h" #include "type/SongFilter.h"
#include "type/SongUpload.h"
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace controller { namespace controller {
class SongController : public oatpp::web::server::api::ApiController { class SongController : public oatpp::web::server::api::ApiController {
public: public:
SongController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) SongController(const model::BinaryPath& bConf, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper))
: oatpp::web::server::api::ApiController(objectMapper), m_exe_path(p) : oatpp::web::server::api::ApiController(objectMapper), m_bConf(bConf) { }
{ }
SongController(const model::BinaryPath& bConf, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) #include OATPP_CODEGEN_BEGIN(ApiController)
: oatpp::web::server::api::ApiController(objectMapper), m_bConf(bConf)
{ }
#include OATPP_CODEGEN_BEGIN(ApiController) // endpoint for uploading a song
ENDPOINT("POST", "/api/v1/song/data", songUpload,
//AUTHORIZATION(std::shared_ptr<oatpp::web::server::handler::DefaultBearerAuthorizationObject>, authObject),
REQUEST(std::shared_ptr<IncomingRequest>, request)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth,
type::Scope::upload), Status::CODE_403, "Not allowed");
// endpoint for uploading a song auto mp =
ENDPOINT("POST", "/api/v1/song/data", songUpload, std::make_shared<oatpp::web::mime::multipart::Multipart>
REQUEST(std::shared_ptr<IncomingRequest>, request)) (request->getHeaders());
{
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::upload), Status::CODE_403, "Not allowed");
auto mp = std::make_shared<oatpp::web::mime::multipart::Multipart>(request->getHeaders()); oatpp::web::mime::multipart::Reader mp_reader(mp.get());
oatpp::web::mime::multipart::Reader mp_reader(mp.get()); mp_reader.setPartReader("file",
oatpp::web::mime::multipart::createInMemoryPartReader(m_dataSize));
mp_reader.setPartReader("file", oatpp::web::mime::multipart::createInMemoryPartReader(m_dataSize)); request->transferBody(&mp_reader);
request->transferBody(&mp_reader); auto file = mp->getNamedPart("file");
auto file = mp->getNamedPart("file"); OATPP_ASSERT_HTTP(file, Status::CODE_400, "file is null");
OATPP_ASSERT_HTTP(file, Status::CODE_400, "file is null"); auto buff = std::make_unique<const char*>(file->getInMemoryData()->c_str());
auto buffSize = file->getKnownSize();
auto buff = std::unique_ptr<char>(new char[file->getKnownSize()]); std::vector<unsigned char> data(*buff, *buff + buffSize);
auto buffSize = file->getInputStream()->read(buff.get(), file->getKnownSize());
std::vector<unsigned char> data(buff.get(), buff.get() + buffSize); model::Song sng;
sng.data = std::move(data);
manager::SongManager songMgr(m_bConf);
const auto result = songMgr.saveSong(sng);
if (!result.first) {
switch (result.second) {
case type::SongUpload::AlreadyExist:
return createResponse(Status::CODE_400, "Song already exists");
default:
break;
}
}
model::Song sng; auto songDto = dto::conversion::DtoConversions::toSongDto(sng);
sng.data = std::move(data);
manager::SongManager songMgr(m_bConf);
songMgr.saveSong(sng);
return createResponse(Status::CODE_200, "OK"); return createDtoResponse(Status::CODE_200, songDto);
} }
// endpoint for retrieving all song records in json format // endpoint for retrieving all song records in json format
ENDPOINT("GET", "/api/v1/song", songRecords, ENDPOINT("GET", "/api/v1/song", songRecords,
REQUEST(std::shared_ptr<IncomingRequest>, request)) REQUEST(std::shared_ptr<IncomingRequest>, request)) {
{ auto authHeader = request->getHeader("Authorization");
auto authHeader = request->getHeader("Authorization"); OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope"); auto auth = authHeader->std_str();
auto auth = authHeader->std_str(); manager::TokenManager tok;
manager::TokenManager tok; OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveSong), Status::CODE_403, "Not allowed");
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveSong), Status::CODE_403, "Not allowed");
std::cout << "starting process of retrieving songs" << std::endl; std::cout << "starting process of retrieving songs\n";
database::SongRepository songRepo(m_bConf); database::SongRepository songRepo(m_bConf);
auto songsDb = songRepo.retrieveRecords(); auto songsDb = songRepo.retrieveRecords();
auto songs = oatpp::data::mapping::type::List<dto::SongDto::ObjectWrapper>::createShared(); auto songs = oatpp::data::mapping::type::List<dto::SongDto::ObjectWrapper>::createShared();
std::cout << "creating object to send" << std::endl; std::cout << "creating object to send\n";
for (auto& songDb : songsDb) { for (auto& songDb : songsDb) {
auto song = dto::SongDto::createShared(); auto song = dto::conversion::DtoConversions::toSongDto(songDb);
song->id = songDb.id;
song->title = songDb.title.c_str();
song->artist = songDb.artist.c_str();
song->album = songDb.album.c_str();
song->genre = songDb.genre.c_str();
song->year = songDb.year;
song->duration = songDb.duration;
song->track = songDb.track;
song->disc = songDb.disc;
songs->pushBack(song); songs->pushBack(song);
} }
return createDtoResponse(Status::CODE_200, songs); return createDtoResponse(Status::CODE_200, songs);
} }
// endpoint for retrieving song record by the song id in json format // endpoint for retrieving song record by the song id in json format
ENDPOINT("GET", "/api/v1/song/{id}", songRecord, ENDPOINT("GET", "/api/v1/song/{id}", songRecord,
REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) { REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization"); auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope"); OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str(); auto auth = authHeader->std_str();
manager::TokenManager tok; manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveSong), Status::CODE_403, "Not allowed"); OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveSong), Status::CODE_403, "Not allowed");
database::SongRepository songRepo(m_bConf); database::SongRepository songRepo(m_bConf);
model::Song songDb(id); model::Song songDb(id);
OATPP_ASSERT_HTTP(songRepo.doesSongExist(songDb, type::SongFilter::id) , Status::CODE_403, if (!songRepo.doesSongExist(songDb, type::SongFilter::id)) {
"song does not exist"); return songDoesNotExist();
}
std::cout << "song exists" << std::endl; std::cout << "song exists\n";
songDb = songRepo.retrieveRecord(songDb, type::SongFilter::id); songDb = songRepo.retrieveRecord(songDb, type::SongFilter::id);
auto song = dto::SongDto::createShared(); auto song = dto::conversion::DtoConversions::toSongDto(songDb);
song->id = songDb.id;
song->title = songDb.title.c_str();
song->artist = songDb.artist.c_str();
song->album = songDb.album.c_str();
song->genre = songDb.genre.c_str();
song->year = songDb.year;
song->duration = songDb.duration;
song->track = songDb.track;
song->disc = songDb.disc;
return createDtoResponse(Status::CODE_200, song); return createDtoResponse(Status::CODE_200, song);
} }
ENDPOINT("GET", "/api/v1/song/data/{id}", downloadSong, ENDPOINT("GET", "/api/v1/song/data/{id}", downloadSong,
REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) { REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization"); auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope"); OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str(); auto auth = authHeader->std_str();
manager::TokenManager tok; manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::download), Status::CODE_403, "Not allowed"); OATPP_ASSERT_HTTP(tok.isTokenValid(auth,
type::Scope::download), Status::CODE_403, "Not allowed");
database::SongRepository songRepo(m_bConf); database::SongRepository songRepo(m_bConf);
model::Song songDb(id); model::Song songDb(id);
songDb = songRepo.retrieveRecord(songDb, type::SongFilter::id); if (!songRepo.doesSongExist(songDb, type::SongFilter::id)) {
return songDoesNotExist();
}
auto rawSong = oatpp::base::StrBuffer::loadFromFile(songDb.songPath.c_str()); songDb = songRepo.retrieveRecord(songDb, type::SongFilter::id);
auto response = createResponse(Status::CODE_200, rawSong);
response->putHeader(Header::CONTENT_TYPE, "audio/mpeg");
return response; auto rawSong = oatpp::base::StrBuffer::loadFromFile(songDb.songPath.c_str());
}
auto response = createResponse(Status::CODE_200, rawSong);
response->putHeader(Header::CONTENT_TYPE, "audio/mpeg");
ENDPOINT("UPDATE", "/api/v1/song/{id}", songUpdate, return response;
REQUEST(std::shared_ptr<IncomingRequest>, request), }
BODY_DTO(dto::SongDto::ObjectWrapper, songDto), PATH(Int32, id)) {
songDto->id = id; ENDPOINT("UPDATE", "/api/v1/song/{id}", songUpdate,
auto authHeader = request->getHeader("Authorization"); REQUEST(std::shared_ptr<IncomingRequest>, request),
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope"); BODY_DTO(dto::SongDto::ObjectWrapper, songDto), PATH(Int32, id)) {
auto auth = authHeader->std_str(); songDto->id = id;
manager::TokenManager tok; auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::updateSong), Status::CODE_403, "Not allowed"); OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth,
type::Scope::updateSong), Status::CODE_403, "Not allowed");
auto updatedSong = manager::SongManager::songDtoConv(songDto); auto updatedSong = dto::conversion::DtoConversions::toSong(songDto);
manager::SongManager songMgr(m_bConf); manager::SongManager songMgr(m_bConf);
songMgr.updateSong(updatedSong); auto result = songMgr.updateSong(updatedSong);
if (!result) {
return songDoesNotExist();
}
return createResponse(Status::CODE_200, "OK"); return createResponse(Status::CODE_200, "OK");
} }
// endpoint to delete a song // endpoint to delete a song
ENDPOINT("DELETE", "api/v1/song/data/{id}", songDelete, ENDPOINT("DELETE", "api/v1/song/data/{id}", songDelete,
REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) { REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization"); auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope"); OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str(); auto auth = authHeader->std_str();
manager::TokenManager tok; manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::deleteSong), Status::CODE_403, "Not allowed"); OATPP_ASSERT_HTTP(tok.isTokenValid(auth,
type::Scope::deleteSong), Status::CODE_403, "Not allowed");
model::Song song(id); model::Song song(id);
manager::SongManager sngMgr(m_bConf); manager::SongManager sngMgr(m_bConf);
sngMgr.deleteSong(song); auto result = sngMgr.deleteSong(song);
if (!result) {
return songDoesNotExist();
}
return createResponse(Status::CODE_200, "OK"); return createResponse(Status::CODE_200, "OK");
} }
// endpoint for streaming a song // endpoint for streaming a song
ENDPOINT("GET", "/api/v1/song/stream/{id}", streamSong, ENDPOINT("GET", "/api/v1/song/stream/{id}", streamSong,
REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) { REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization"); auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope"); OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str(); auto auth = authHeader->std_str();
manager::TokenManager tok; manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::stream), Status::CODE_403, "Not allowed"); OATPP_ASSERT_HTTP(tok.isTokenValid(auth,
type::Scope::stream), Status::CODE_403, "Not allowed");
database::SongRepository songRepo(m_bConf); database::SongRepository songRepo(m_bConf);
model::Song songDb(id); model::Song songDb(id);
songDb = songRepo.retrieveRecord(songDb, type::SongFilter::id); if (!songRepo.doesSongExist(songDb, type::SongFilter::id)) {
return songDoesNotExist();
}
oatpp::data::v_io_size dSize = 1024; songDb = songRepo.retrieveRecord(songDb, type::SongFilter::id);
auto db = std::make_shared<oatpp::web::protocol::http::outgoing::ChunkedBody>( auto dSize = 1024;
std::make_shared<callback::StreamCallback>(songDb.songPath), nullptr, dSize);
auto response = OutgoingResponse::createShared(Status::CODE_200, db); auto callback = std::make_shared<callback::StreamCallback>(songDb.songPath);
response->putHeader(Header::CONNECTION, Header::Value::CONNECTION_KEEP_ALIVE); auto db = std::make_shared<oatpp::web::protocol::http::outgoing::StreamingBody>(
response->putHeader(Header::CONTENT_TYPE, "audio/mpeg"); callback
);
return response; auto response = OutgoingResponse::createShared(Status::CODE_200, db);
} response->putHeader(Header::CONNECTION, Header::Value::CONNECTION_KEEP_ALIVE);
response->putHeader(Header::CONTENT_TYPE, "audio/mpeg");
#include OATPP_CODEGEN_END(ApiController) return response;
private: }
std::string m_exe_path;
model::BinaryPath m_bConf; #include OATPP_CODEGEN_END(ApiController)
private:
std::shared_ptr<oatpp::web::protocol::http::outgoing::Response>
songDoesNotExist() {
const long m_dataSize = std::numeric_limits<long long int>::max(); return createResponse(Status::CODE_404, "Song not found");
}; }
model::BinaryPath m_bConf;
const long m_dataSize = std::numeric_limits<long long int>::max();
};
} }
#endif #endif
+53 -55
View File
@@ -27,73 +27,71 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace controller { namespace controller {
class YearController : public oatpp::web::server::api::ApiController class YearController : public oatpp::web::server::api::ApiController {
{ public:
public: YearController(const model::BinaryPath& bConf,
YearController(std::string p, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper))
: oatpp::web::server::api::ApiController(objectMapper), m_exe_path(p) : oatpp::web::server::api::ApiController(objectMapper), m_bConf(bConf) { }
{ }
YearController(const model::BinaryPath& bConf, OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper)) #include OATPP_CODEGEN_BEGIN(ApiController)
: oatpp::web::server::api::ApiController(objectMapper), m_bConf(bConf)
{ }
#include OATPP_CODEGEN_BEGIN(ApiController) // endpoint for retrieving all year records in json format
ENDPOINT("GET", "/api/v1/year", yearRecords,
REQUEST(std::shared_ptr<IncomingRequest>, request)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth,
type::Scope::retrieveYear), Status::CODE_403, "Not allowed");
// endpoint for retrieving all year records in json format std::cout << "starting process of retrieving year\n";
ENDPOINT("GET", "/api/v1/year", yearRecords, database::YearRepository yrRepo(m_bConf);
REQUEST(std::shared_ptr<IncomingRequest>, request)) auto yrsDb = yrRepo.retrieveRecords();
{ auto yearRecs = oatpp::data::mapping::type::
auto authHeader = request->getHeader("Authorization"); List<dto::YearDto::ObjectWrapper>::createShared();
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveYear), Status::CODE_403, "Not allowed");
std::cout << "starting process of retrieving year" << std::endl; for (auto& yrDb : yrsDb) {
database::YearRepository yrRepo(m_bConf); auto yr = dto::YearDto::createShared();
auto yrsDb = yrRepo.retrieveRecords(); yr->id = yrDb.id;
auto yearRecs = oatpp::data::mapping::type::List<dto::YearDto::ObjectWrapper>::createShared(); yr->year = yrDb.year;
for (auto& yrDb : yrsDb) { yearRecs->pushBack(yr);
auto yr = dto::YearDto::createShared(); }
yr->id = yrDb.id;
yr->year = yrDb.year;
yearRecs->pushBack(yr); return createDtoResponse(Status::CODE_200, yearRecs);
} }
return createDtoResponse(Status::CODE_200, yearRecs); // endpoint for retrieving single year record by the year id in json format
} ENDPOINT("GET", "/api/v1/year/{id}", yearRecord,
REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth,
type::Scope::retrieveYear), Status::CODE_403, "Not allowed");
// endpoint for retrieving single year record by the year id in json format database::YearRepository yrRepo(m_bConf);
ENDPOINT("GET", "/api/v1/year/{id}", yearRecord, model::Year yrDb(id);
REQUEST(std::shared_ptr<IncomingRequest>, request), PATH(Int32, id)) {
auto authHeader = request->getHeader("Authorization");
OATPP_ASSERT_HTTP(authHeader, Status::CODE_403, "Nope");
auto auth = authHeader->std_str();
manager::TokenManager tok;
OATPP_ASSERT_HTTP(tok.isTokenValid(auth, type::Scope::retrieveYear), Status::CODE_403, "Not allowed");
database::YearRepository yrRepo(m_bConf); OATPP_ASSERT_HTTP(yrRepo.doesYearExist(yrDb,
model::Year yrDb(id); type::YearFilter::id) , Status::CODE_403, "year does not exist");
OATPP_ASSERT_HTTP(yrRepo.doesYearExist(yrDb, type::YearFilter::id) , Status::CODE_403, "year does not exist"); std::cout << "year exist\n";
yrDb = yrRepo.retrieveRecord(yrDb, type::YearFilter::id);
std::cout << "year exist" << std::endl; auto year = dto::YearDto::createShared();
yrDb = yrRepo.retrieveRecord(yrDb, type::YearFilter::id); year->id = yrDb.id;
year->year= yrDb.year;
auto year = dto::YearDto::createShared(); return createDtoResponse(Status::CODE_200, year);
year->id = yrDb.id; }
year->year= yrDb.year;
return createDtoResponse(Status::CODE_200, year); #include OATPP_CODEGEN_END(ApiController)
} private:
model::BinaryPath m_bConf;
#include OATPP_CODEGEN_END(ApiController) };
private:
std::string m_exe_path;
model::BinaryPath m_bConf;
};
} }
#endif #endif
+10 -7
View File
@@ -1,6 +1,7 @@
#ifndef ALBUMREPOSITORY_H_ #ifndef ALBUMREPOSITORY_H_
#define ALBUMREPOSITORY_H_ #define ALBUMREPOSITORY_H_
#include <memory>
#include <utility> #include <utility>
#include <vector> #include <vector>
@@ -8,10 +9,8 @@
#include "model/Models.h" #include "model/Models.h"
#include "type/AlbumFilter.h" #include "type/AlbumFilter.h"
namespace database namespace database {
{ class AlbumRepository : public BaseRepository {
class AlbumRepository : public BaseRepository
{
public: public:
AlbumRepository(const model::BinaryPath&); AlbumRepository(const model::BinaryPath&);
@@ -30,9 +29,13 @@ namespace database
std::pair<model::Album, int> parseRecordWithSongCount(MYSQL_STMT*); std::pair<model::Album, int> parseRecordWithSongCount(MYSQL_STMT*);
// TODO: after parseRecord(MYSQL_STMT*) is implemented remove std::shared_ptr<MYSQL_BIND> valueBind(model::Album&,
// parseRecord(MYSQL_RES*) std::tuple<char*, char*>&);
model::Album parseRecord(MYSQL_RES*); std::shared_ptr<MYSQL_BIND> valueBindWithSongCount(model::Album&,
std::tuple<char*, char*>&, int&);
std::tuple<char*, char*> metadataBuffer();
model::Album parseRecord(MYSQL_STMT*); model::Album parseRecord(MYSQL_STMT*);
}; };
} }
+12 -8
View File
@@ -1,6 +1,7 @@
#ifndef ARTISTREPOSITORY_H_ #ifndef ARTISTREPOSITORY_H_
#define ARTISTREPOSITORY_H_ #define ARTISTREPOSITORY_H_
#include <memory>
#include <utility> #include <utility>
#include <vector> #include <vector>
@@ -8,16 +9,15 @@
#include "model/Models.h" #include "model/Models.h"
#include "type/ArtistFilter.h" #include "type/ArtistFilter.h"
namespace database namespace database {
{ class ArtistRepository : public BaseRepository {
class ArtistRepository : public BaseRepository
{
public: public:
ArtistRepository(const model::BinaryPath&); ArtistRepository(const model::BinaryPath&);
std::vector<model::Artist> retrieveRecords(); std::vector<model::Artist> retrieveRecords();
std::pair<model::Artist, int> retrieveRecordWithSongCount(model::Artist&, type::ArtistFilter); std::pair<model::Artist, int> retrieveRecordWithSongCount(
model::Artist&, type::ArtistFilter);
model::Artist retrieveRecord(model::Artist&, type::ArtistFilter); model::Artist retrieveRecord(model::Artist&, type::ArtistFilter);
@@ -30,9 +30,13 @@ namespace database
std::pair<model::Artist, int> parseRecordWithSongCount(MYSQL_STMT*); std::pair<model::Artist, int> parseRecordWithSongCount(MYSQL_STMT*);
// TODO: After parseRecord(MYSQL_STMT*) is implemented std::shared_ptr<MYSQL_BIND> valueBind(model::Artist&,
// remove parseRecord(MYSQL_RES*) std::tuple<char*>&);
model::Artist parseRecord(MYSQL_RES*); std::shared_ptr<MYSQL_BIND> valueBindWithSongCount(model::Artist&,
std::tuple<char*>&, int&);
std::tuple<char*> metadataBuffer();
model::Artist parseRecord(MYSQL_STMT*); model::Artist parseRecord(MYSQL_STMT*);
}; };
} }
+3 -5
View File
@@ -7,12 +7,10 @@
#include "model/Models.h" #include "model/Models.h"
namespace database namespace database {
{ class BaseRepository {
class BaseRepository
{
public: public:
BaseRepository(); BaseRepository() = default;
BaseRepository(const std::string&); BaseRepository(const std::string&);
BaseRepository(const model::BinaryPath&); BaseRepository(const model::BinaryPath&);
+9 -9
View File
@@ -1,6 +1,7 @@
#ifndef COVERARTREPOSITORY_H_ #ifndef COVERARTREPOSITORY_H_
#define COVERARTREPOSITORY_H_ #define COVERARTREPOSITORY_H_
#include <memory>
#include <vector> #include <vector>
#include <mysql/mysql.h> #include <mysql/mysql.h>
@@ -9,12 +10,9 @@
#include "model/Models.h" #include "model/Models.h"
#include "type/CoverFilter.h" #include "type/CoverFilter.h"
namespace database namespace database {
{ class CoverArtRepository : public BaseRepository {
class CoverArtRepository : public BaseRepository
{
public: public:
CoverArtRepository(const std::string&);
CoverArtRepository(const model::BinaryPath&); CoverArtRepository(const model::BinaryPath&);
std::vector<model::Cover> retrieveRecords(); std::vector<model::Cover> retrieveRecords();
@@ -23,15 +21,17 @@ namespace database
bool doesCoverArtExist(const model::Cover&, type::CoverFilter); bool doesCoverArtExist(const model::Cover&, type::CoverFilter);
void deleteRecord(const model::Cover&); void deleteRecord(const model::Cover&, type::CoverFilter = type::CoverFilter::id);
void saveRecord(const model::Cover&); void saveRecord(const model::Cover&);
void updateRecord(const model::Cover&); void updateRecord(const model::Cover&);
private: private:
std::vector<model::Cover> parseRecords(MYSQL_STMT*); std::vector<model::Cover> parseRecords(MYSQL_STMT*);
// TODO: After parseRecord(MYSQL_STMT*) is implemented std::shared_ptr<MYSQL_BIND> valueBind(model::Cover&,
// remove parseRecord(MYSQL_RES*) std::tuple<char*, char*>&);
model::Cover parseRecord(MYSQL_RES*);
std::tuple<char*, char*> metadataBuffer();
model::Cover parseRecord(MYSQL_STMT*); model::Cover parseRecord(MYSQL_STMT*);
}; };
} }
+10 -7
View File
@@ -1,6 +1,7 @@
#ifndef GENREREPOSITORY_H_ #ifndef GENREREPOSITORY_H_
#define GENREREPOSITORY_H_ #define GENREREPOSITORY_H_
#include <memory>
#include <utility> #include <utility>
#include <vector> #include <vector>
@@ -8,10 +9,8 @@
#include "model/Models.h" #include "model/Models.h"
#include "type/GenreFilter.h" #include "type/GenreFilter.h"
namespace database namespace database {
{ class GenreRepository : public BaseRepository {
class GenreRepository : public BaseRepository
{
public: public:
GenreRepository(const model::BinaryPath&); GenreRepository(const model::BinaryPath&);
@@ -30,9 +29,13 @@ namespace database
std::pair<model::Genre, int> parseRecordWithSongCount(MYSQL_STMT*); std::pair<model::Genre, int> parseRecordWithSongCount(MYSQL_STMT*);
// TODO: After parseRecord(MYSQL_STMT*) is implemented std::shared_ptr<MYSQL_BIND> valueBind(model::Genre&,
// remove parseRecord(MYSQL_RES*) std::tuple<char*>&);
model::Genre parseRecord(MYSQL_RES*); std::shared_ptr<MYSQL_BIND> valueBindWithSongCount(model::Genre&,
std::tuple<char*>&, int&);
std::tuple<char*> metadataBuffer();
model::Genre parseRecord(MYSQL_STMT*); model::Genre parseRecord(MYSQL_STMT*);
}; };
} }
+10 -9
View File
@@ -2,6 +2,7 @@
#define SONGREPOSITORY_H_ #define SONGREPOSITORY_H_
#include <memory> #include <memory>
#include <utility>
#include <vector> #include <vector>
#include <mysql/mysql.h> #include <mysql/mysql.h>
@@ -10,28 +11,28 @@
#include "model/Models.h" #include "model/Models.h"
#include "type/SongFilter.h" #include "type/SongFilter.h"
namespace database namespace database {
{ class SongRepository : public BaseRepository {
class SongRepository : public BaseRepository
{
public: public:
SongRepository(const std::string&);
SongRepository(const model::BinaryPath&); SongRepository(const model::BinaryPath&);
std::vector<model::Song> retrieveRecords(); std::vector<model::Song> retrieveRecords();
model::Song retrieveRecord(model::Song&, type::SongFilter); model::Song retrieveRecord(const model::Song&, type::SongFilter = type::SongFilter::id);
bool doesSongExist(const model::Song&, type::SongFilter); bool doesSongExist(const model::Song&, type::SongFilter = type::SongFilter::id);
bool deleteRecord(const model::Song&); bool deleteRecord(const model::Song&);
void saveRecord(const model::Song&); void saveRecord(const model::Song&);
void updateRecord(const model::Song&); void updateRecord(const model::Song&);
private: private:
std::vector<model::Song> parseRecords(MYSQL_RES*); // TODO: to be removed std::shared_ptr<MYSQL_BIND> valueBind(model::Song&,
std::tuple<char*, char*, char*, char*, char*, char*>&);
std::tuple<char*, char*, char*, char*, char*, char*> metadataBuffer();
std::vector<model::Song> parseRecords(MYSQL_STMT*); std::vector<model::Song> parseRecords(MYSQL_STMT*);
model::Song parseRecord(MYSQL_RES*); // TODO: to be removed
model::Song parseRecord(MYSQL_STMT*); model::Song parseRecord(MYSQL_STMT*);
}; };
} }
+37 -37
View File
@@ -11,48 +11,48 @@
#include "type/UserFilter.h" #include "type/UserFilter.h"
namespace database { namespace database {
class UserRepository : BaseRepository { class UserRepository : BaseRepository {
public: public:
UserRepository(const model::BinaryPath&); UserRepository(const model::BinaryPath&);
model::User retrieveUserRecord(model::User&, type::UserFilter); model::User retrieveUserRecord(model::User&, type::UserFilter);
model::PassSec retrieverUserSaltRecord(model::PassSec&, type::SaltFilter); model::PassSec retrieverUserSaltRecord(model::PassSec&, type::SaltFilter);
bool doesUserRecordExist(const model::User&, type::UserFilter); bool doesUserRecordExist(const model::User&, type::UserFilter);
void saveUserRecord(const model::User&); void saveUserRecord(const model::User&);
void saveUserSalt(const model::PassSec&); void saveUserSalt(const model::PassSec&);
private: private:
struct UserLengths; struct UserLengths;
struct SaltLengths; struct SaltLengths;
struct UserLengths struct UserLengths {
{ unsigned long firstnameLength;
unsigned long firstnameLength; unsigned long lastnameLength;
unsigned long lastnameLength; unsigned long phoneLength;
unsigned long phoneLength; unsigned long emailLength;
unsigned long emailLength; unsigned long usernameLength;
unsigned long usernameLength; unsigned long passwordLength;
unsigned long passwordLength; };
struct SaltLengths {
unsigned long saltLength;
};
std::shared_ptr<MYSQL_BIND> insertUserValues(const model::User&,
std::shared_ptr<UserLengths>);
std::shared_ptr<MYSQL_BIND> insertSaltValues(const model::PassSec&,
std::shared_ptr<SaltLengths>);
std::shared_ptr<MYSQL_BIND> valueBind(model::User&,
std::tuple<char*, char*, char*, char*, char*, char*>&);
std::shared_ptr<MYSQL_BIND> saltValueBind(model::PassSec&, char*);
std::shared_ptr<UserLengths> fetchUserLengths(const model::User&);
std::shared_ptr<SaltLengths> fetchSaltLengths(const model::PassSec&);
std::tuple<char*, char*, char*, char*, char*, char*> fetchUV();
model::User parseRecord(MYSQL_STMT*);
model::PassSec parseSaltRecord(MYSQL_STMT*);
}; };
struct SaltLengths
{
unsigned long saltLength;
};
std::shared_ptr<MYSQL_BIND> insertUserValues(const model::User&, std::shared_ptr<UserLengths>);
std::shared_ptr<MYSQL_BIND> insertSaltValues(const model::PassSec&, std::shared_ptr<SaltLengths>);
std::shared_ptr<MYSQL_BIND> valueBind(model::User&, std::tuple<char*, char*, char*, char*, char*, char*>&);
std::shared_ptr<MYSQL_BIND> saltValueBind(model::PassSec&, char*);
std::shared_ptr<UserLengths> fetchUserLengths(const model::User&);
std::shared_ptr<SaltLengths> fetchSaltLengths(const model::PassSec&);
std::tuple<char*, char*, char*, char*, char*, char*> fetchUV();
model::User parseRecord(MYSQL_STMT*);
model::PassSec parseSaltRecord(MYSQL_STMT*);
};
} }
#endif #endif
+12 -11
View File
@@ -1,6 +1,7 @@
#ifndef YEARREPOSITORY_H_ #ifndef YEARREPOSITORY_H_
#define YEARREPOSITORY_H_ #define YEARREPOSITORY_H_
#include <memory>
#include <utility> #include <utility>
#include <vector> #include <vector>
@@ -8,31 +9,31 @@
#include "model/Models.h" #include "model/Models.h"
#include "type/YearFilter.h" #include "type/YearFilter.h"
namespace database namespace database {
{ class YearRepository : public BaseRepository {
class YearRepository : public BaseRepository
{
public: public:
YearRepository(const model::BinaryPath&); YearRepository(const model::BinaryPath&);
std::vector<model::Year> retrieveRecords(); std::vector<model::Year> retrieveRecords();
std::pair<model::Year, int> retrieveRecordWithSongCount(model::Year&, type::YearFilter); std::pair<model::Year, int> retrieveRecordWithSongCount(model::Year&,
type::YearFilter = type::YearFilter::id);
model::Year retrieveRecord(model::Year&, type::YearFilter); model::Year retrieveRecord(model::Year&, type::YearFilter = type::YearFilter::id);
bool doesYearExist(const model::Year&, type::YearFilter); bool doesYearExist(const model::Year&, type::YearFilter = type::YearFilter::id);
void saveRecord(const model::Year&); void saveRecord(const model::Year&);
void deleteYear(const model::Year&, type::YearFilter); void deleteYear(const model::Year&, type::YearFilter = type::YearFilter::id);
private: private:
std::vector<model::Year> parseRecords(MYSQL_STMT*); std::vector<model::Year> parseRecords(MYSQL_STMT*);
std::pair<model::Year, int> parseRecordWithSongCount(MYSQL_STMT*); std::pair<model::Year, int> parseRecordWithSongCount(MYSQL_STMT*);
// TODO: After parseRecord(MYSQL_STMT*) is implemented std::shared_ptr<MYSQL_BIND> valueBind(model::Year&);
// remove parseRecord(MYSQL_RES*) std::shared_ptr<MYSQL_BIND> valueBindWithSongCount(model::Year&,
model::Year parseRecord(MYSQL_RES*); int&);
model::Year parseRecord(MYSQL_STMT*); model::Year parseRecord(MYSQL_STMT*);
}; };
} }
+3 -4
View File
@@ -4,16 +4,15 @@
#include "oatpp/core/data/mapping/type/Object.hpp" #include "oatpp/core/data/mapping/type/Object.hpp"
#include "oatpp/core/macro/codegen.hpp" #include "oatpp/core/macro/codegen.hpp"
namespace dto namespace dto {
{
#include OATPP_CODEGEN_BEGIN(DTO) #include OATPP_CODEGEN_BEGIN(DTO)
class AlbumDto : public oatpp::data::mapping::type::Object class AlbumDto : public oatpp::data::mapping::type::Object {
{
DTO_INIT(AlbumDto, Object) DTO_INIT(AlbumDto, Object)
DTO_FIELD(Int32, id); DTO_FIELD(Int32, id);
DTO_FIELD(String, title); DTO_FIELD(String, title);
DTO_FIELD(String, artist);
DTO_FIELD(Int32, year); DTO_FIELD(Int32, year);
}; };
+2 -4
View File
@@ -4,12 +4,10 @@
#include "oatpp/core/data/mapping/type/Object.hpp" #include "oatpp/core/data/mapping/type/Object.hpp"
#include "oatpp/core/macro/codegen.hpp" #include "oatpp/core/macro/codegen.hpp"
namespace dto namespace dto {
{
#include OATPP_CODEGEN_BEGIN(DTO) #include OATPP_CODEGEN_BEGIN(DTO)
class ArtistDto : public oatpp::data::mapping::type::Object class ArtistDto : public oatpp::data::mapping::type::Object {
{
DTO_INIT(ArtistDto, Object) DTO_INIT(ArtistDto, Object)
DTO_FIELD(Int32, id); DTO_FIELD(Int32, id);
+2 -4
View File
@@ -4,12 +4,10 @@
#include "oatpp/core/data/mapping/type/Object.hpp" #include "oatpp/core/data/mapping/type/Object.hpp"
#include "oatpp/core/macro/codegen.hpp" #include "oatpp/core/macro/codegen.hpp"
namespace dto namespace dto {
{
#include OATPP_CODEGEN_BEGIN(DTO) #include OATPP_CODEGEN_BEGIN(DTO)
class CoverArtDto : public oatpp::data::mapping::type::Object class CoverArtDto : public oatpp::data::mapping::type::Object {
{
DTO_INIT(CoverArtDto, Object) DTO_INIT(CoverArtDto, Object)
DTO_FIELD(Int32, id); DTO_FIELD(Int32, id);
+2 -4
View File
@@ -4,12 +4,10 @@
#include "oatpp/core/data/mapping/type/Object.hpp" #include "oatpp/core/data/mapping/type/Object.hpp"
#include "oatpp/core/macro/codegen.hpp" #include "oatpp/core/macro/codegen.hpp"
namespace dto namespace dto {
{
#include OATPP_CODEGEN_BEGIN(DTO) #include OATPP_CODEGEN_BEGIN(DTO)
class GenreDto : public oatpp::data::mapping::type::Object class GenreDto : public oatpp::data::mapping::type::Object {
{
DTO_INIT(GenreDto, Object) DTO_INIT(GenreDto, Object)
DTO_FIELD(Int32, id); DTO_FIELD(Int32, id);
+4 -4
View File
@@ -6,23 +6,23 @@
#include "model/Models.h" #include "model/Models.h"
namespace dto namespace dto {
{
#include OATPP_CODEGEN_BEGIN(DTO) #include OATPP_CODEGEN_BEGIN(DTO)
class SongDto : public oatpp::data::mapping::type::Object class SongDto : public oatpp::data::mapping::type::Object {
{
DTO_INIT(SongDto, Object) DTO_INIT(SongDto, Object)
DTO_FIELD(Int32, id); DTO_FIELD(Int32, id);
DTO_FIELD(String, title); DTO_FIELD(String, title);
DTO_FIELD(String, artist); DTO_FIELD(String, artist);
DTO_FIELD(String, album_artist);
DTO_FIELD(String, album); DTO_FIELD(String, album);
DTO_FIELD(String, genre); DTO_FIELD(String, genre);
DTO_FIELD(Int32, track); DTO_FIELD(Int32, track);
DTO_FIELD(Int32, disc); DTO_FIELD(Int32, disc);
DTO_FIELD(Int32, year); DTO_FIELD(Int32, year);
DTO_FIELD(Int32, duration); DTO_FIELD(Int32, duration);
DTO_FIELD(Int32, coverart_id);
}; };
#include OATPP_CODEGEN_END(DTO) #include OATPP_CODEGEN_END(DTO)
+2 -4
View File
@@ -4,12 +4,10 @@
#include "oatpp/core/data/mapping/type/Object.hpp" #include "oatpp/core/data/mapping/type/Object.hpp"
#include "oatpp/core/macro/codegen.hpp" #include "oatpp/core/macro/codegen.hpp"
namespace dto namespace dto {
{
#include OATPP_CODEGEN_BEGIN(DTO) #include OATPP_CODEGEN_BEGIN(DTO)
class YearDto : public oatpp::data::mapping::type::Object class YearDto : public oatpp::data::mapping::type::Object {
{
DTO_INIT(YearDto, Object) DTO_INIT(YearDto, Object)
DTO_FIELD(Int32, id); DTO_FIELD(Int32, id);
+5
View File
@@ -1,6 +1,7 @@
#ifndef DTOCONVERSIONS_H_ #ifndef DTOCONVERSIONS_H_
#define DTOCONVERSIONS_H_ #define DTOCONVERSIONS_H_
#include "dto/AlbumDto.hpp"
#include "dto/LoginResultDto.hpp" #include "dto/LoginResultDto.hpp"
#include "dto/SongDto.hpp" #include "dto/SongDto.hpp"
#include "model/Models.h" #include "model/Models.h"
@@ -13,6 +14,10 @@ namespace dto { namespace conversion {
static dto::RegisterResultDto::ObjectWrapper toRegisterResultDto( static dto::RegisterResultDto::ObjectWrapper toRegisterResultDto(
const model::RegisterResult&); 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&); static model::Song toSong(dto::SongDto::ObjectWrapper&);
static model::User toUser(dto::UserDto::ObjectWrapper&); static model::User toUser(dto::UserDto::ObjectWrapper&);
+2 -4
View File
@@ -3,10 +3,8 @@
#include "model/Models.h" #include "model/Models.h"
namespace manager namespace manager {
{ class AlbumManager {
class AlbumManager
{
public: public:
AlbumManager(const model::BinaryPath&); AlbumManager(const model::BinaryPath&);
+2 -4
View File
@@ -3,10 +3,8 @@
#include "model/Models.h" #include "model/Models.h"
namespace manager namespace manager {
{ class ArtistManager {
class ArtistManager
{
public: public:
ArtistManager(const model::BinaryPath&); ArtistManager(const model::BinaryPath&);
+2 -6
View File
@@ -6,12 +6,9 @@
#include "model/Models.h" #include "model/Models.h"
namespace manager namespace manager {
{ class CoverArtManager {
class CoverArtManager
{
public: public:
CoverArtManager(const std::string&);
CoverArtManager(const model::BinaryPath& bConf); CoverArtManager(const model::BinaryPath& bConf);
model::Cover saveCover(const model::Song&); model::Cover saveCover(const model::Song&);
@@ -25,7 +22,6 @@ namespace manager
std::string createImagePath(const model::Song&); std::string createImagePath(const model::Song&);
model::BinaryPath m_bConf; model::BinaryPath m_bConf;
std::string path;
}; };
} }
+12 -6
View File
@@ -3,19 +3,21 @@
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <filesystem>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include "model/Models.h" #include "model/Models.h"
#include "type/PathType.h" #include "type/PathType.h"
namespace manager namespace manager {
{ class DirectoryManager {
class DirectoryManager
{
public: public:
static std::string createDirectoryProcess(model::Song, const std::string&); DirectoryManager() = default;
static std::string createDirectoryProcess(const model::Song&, const model::BinaryPath&, type::PathType);
static std::string createDirectoryProcess(const 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(std::string_view);
static std::string configPath(const model::BinaryPath&); static std::string configPath(const model::BinaryPath&);
static std::string contentOfPath(const std::string&); static std::string contentOfPath(const std::string&);
@@ -30,6 +32,10 @@ namespace manager
void deleteCoverArtFile(const std::string&, const std::string&); void deleteCoverArtFile(const std::string&, const std::string&);
private: private:
std::filesystem::path relativeDiscSongPathFilesystem(const std::filesystem::path&,
const model::Song&);
std::string relativeDiscSongPath(const std::filesystem::path&, const model::Song&);
void deleteSong(const model::Song); void deleteSong(const model::Song);
}; };
} }
+2 -4
View File
@@ -3,10 +3,8 @@
#include "model/Models.h" #include "model/Models.h"
namespace manager namespace manager {
{ class GenreManager {
class GenreManager
{
public: public:
GenreManager(const model::BinaryPath&); GenreManager(const model::BinaryPath&);
+7 -12
View File
@@ -4,30 +4,26 @@
#include <iostream> #include <iostream>
#include <map> #include <map>
#include <string> #include <string>
#include <utility>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include "dto/SongDto.hpp" #include "dto/SongDto.hpp"
#include "model/Models.h" #include "model/Models.h"
#include "type/SongChanged.h" #include "type/SongChanged.h"
#include "type/SongUpload.h"
namespace manager namespace manager {
{ class SongManager {
class SongManager
{
public: public:
SongManager(std::string&);
SongManager(const model::BinaryPath&); SongManager(const model::BinaryPath&);
std::pair<bool, type::SongUpload> saveSong(model::Song&);
bool didSongChange(const model::Song&, const model::Song&); bool didSongChange(const model::Song&, const model::Song&);
bool requiresFilesystemChange(const model::Song&, const model::Song&); bool requiresFilesystemChange(const model::Song&, const model::Song&);
bool deleteSong(model::Song&);
void saveSong(model::Song&); bool updateSong(model::Song&);
void deleteSong(model::Song&);
void updateSong(model::Song&);
static model::Song songDtoConv(dto::SongDto::ObjectWrapper&);
static void printSong(const model::Song&); static void printSong(const model::Song&);
private: private:
@@ -47,7 +43,6 @@ namespace manager
void modifySongOnFilesystem(model::Song&, const model::Song&); void modifySongOnFilesystem(model::Song&, const model::Song&);
model::BinaryPath m_bConf; model::BinaryPath m_bConf;
std::string exe_path;
}; };
} }
+14 -15
View File
@@ -14,27 +14,26 @@
#include "type/Scopes.h" #include "type/Scopes.h"
namespace manager { namespace manager {
class TokenManager { class TokenManager {
public: public:
TokenManager(); TokenManager() = default;
model::Token retrieveToken(const model::BinaryPath&); model::Token retrieveToken(const model::BinaryPath&);
bool isTokenValid(std::string&, type::Scope); bool isTokenValid(std::string&, type::Scope);
bool testAuth(const model::BinaryPath&); bool testAuth(const model::BinaryPath&);
private: private:
cpr::Response sendRequest(std::string_view, nlohmann::json&); cpr::Response sendRequest(std::string_view, nlohmann::json&);
nlohmann::json createTokenBody(const model::AuthCredentials&); nlohmann::json createTokenBody(const model::AuthCredentials&);
model::AuthCredentials parseAuthCredentials(std::string_view); model::AuthCredentials parseAuthCredentials(const model::BinaryPath&);
model::AuthCredentials parseAuthCredentials(const model::BinaryPath&);
std::vector<std::string> extractScopes(const jwt::decoded_jwt&&); std::vector<std::string> extractScopes(const jwt::decoded_jwt&&);
std::pair<bool, std::vector<std::string>> fetchAuthHeader(const std::string&); std::pair<bool, std::vector<std::string>> fetchAuthHeader(const std::string&);
bool tokenSupportsScope(const std::vector<std::string>&, const std::string&&); bool tokenSupportsScope(const std::vector<std::string>&, const std::string&&);
}; };
} }
#endif #endif
+10 -10
View File
@@ -7,19 +7,19 @@
#include "model/Models.h" #include "model/Models.h"
namespace manager { namespace manager {
class UserManager { class UserManager {
public: public:
UserManager(const model::BinaryPath&); UserManager(const model::BinaryPath&);
model::RegisterResult registerUser(model::User&); model::RegisterResult registerUser(model::User&);
bool doesUserExist(const model::User&); bool doesUserExist(const model::User&);
bool validatePassword(const model::User&); bool validatePassword(const model::User&);
void printUser(const model::User&); void printUser(const model::User&);
private: private:
model::BinaryPath m_bConf; model::BinaryPath m_bConf;
}; };
} }
#endif #endif
+2 -4
View File
@@ -3,10 +3,8 @@
#include "model/Models.h" #include "model/Models.h"
namespace manager namespace manager {
{ class YearManager {
class YearManager
{
public: public:
YearManager(const model::BinaryPath&); YearManager(const model::BinaryPath&);
+144 -144
View File
@@ -5,172 +5,172 @@
#include <vector> #include <vector>
namespace model { namespace model {
struct Song { class Song {
Song() = default; public:
Song(const int id) : id(id) { } 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; int id;
std::string title; std::string title;
std::string artist; std::string artist;
std::string album; std::string album;
std::string genre; std::string albumArtist;
int year; std::string genre;
int duration; int year;
int track; int duration;
int disc; int track;
std::string songPath; int disc;
std::vector<unsigned char> data; std::string songPath;
int coverArtId; std::vector<unsigned char> data;
int artistId; int coverArtId;
int albumId; int artistId;
int genreId; int albumId;
int yearId; int genreId;
}; int yearId;
};
struct Artist { class Artist {
Artist() = default; public:
Artist(const Song& song) Artist() = default;
{ Artist(const Song& song) : id(song.artistId), artist(song.artist) { }
id = song.artistId; Artist(const int id) : id(id) { }
artist = song.artist;
}
Artist(const int id) : id(id) { }
int id; int id;
std::string artist; std::string artist;
}; };
struct Album { class Album {
Album() = default; public:
Album(const Song& song) Album() = default;
{ Album(const Song& song) :
id = song.albumId; id(song.albumId), title(song.album),artist(song.artist), year(song.year) { }
title = song.album; Album(const int id) : id(id) { }
year = song.year;
}
Album(const int id) : id(id) { }
int id; int id;
std::string title; std::string title;
int year; std::string artist;
std::vector<Song> songs; int year;
}; std::vector<Song> songs;
};
struct Genre { class Genre {
Genre() = default; public:
Genre(const Song& song) Genre() = default;
{ Genre(const Song& song) :
id = song.genreId; id(song.genreId), category(song.genre) { }
category = song.genre; Genre(const int id) : id(id) { }
}
Genre(const int id) : id(id) { }
int id; int id;
std::string category; std::string category;
}; };
struct Year { class Year {
Year() = default; public:
Year(const Song& song) Year() = default;
{ Year(const Song& song) :
id = song.yearId; id(song.yearId), year(song.year) { }
year = song.year; Year(const int id) : id(id) { }
}
Year(const int id) : id(id) { }
int id; int id;
int year; int year;
}; };
struct Cover { class Cover {
Cover() = default; public:
Cover(const Song& song) Cover() = default;
{ Cover(const Song& song) :
id = song.coverArtId; id(song.coverArtId), songTitle(song.title) { }
songTitle = song.title; Cover(const int id) : id(id) { }
}
Cover(const int id) : id(id) { }
int id; int id;
std::string songTitle; std::string songTitle;
std::string imagePath; std::string imagePath;
// Not being used but it should be // Not being used but it should be
std::vector<unsigned char> data; std::vector<unsigned char> data;
}; };
class Token { class Token {
public: public:
Token() = default; Token() = default;
Token(const std::string& accessToken) : Token(const std::string& accessToken) :
accessToken(accessToken) { } accessToken(accessToken) { }
Token(const std::string& accessToken, const std::string& tokenType, Token(const std::string& accessToken, const std::string& tokenType,
const int expiration) : const int expiration) :
accessToken(accessToken), tokenType(tokenType), accessToken(accessToken), tokenType(tokenType),
expiration(expiration) { } expiration(expiration) { }
std::string accessToken; std::string accessToken;
int expiration; int expiration;
std::string tokenType; std::string tokenType;
}; };
struct LoginResult { struct LoginResult {
int userId; int userId;
std::string username; std::string username;
std::string accessToken; std::string accessToken;
std::string tokenType; std::string tokenType;
std::string message; std::string message;
int expiration; int expiration;
}; };
class RegisterResult { class RegisterResult {
public: public:
std::string username; std::string username;
bool registered; bool registered;
std::string message; std::string message;
}; };
struct User { struct User {
int id; int id;
std::string firstname; std::string firstname;
std::string lastname; std::string lastname;
std::string email; std::string email;
std::string phone; std::string phone;
std::string username; std::string username;
std::string password; std::string password;
}; };
struct PassSec { struct PassSec {
int id; int id;
std::string hashPassword; std::string hashPassword;
std::string salt; std::string salt;
int userId; int userId;
}; };
struct AuthCredentials { struct AuthCredentials {
std::string domain; std::string domain;
std::string apiIdentifier; std::string apiIdentifier;
std::string clientId; std::string clientId;
std::string clientSecret; std::string clientSecret;
std::string uri; std::string uri;
std::string endpoint; std::string endpoint;
}; };
struct DatabaseConnection { struct DatabaseConnection {
std::string server; std::string server;
std::string username; std::string username;
std::string password; std::string password;
std::string database; std::string database;
}; };
struct BinaryPath { class BinaryPath {
BinaryPath() = default; public:
BinaryPath(const char *p) : path(std::move(p)) { } BinaryPath() = default;
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) { }
BinaryPath(const std::string&& p) : path(p) { }
std::string path; std::string path;
}; };
} }
#endif #endif
+2 -4
View File
@@ -1,10 +1,8 @@
#ifndef ALBUMFILTER_H_ #ifndef ALBUMFILTER_H_
#define ALBUMFILTER_H_ #define ALBUMFILTER_H_
namespace type namespace type {
{ enum class AlbumFilter {
enum class AlbumFilter
{
id = 0, id = 0,
title, title,
year year
+2 -4
View File
@@ -1,10 +1,8 @@
#ifndef ARTISTFILTER_H_ #ifndef ARTISTFILTER_H_
#define ARTISTFILTER_H_ #define ARTISTFILTER_H_
namespace type namespace type {
{ enum class ArtistFilter {
enum class ArtistFilter
{
id = 0, id = 0,
artist artist
}; };
+2 -4
View File
@@ -1,10 +1,8 @@
#ifndef COVERFILTER_H_ #ifndef COVERFILTER_H_
#define COVERFILTER_H_ #define COVERFILTER_H_
namespace type namespace type {
{ enum class CoverFilter {
enum class CoverFilter
{
id = 0, id = 0,
songTitle, songTitle,
imagePath imagePath
+2 -4
View File
@@ -1,10 +1,8 @@
#ifndef GENREFILTER_H_ #ifndef GENREFILTER_H_
#define GENREFILTER_H_ #define GENREFILTER_H_
namespace type namespace type {
{ enum class GenreFilter {
enum class GenreFilter
{
id = 0, id = 0,
category category
}; };
+2 -4
View File
@@ -1,10 +1,8 @@
#ifndef PATHTYPE_H_ #ifndef PATHTYPE_H_
#define PATHTYPE_H_ #define PATHTYPE_H_
namespace type namespace type {
{ enum PathType {
enum PathType
{
music = 0, music = 0,
archive, archive,
temp, temp,
+1 -2
View File
@@ -2,8 +2,7 @@
#define SALTFILTER_H_ #define SALTFILTER_H_
namespace type { namespace type {
enum class SaltFilter enum class SaltFilter {
{
id = 0, id = 0,
salt, salt,
userId userId
+2 -4
View File
@@ -1,10 +1,8 @@
#ifndef SCOPES_H_ #ifndef SCOPES_H_
#define SCOPES_H_ #define SCOPES_H_
namespace type namespace type {
{ enum class Scope {
enum class Scope
{
upload = 0, upload = 0,
download, download,
stream, stream,
+2 -4
View File
@@ -1,10 +1,8 @@
#ifndef SONGCHANGED_H_ #ifndef SONGCHANGED_H_
#define SONGCHANGED_H_ #define SONGCHANGED_H_
namespace type namespace type {
{ enum SongChanged {
enum SongChanged
{
title = 0, title = 0,
artist, artist,
album, album,
+7 -5
View File
@@ -1,16 +1,18 @@
#ifndef SONGFILTER_H_ #ifndef SONGFILTER_H_
#define SONGFILTER_H_ #define SONGFILTER_H_
namespace type namespace type {
{ enum class SongFilter {
enum class SongFilter
{
id = 0, id = 0,
title, title,
album, album,
artist, artist,
albumArtist,
genre, genre,
year year,
titleAndArtist,
titleAlbArtistAlbum,
titleAlbArtistAlbumTrack
}; };
} }
+12
View File
@@ -0,0 +1,12 @@
#ifndef SONGUPLOAD_H_
#define SONGUPLOAD_H_
namespace type {
enum class SongUpload {
Successful = 0,
AlreadyExist = 1,
Failed = 2
};
}
#endif
+1 -2
View File
@@ -2,8 +2,7 @@
#define USERFILTER_H_ #define USERFILTER_H_
namespace type { namespace type {
enum class UserFilter enum class UserFilter {
{
id = 0, id = 0,
username username
}; };
+2 -4
View File
@@ -1,10 +1,8 @@
#ifndef YEARFILTER_H_ #ifndef YEARFILTER_H_
#define YEARFILTER_H_ #define YEARFILTER_H_
namespace type namespace type {
{ enum class YearFilter {
enum class YearFilter
{
id = 0, id = 0,
year year
}; };
+2 -4
View File
@@ -13,10 +13,8 @@
#include <tpropertymap.h> #include <tpropertymap.h>
#include <id3v2tag.h> #include <id3v2tag.h>
namespace utility namespace utility {
{ class ImageFile : public TagLib::File {
class ImageFile : public TagLib::File
{
public: public:
ImageFile(const char *file); ImageFile(const char *file);
+17 -12
View File
@@ -4,23 +4,28 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <attachedpictureframe.h>
#include <textidentificationframe.h>
#include <fileref.h>
#include <mpegfile.h>
#include <tag.h>
#include "model/Models.h" #include "model/Models.h"
namespace utility { namespace utility {
class MetadataRetriever class MetadataRetriever {
{ public:
public: model::Song retrieveMetadata(model::Song&);
model::Song retrieveMetadata(std::string&);
model::Cover updateCoverArt(const model::Song&, model::Cover&, const std::string&);
model::Cover applyStockCoverArt(const model::Song&, model::Cover&, const std::string&);
model::Cover applyCoverArt(const model::Song&, model::Cover&);
model::Cover updateCoverArt(const model::Song&, model::Cover&, const std::string&); bool songContainsCoverArt(const model::Song&);
model::Cover applyStockCoverArt(const model::Song&, model::Cover&, const std::string&);
model::Cover applyCoverArt(const model::Song&, model::Cover&);
bool songContainsCoverArt(const model::Song&); void updateMetadata(model::Song&, const model::Song&);
private:
void updateMetadata(model::Song&, const model::Song&); };
private:
};
} }
#endif #endif
+1 -1
View File
@@ -12,7 +12,7 @@ public:
bool isPasswordValid(const model::User&, const model::PassSec&); bool isPasswordValid(const model::User&, const model::PassSec&);
private: private:
int saltSize(); constexpr int saltSize() noexcept;
}; };
} }
+2 -4
View File
@@ -5,10 +5,8 @@
#include "model/Models.h" #include "model/Models.h"
namespace verify namespace verify {
{ class Initialization {
class Initialization
{
public: public:
static bool skipVerification(const std::string&); static bool skipVerification(const std::string&);
+3 -5
View File
@@ -23,8 +23,7 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
void run(const model::BinaryPath& bConf) void run(const model::BinaryPath& bConf) {
{
component::AppComponent component; component::AppComponent component;
OATPP_COMPONENT(std::shared_ptr<oatpp::web::server::HttpRouter>, router); OATPP_COMPONENT(std::shared_ptr<oatpp::web::server::HttpRouter>, router);
@@ -58,8 +57,7 @@ void run(const model::BinaryPath& bConf)
server.run(); server.run();
} }
int main(int argc, char **argv) int main(int argc, char **argv) {
{
oatpp::base::Environment::init(); oatpp::base::Environment::init();
model::BinaryPath bConf(std::move(argv[0])); model::BinaryPath bConf(std::move(argv[0]));
@@ -68,7 +66,7 @@ int main(int argc, char **argv)
if (!verify::Initialization::skipVerification(argv[1])) { if (!verify::Initialization::skipVerification(argv[1])) {
verify::Initialization::checkIcarus(bConf); verify::Initialization::checkIcarus(bConf);
} else { } else {
std::cout << "skiping verifyication" << std::endl; std::cout << "skiping verifyication\n";
} }
} else { } else {
verify::Initialization::checkIcarus(bConf); verify::Initialization::checkIcarus(bConf);
+27 -28
View File
@@ -5,40 +5,39 @@
#include <memory> #include <memory>
namespace callback { namespace callback {
StreamCallback::StreamCallback() : StreamCallback::StreamCallback() : m_counter(0) { }
m_counter(0) { }
StreamCallback::StreamCallback(const std::string& songPath) : StreamCallback::StreamCallback(const std::string& songPath) :
m_songPath(songPath), m_songPath(songPath),
m_counter(0), m_counter(0),
m_bytesRead(0) m_bytesRead(0) {
{ // getting file size
// getting file size std::ifstream song(m_songPath.c_str(), std::ios::binary | std::ios::in | std::ios::ate);
std::ifstream song(m_songPath.c_str(), std::ios::binary | std::ios::in | std::ios::ate); m_fileSize = song.tellg();
m_fileSize = song.tellg(); std::cout << "file size is " << m_fileSize << " bytes\n";
std::cout << "file size is " << m_fileSize << " bytes" << std::endl;
}
oatpp::data::v_io_size StreamCallback::read(void *buff, oatpp::data::v_io_size count)
{
if (m_counter >= m_fileSize) {
std::cout << "done streaming song" << std::endl;
return 0;
} }
std::fstream song(m_songPath.c_str(), std::ios::binary | std::ios::in);
song.seekg(m_counter);
std::unique_ptr<char[]> data(new char[count]);
song.read(data.get(), count); oatpp::v_io_size StreamCallback::read(void *buff, v_buff_size count,
std::memcpy(buff, data.get(), count); oatpp::async::Action& action) {
if (m_counter >= m_fileSize) {
std::cout << "done streaming song\n";
return 0;
}
m_counter += count; std::fstream song(m_songPath.c_str(), std::ios::binary | std::ios::in);
song.seekg(m_counter);
song.close(); std::unique_ptr<char[]> data(new char[count]);
return count; song.read(data.get(), count);
} std::memcpy(buff, data.get(), count);
m_counter += count;
song.close();
return count;
}
} }
+355 -413
View File
@@ -7,443 +7,385 @@
#include <string> #include <string>
#include <cstring> #include <cstring>
database::AlbumRepository::AlbumRepository(const model::BinaryPath& bConf) namespace database {
: BaseRepository(bConf) AlbumRepository::AlbumRepository(const model::BinaryPath& bConf) : BaseRepository(bConf) { }
{ }
std::vector<model::Album> database::AlbumRepository::retrieveRecords() std::vector<model::Album> AlbumRepository::retrieveRecords() {
{ auto conn = setupMysqlConnection();
auto conn = setupMysqlConnection(); auto stmt = mysql_stmt_init(conn);
auto stmt = mysql_stmt_init(conn);
const std::string query = "SELECT * FROM Album"; const std::string query = "SELECT * FROM Album";
mysql_stmt_prepare(stmt, query.c_str(), query.size()); mysql_stmt_prepare(stmt, query.c_str(), query.size());
mysql_stmt_execute(stmt); mysql_stmt_execute(stmt);
auto albums = parseRecords(stmt); auto albums = parseRecords(stmt);
mysql_stmt_close(stmt); mysql_stmt_close(stmt);
mysql_close(conn); mysql_close(conn);
return albums; return albums;
}
std::pair<model::Album, int> database::AlbumRepository::retrieveRecordWithSongCount(model::Album& album, type::AlbumFilter filter = type::AlbumFilter::id)
{
std::cout << "retrieving album with song count" << std::endl;
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
MYSQL_BIND params[4];
std::memset(params, 0, sizeof(params));
qry << "SELECT alb.*, COUNT(*) AS SongCount FROM Album alb LEFT JOIN ";
qry << "Song sng ON alb.AlbumId=sng.AlbumId WHERE ";
switch (filter) {
case type::AlbumFilter::id:
qry << "sng.AlbumId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&album.id;
params[0].length = 0;
params[0].is_null = 0;
break;
default:
break;
}
qry << " GROUP BY alb.AlbumId LIMIT 1";
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);
auto albWSC = parseRecordWithSongCount(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
return albWSC;
}
model::Album database::AlbumRepository::retrieveRecord(model::Album& album, type::AlbumFilter filter)
{
std::cout << "retrieving album record" << std::endl;
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
MYSQL_BIND params[1];
memset(params, 0, sizeof(params));
qry << "SELECT alb.* FROM Album alb WHERE ";
auto titleLength = album.title.size();
switch (filter) {
case type::AlbumFilter::id:
qry << "alb.AlbumId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&album.id;
params[0].length = 0;
params[0].is_null = 0;
break;
case type::AlbumFilter::title:
qry << "alb.Title = ?";
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)album.title.c_str();
params[0].length = &titleLength;
params[0].is_null = 0;
break;
default:
break;
} }
qry << " LIMIT 1";
const auto query = qry.str(); std::pair<model::Album, int> AlbumRepository::retrieveRecordWithSongCount(model::Album& album,
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size()); type::AlbumFilter filter = type::AlbumFilter::id) {
status = mysql_stmt_bind_param(stmt, params); std::cout << "retrieving album with song count\n";
status = mysql_stmt_execute(stmt); std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
album = parseRecord(stmt); MYSQL_BIND params[1];
std::memset(params, 0, sizeof(params));
mysql_stmt_close(stmt); qry << "SELECT alb.*, COUNT(*) AS SongCount FROM Album alb LEFT JOIN ";
mysql_close(conn); qry << "Song sng ON alb.AlbumId=sng.AlbumId WHERE ";
std::cout << "done" << std::endl; switch (filter) {
case type::AlbumFilter::id:
qry << "sng.AlbumId = ?";
return album; params[0].buffer_type = MYSQL_TYPE_LONG;
} params[0].buffer = (char*)&album.id;
params[0].length = 0;
bool database::AlbumRepository::doesAlbumExists(const model::Album& album, type::AlbumFilter filter) params[0].is_null = 0;
{ break;
auto conn = setupMysqlConnection(); default:
auto stmt = mysql_stmt_init(conn); break;
MYSQL_BIND params[1];
memset(params, 0, sizeof(params));
std::stringstream qry;
qry << "SELECT * FROM Album WHERE ";
auto titleLength = album.title.size();
switch (filter) {
case type::AlbumFilter::id:
qry << "AlbumId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&album.id;
params[0].length = 0;
params[0].is_null = 0;
break;
case type::AlbumFilter::title:
qry << "Title = ?";
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)album.title.c_str();
params[0].length = &titleLength;
params[0].is_null = 0;
break;
default:
break;
}
qry << " LIMIT 1";
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);
std::cout << "the query has been performed" << std::endl;
::mysql_stmt_store_result(stmt);
auto rowCount = ::mysql_stmt_num_rows(stmt);
::mysql_stmt_close(stmt);
::mysql_close(conn);
return (rowCount > 0) ? true : false;
}
void database::AlbumRepository::saveAlbum(const model::Album& album)
{
std::cout << "beginning to insert album record" << std::endl;
auto conn = setupMysqlConnection();
MYSQL_STMT *stmt = mysql_stmt_init(conn);
const std::string query = "INSERT INTO Album(Title, Year) VALUES(?, ?)";
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
MYSQL_BIND params[2];
memset(params, 0, sizeof(params));
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)album.title.c_str();
auto titleLength = album.title.size();
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].is_null = 0;
status = mysql_stmt_bind_param(stmt, params);
status = mysql_stmt_execute(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
std::cout << "done inserting album record" << std::endl;
}
void database::AlbumRepository::deleteAlbum(const model::Album& album, type::AlbumFilter filter = type::AlbumFilter::id)
{
std::cout << "deleting album record" << std::endl;
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
MYSQL_BIND params[1];
std::memset(params, 0, sizeof(params));
qry << "DELETE FROM Album WHERE ";
switch (filter) {
case type::AlbumFilter::id:
qry << "AlbumId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&album.id;
params[0].length = 0;
params[0].is_null = 0;
break;
default:
break;
}
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);
std::cout << "execute delete query" << std::endl;
}
std::vector<model::Album> database::AlbumRepository::parseRecords(MYSQL_STMT* stmt)
{
std::cout << "parsing album record" << std::endl;
mysql_stmt_store_result(stmt);
std::vector<model::Album> albums;
albums.reserve(mysql_stmt_num_rows(stmt));
const auto valAmt = 3;
unsigned long len[valAmt];
my_bool nullRes[valAmt];
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);
while (true) {
std::cout << "fetching statement result" << std::endl;
status = mysql_stmt_fetch(stmt);
if (status == 1 || status == MYSQL_NO_DATA) {
break;
}
alb.title = title;
albums.push_back(std::move(alb));
}
} }
std::cout << "fetching next result" << std::endl; qry << " GROUP BY alb.AlbumId LIMIT 1";
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);
auto albWSC = parseRecordWithSongCount(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
return albWSC;
} }
return albums; model::Album AlbumRepository::retrieveRecord(model::Album& album, type::AlbumFilter filter) {
} std::cout << "retrieving album record\n";
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
// TODO: check to see if this is not used, if not then remove it MYSQL_BIND params[1];
model::Album database::AlbumRepository::parseRecord(MYSQL_RES* results) memset(params, 0, sizeof(params));
{
std::cout << "parsing album record" << std::endl;
model::Album album;
auto fieldNum = mysql_num_fields(results); qry << "SELECT alb.* FROM Album alb WHERE ";
auto row = mysql_fetch_row(results);
for (auto i = 0; i != fieldNum; ++i) { auto titleLength = album.title.size();
const std::string field(mysql_fetch_field(results)->name); switch (filter) {
case type::AlbumFilter::id:
qry << "alb.AlbumId = ?";
if (field.compare("AlbumId") == 0) { params[0].buffer_type = MYSQL_TYPE_LONG;
album.id = std::stoi(row[i]); params[0].buffer = (char*)&album.id;
} params[0].length = 0;
if (field.compare("Title") == 0) { params[0].is_null = 0;
album.title = row[i]; break;
} case type::AlbumFilter::title:
if (field.compare("Year") == 0) { qry << "alb.Title = ?";
album.year = std::stoi(row[i]);
}
}
return album; params[0].buffer_type = MYSQL_TYPE_STRING;
} params[0].buffer = (char*)album.title.c_str();
params[0].length = &titleLength;
std::pair<model::Album, int> database::AlbumRepository::parseRecordWithSongCount(MYSQL_STMT *stmt) params[0].is_null = 0;
{ break;
std::cout << "parsing album record with song count" << std::endl; default:
mysql_stmt_store_result(stmt); break;
auto rowCount = mysql_stmt_num_rows(stmt);
model::Album album;
int songCount = 0;
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);
status = mysql_stmt_fetch(stmt);
album.title = std::move(title);
std::cout << "done parsing album record with song count" << std::endl;
auto albWSC = std::make_pair(album, songCount);
return albWSC;
}
model::Album database::AlbumRepository::parseRecord(MYSQL_STMT *stmt)
{
std::cout << "parsing album record" << std::endl;
mysql_stmt_store_result(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); qry << " LIMIT 1";
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);
album = parseRecord(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
std::cout << "done\n";
return album;
} }
std::cout << "done parsing album record" << std::endl; bool AlbumRepository::doesAlbumExists(const model::Album& album, type::AlbumFilter filter) {
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
return album; MYSQL_BIND params[1];
memset(params, 0, sizeof(params));
std::stringstream qry;
qry << "SELECT * FROM Album WHERE ";
auto titleLength = album.title.size();
switch (filter) {
case type::AlbumFilter::id:
qry << "AlbumId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&album.id;
params[0].length = 0;
params[0].is_null = 0;
break;
case type::AlbumFilter::title:
qry << "Title = ?";
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)album.title.c_str();
params[0].length = &titleLength;
params[0].is_null = 0;
break;
default:
break;
}
qry << " LIMIT 1";
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);
std::cout << "the query has been performed\n";
mysql_stmt_store_result(stmt);
auto rowCount = mysql_stmt_num_rows(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
return (rowCount > 0) ? true : false;
}
void AlbumRepository::saveAlbum(const model::Album& album) {
std::cout << "beginning to insert album record\n";
auto conn = setupMysqlConnection();
MYSQL_STMT *stmt = mysql_stmt_init(conn);
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[3];
memset(params, 0, sizeof(params));
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)album.title.c_str();
auto titleLength = album.title.size();
params[0].length= &titleLength;
params[0].is_null = 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);
mysql_stmt_close(stmt);
mysql_close(conn);
std::cout << "done inserting album record\n";
}
void AlbumRepository::deleteAlbum(const model::Album& album,
type::AlbumFilter filter = type::AlbumFilter::id) {
std::cout << "deleting album record\n";
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
MYSQL_BIND params[1];
std::memset(params, 0, sizeof(params));
qry << "DELETE FROM Album WHERE ";
switch (filter) {
case type::AlbumFilter::id:
qry << "AlbumId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&album.id;
params[0].length = 0;
params[0].is_null = 0;
break;
default:
break;
}
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);
std::cout << "execute delete query\n";
}
std::vector<model::Album> AlbumRepository::parseRecords(MYSQL_STMT* stmt) {
std::cout << "parsing album record\n";
mysql_stmt_store_result(stmt);
std::vector<model::Album> albums;
albums.reserve(mysql_stmt_num_rows(stmt));
const auto valAmt = 3;
unsigned long len[valAmt];
my_bool nullRes[valAmt];
for (auto status = 0; status == 0; status = mysql_stmt_next_result(stmt)) {
if (mysql_stmt_field_count(stmt) > 0) {
model::Album alb;
auto metaBuff = metadataBuffer();
auto bindedValues = valueBind(alb, metaBuff);
status = mysql_stmt_bind_result(stmt, bindedValues.get());
while (true) {
std::cout << "fetching statement result\n";
status = mysql_stmt_fetch(stmt);
if (status == 1 || status == MYSQL_NO_DATA) break;
alb.title = std::get<0>(metaBuff);
alb.artist = std::get<1>(metaBuff);
albums.push_back(std::move(alb));
}
}
std::cout << "fetching next result\n";
}
return albums;
}
std::pair<model::Album, int> AlbumRepository::parseRecordWithSongCount(MYSQL_STMT *stmt) {
std::cout << "parsing album record with song count\n";
mysql_stmt_store_result(stmt);
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\n";
return std::make_pair(album, songCount);
}
auto status = mysql_stmt_bind_result(stmt, val.get());
status = mysql_stmt_fetch(stmt);
album.title = std::get<0>(metaBuff);
album.artist = std::get<1>(metaBuff);
std::cout << "done parsing album record with song count\n";
auto albWSC = std::make_pair(album, songCount);
return albWSC;
}
std::shared_ptr<MYSQL_BIND> AlbumRepository::valueBind(model::Album& album,
std::tuple<char*, char*>& metadata) {
constexpr auto wordLen = 1024;
constexpr auto valueCount = 4;
std::shared_ptr<MYSQL_BIND> 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<MYSQL_BIND> AlbumRepository::valueBindWithSongCount(model::Album& album,
std::tuple<char*, char*>& metadata, int& songCount) {
constexpr auto wordLen = 1024;
constexpr auto valueCount = 5;
std::shared_ptr<MYSQL_BIND> 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<char*, char*> 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\n";
mysql_stmt_store_result(stmt);
auto rows = mysql_stmt_num_rows(stmt);
model::Album album;
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\n";
return album;
}
} }
+315 -369
View File
@@ -6,410 +6,356 @@
#include <string> #include <string>
#include <cstring> #include <cstring>
database::ArtistRepository::ArtistRepository(const model::BinaryPath& binConf) namespace database {
: BaseRepository(binConf) ArtistRepository::ArtistRepository(const model::BinaryPath& binConf) :
{ BaseRepository(binConf) { }
}
std::vector<model::Artist> database::ArtistRepository::retrieveRecords() std::vector<model::Artist> ArtistRepository::retrieveRecords() {
{ auto conn = setupMysqlConnection();
auto conn = setupMysqlConnection(); auto stmt = mysql_stmt_init(conn);
auto stmt = mysql_stmt_init(conn); const std::string query = "SELECT * FROM Artist";
const std::string query = "SELECT * FROM Artist";
mysql_stmt_prepare(stmt, query.c_str(), query.size()); mysql_stmt_prepare(stmt, query.c_str(), query.size());
mysql_stmt_execute(stmt); mysql_stmt_execute(stmt);
auto artists = parseRecords(stmt); auto artists = parseRecords(stmt);
mysql_stmt_close(stmt); mysql_stmt_close(stmt);
mysql_close(conn); mysql_close(conn);
return artists;
}
std::pair<model::Artist, int> database::ArtistRepository::retrieveRecordWithSongCount(model::Artist& artist, type::ArtistFilter filter = type::ArtistFilter::id)
{
std::cout << "retrieving artist record with song count" << std::endl;
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
qry << "SELECT art.*, COUNT(*) AS SongCount FROM Artist art LEFT JOIN ";
qry << "Song sng ON art.ArtistId=sng.ArtistId WHERE ";
MYSQL_BIND params[1];
std::memset(params, 0, sizeof(params));
switch (filter) {
case type::ArtistFilter::id:
qry << "sng.ArtistId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&artist.id;
params[0].length = 0;
params[0].is_null = 0;
break;
default:
break;
}
qry << " GROUP BY art.ArtistId LIMIT 1";
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);
std::cout << "Artist record with song count query ";
std::cout << "has been performed" << std::endl;
auto artWSC = parseRecordWithSongCount(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
return artWSC;
}
model::Artist database::ArtistRepository::retrieveRecord(model::Artist& artist, type::ArtistFilter filter)
{
std::cout << "retrieving artist record" << std::endl;
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
qry << "SELECT art.* FROM Artist art WHERE ";
MYSQL_BIND params[1];
memset(params, 0, sizeof(params));
auto artistLength = artist.artist.size();
switch (filter) {
case type::ArtistFilter::id:
qry << "art.ArtistId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&artist.id;
params[0].length = 0;
params[0].is_null = 0;
break;
case type::ArtistFilter::artist:
qry << "art.Artist = ?";
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)artist.artist.c_str();
params[0].length = &artistLength;
params[0].is_null = 0;
break;
default:
break;
}
qry << " LIMIT 1";
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);
artist = parseRecord(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
std::cout << "retrieved record" << std::endl;
return artist;
}
bool database::ArtistRepository::doesArtistExist(const model::Artist& artist, type::ArtistFilter filter)
{
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
MYSQL_BIND params[1];
memset(params, 0, sizeof(params));
std::stringstream qry;
qry << "SELECT * FROM Artist WHERE ";
auto artistLength = artist.artist.size();
switch (filter) {
case type::ArtistFilter::id:
qry << "ArtistId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&artist.id;
params[0].length = 0;
params[0].is_null = 0;
break;
case type::ArtistFilter::artist:
qry << "Artist = ?";
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)artist.artist.c_str();
params[0].length = &artistLength;
params[0].is_null = 0;
break;
default:
break;
}
qry << " LIMIT 1";
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);
std::cout << "the query has been performed" << std::endl;
mysql_stmt_store_result(stmt);
auto rowCount = mysql_stmt_num_rows(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
return (rowCount > 0) ? true : false;
}
void database::ArtistRepository::saveRecord(const model::Artist& artist)
{
std::cout << "inserting artist record" << std::endl;
auto conn = setupMysqlConnection();
MYSQL_STMT *stmt = mysql_stmt_init(conn);
const std::string query = "INSERT INTO Artist(Artist) VALUES(?)";
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
MYSQL_BIND params[1];
memset(params, 0, sizeof(params));
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)artist.artist.c_str();
auto artistLength = artist.artist.size();
params[0].length = &artistLength;
params[0].is_null = 0;
status = mysql_stmt_bind_param(stmt, params);
status = mysql_stmt_execute(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
std::cout<< "inserted artist record" << std::endl;
}
void database::ArtistRepository::deleteArtist(const model::Artist& artist, type::ArtistFilter filter = type::ArtistFilter::id) {
// TODO: implement this
std::cout << "delete Artist record" << std::endl;
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
qry << "DELETE FROM Artist WHERE ";
MYSQL_BIND params[1];
std::memset(params, 0, sizeof(params));
switch (filter) {
case type::ArtistFilter::id:
qry << "ArtistId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&artist.id;
params[0].length = 0;
params[0].is_null = 0;
break;
default:
break;
}
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);
mysql_stmt_close(stmt);
mysql_close(conn);
std::cout << "deleted artist record" << std::endl;
}
std::vector<model::Artist> database::ArtistRepository::parseRecords(MYSQL_STMT *stmt)
{
mysql_stmt_store_result(stmt);
std::vector<model::Artist> artists;
artists.reserve(mysql_stmt_num_rows(stmt));
if (mysql_stmt_field_count(stmt) == 0) {
std::cout << "field count is 0" << std::endl;
return artists; return artists;
} }
model::Artist art; std::pair<model::Artist, int> ArtistRepository::retrieveRecordWithSongCount(
const auto valAmt = 2; model::Artist& artist, type::ArtistFilter filter = type::ArtistFilter::id) {
unsigned long len[valAmt]; std::cout << "retrieving artist record with song count\n";
my_bool nullRes[valAmt]; std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
auto res = mysql_stmt_result_metadata(stmt); qry << "SELECT art.*, COUNT(*) AS SongCount FROM Artist art LEFT JOIN ";
auto fields = mysql_fetch_fields(res); qry << "Song sng ON art.ArtistId=sng.ArtistId WHERE ";
const auto strLen = 1024;
char artist[strLen]; MYSQL_BIND params[1];
std::memset(params, 0, sizeof(params));
switch (filter) {
case type::ArtistFilter::id:
qry << "sng.ArtistId = ?";
MYSQL_BIND val[valAmt]; params[0].buffer_type = MYSQL_TYPE_LONG;
memset(val, 0, sizeof(val)); params[0].buffer = (char*)&artist.id;
params[0].length = 0;
params[0].is_null = 0;
break;
default:
break;
}
val[0].buffer_type = MYSQL_TYPE_LONG; qry << " GROUP BY art.ArtistId LIMIT 1";
val[0].buffer = (char*)&art.id;
val[0].length = &len[0];
val[0].is_null = &nullRes[0];
val[1].buffer_type = MYSQL_TYPE_STRING; const auto query = qry.str();
val[1].buffer = (char*)artist; auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
val[1].buffer_length = strLen; status = mysql_stmt_bind_param(stmt, params);
val[1].length = &len[1]; status = mysql_stmt_execute(stmt);
val[1].is_null = &nullRes[1];
for (auto status = mysql_stmt_bind_result(stmt, val); status == 0; ) { std::cout << "Artist record with song count query ";
std::cout << "fetching statement result" << std::endl; std::cout << "has been performed\n";
auto artWSC = parseRecordWithSongCount(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
return artWSC;
}
model::Artist ArtistRepository::retrieveRecord(model::Artist& artist,
type::ArtistFilter filter) {
std::cout << "retrieving artist record\n";
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
qry << "SELECT art.* FROM Artist art WHERE ";
MYSQL_BIND params[1];
memset(params, 0, sizeof(params));
auto artistLength = artist.artist.size();
switch (filter) {
case type::ArtistFilter::id:
qry << "art.ArtistId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&artist.id;
params[0].length = 0;
params[0].is_null = 0;
break;
case type::ArtistFilter::artist:
qry << "art.Artist = ?";
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)artist.artist.c_str();
params[0].length = &artistLength;
params[0].is_null = 0;
break;
default:
break;
}
qry << " LIMIT 1";
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);
artist = parseRecord(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
std::cout << "retrieved record\n";
return artist;
}
bool ArtistRepository::doesArtistExist(const model::Artist& artist, type::ArtistFilter filter) {
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
MYSQL_BIND params[1];
memset(params, 0, sizeof(params));
std::stringstream qry;
qry << "SELECT * FROM Artist WHERE ";
auto artistLength = artist.artist.size();
switch (filter) {
case type::ArtistFilter::id:
qry << "ArtistId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&artist.id;
params[0].length = 0;
params[0].is_null = 0;
break;
case type::ArtistFilter::artist:
qry << "Artist = ?";
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)artist.artist.c_str();
params[0].length = &artistLength;
params[0].is_null = 0;
break;
default:
break;
}
qry << " LIMIT 1";
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);
std::cout << "the query has been performed\n";
mysql_stmt_store_result(stmt);
auto rowCount = mysql_stmt_num_rows(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
return (rowCount > 0) ? true : false;
}
void ArtistRepository::saveRecord(const model::Artist& artist) {
std::cout << "inserting artist record\n";
auto conn = setupMysqlConnection();
MYSQL_STMT *stmt = mysql_stmt_init(conn);
const std::string query = "INSERT INTO Artist(Artist) VALUES(?)";
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
MYSQL_BIND params[1];
memset(params, 0, sizeof(params));
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)artist.artist.c_str();
auto artistLength = artist.artist.size();
params[0].length = &artistLength;
params[0].is_null = 0;
status = mysql_stmt_bind_param(stmt, params);
status = mysql_stmt_execute(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
std::cout<< "inserted artist record\n";
}
void ArtistRepository::deleteArtist(const model::Artist& artist,
type::ArtistFilter filter = type::ArtistFilter::id) {
std::cout << "delete Artist record\n";
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
qry << "DELETE FROM Artist WHERE ";
MYSQL_BIND params[1];
std::memset(params, 0, sizeof(params));
switch (filter) {
case type::ArtistFilter::id:
qry << "ArtistId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&artist.id;
params[0].length = 0;
params[0].is_null = 0;
break;
default:
break;
}
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);
mysql_stmt_close(stmt);
mysql_close(conn);
std::cout << "deleted artist record\n";
}
std::vector<model::Artist> ArtistRepository::parseRecords(MYSQL_STMT *stmt) {
mysql_stmt_store_result(stmt);
std::vector<model::Artist> artists;
artists.reserve(mysql_stmt_num_rows(stmt));
constexpr auto valAmt = 2;
unsigned long len[valAmt];
my_bool nullRes[valAmt];
for (auto status = 0; status == 0; status = mysql_stmt_next_result(stmt)) {
if (mysql_stmt_field_count(stmt) > 0) {
model::Artist art;
auto metaBuff = metadataBuffer();
auto bindedValues = valueBind(art, metaBuff);
status = mysql_stmt_bind_result(stmt, bindedValues.get());
std::cout << "fetching statement result\n";
status = mysql_stmt_fetch(stmt);
if (status == 1 || status == MYSQL_NO_DATA) break;
art.artist = std::get<0>(metaBuff);
artists.push_back(art);
}
std::cout << "fetching next result\n";
}
return artists;
}
std::pair<model::Artist, int> ArtistRepository::parseRecordWithSongCount(MYSQL_STMT *stmt) {
std::cout << "parsing artist record with song count\n";
mysql_stmt_store_result(stmt);
const auto rowCount = mysql_stmt_num_rows(stmt);
model::Artist artist;
auto metaBuff = metadataBuffer();
auto songCount = 0;
auto val = valueBindWithSongCount(artist, metaBuff, songCount);
if (rowCount == 0) {
std::cout << "no results\n";
return std::make_pair(artist, songCount);
}
auto status = mysql_stmt_bind_result(stmt, val.get());
status = mysql_stmt_fetch(stmt); status = mysql_stmt_fetch(stmt);
if (status == 0) { artist.artist = std::get<0>(metaBuff);
art.artist = artist;
artists.push_back(std::move(art)); std::cout << "done parsing album record with song count\n";
}
return std::make_pair(artist, songCount);
} }
return artists;
}
std::shared_ptr<MYSQL_BIND> ArtistRepository::valueBind(model::Artist& artist,
std::tuple<char*>& metadata) {
constexpr auto valueCount = 2;
constexpr auto wordLen = 1024;
std::shared_ptr<MYSQL_BIND> values((MYSQL_BIND*)
std::calloc(valueCount, sizeof(MYSQL_BIND)));
model::Artist database::ArtistRepository::parseRecord(MYSQL_RES* results) values.get()[0].buffer_type = MYSQL_TYPE_LONG;
{ values.get()[0].buffer = (char*)&artist.id;
std::cout << "parsing artist record" << std::endl;
model::Artist artist;
auto fieldNum = mysql_num_fields(results); values.get()[1].buffer_type = MYSQL_TYPE_STRING;
auto row = mysql_fetch_row(results); values.get()[1].buffer = (char*)std::get<0>(metadata);
values.get()[1].buffer_length = wordLen;
for (auto i = 0; i != fieldNum; ++i) { return values;
const std::string field(mysql_fetch_field(results)->name);
if (field.compare("ArtistId") == 0) {
artist.id = std::stoi(row[i]);
}
if (field.compare("Artist") == 0) {
artist.artist = row[i];
}
} }
std::cout << "parsed artist record" << std::endl; std::shared_ptr<MYSQL_BIND> ArtistRepository::valueBindWithSongCount(model::Artist& artist,
std::tuple<char*>& metadata, int& songCount) {
constexpr auto valueCount = 3;
constexpr auto wordLen = 1024;
std::shared_ptr<MYSQL_BIND> values((MYSQL_BIND*)
std::calloc(valueCount, sizeof(MYSQL_BIND)));
return artist; values.get()[0].buffer_type = MYSQL_TYPE_LONG;
} values.get()[0].buffer = reinterpret_cast<char*>(&artist.id);
std::pair<model::Artist, int> database::ArtistRepository::parseRecordWithSongCount(MYSQL_STMT *stmt) values.get()[1].buffer_type = MYSQL_TYPE_STRING;
{ values.get()[1].buffer = reinterpret_cast<char*>(std::get<0>(metadata));
std::cout << "parsing artist record with song count" << std::endl; values.get()[1].buffer_length = wordLen;
mysql_stmt_store_result(stmt);
const auto strLen = 1024; values.get()[2].buffer_type = MYSQL_TYPE_LONG;
const auto valAmt = 3; values.get()[2].buffer = reinterpret_cast<char*>(&songCount);
unsigned long len[valAmt];
my_bool nullRes[valAmt];
model::Artist artist;
int songCount = 0;
if (mysql_stmt_num_rows(stmt) == 0) { return values;
std::cout << "no results" << std::endl;
return std::make_pair(artist, songCount);
} }
MYSQL_BIND params[valAmt];
std::memset(params, 0, sizeof(params));
char art[strLen]; std::tuple<char*> ArtistRepository::metadataBuffer() {
constexpr auto wordLen = 1024;
char artist[wordLen];
params[0].buffer_type = MYSQL_TYPE_LONG; return std::make_tuple(artist);
params[0].buffer = (char*)&artist.id;
params[0].length = &len[0];
params[0].is_null = &nullRes[0];
params[1].buffer_type = MYSQL_TYPE_STRING;
params[1].buffer = (char*)art;
params[1].buffer_length = strLen;
params[1].length = &len[1];
params[1].is_null = &nullRes[1];
params[2].buffer_type = MYSQL_TYPE_LONG;
params[2].buffer = (char*)&songCount;
params[2].length = &len[2];
params[2].is_null = &nullRes[2];
mysql_stmt_bind_result(stmt, params);
mysql_stmt_fetch(stmt);
artist.artist = std::move(art);
std::cout << "parsed artist record with song count" << std::endl;
return std::make_pair(artist, songCount);
}
model::Artist database::ArtistRepository::parseRecord(MYSQL_STMT *stmt)
{
std::cout << "parsing artist record" << std::endl;
mysql_stmt_store_result(stmt);
model::Artist art;
auto status = 0;
auto time = 0;
auto valAmt = 2;
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 artist[strLen];
val[0].buffer_type = MYSQL_TYPE_LONG;
val[0].buffer = (char*)&art.id;
val[0].length = &len[0];
val[0].is_null = &nullRes[0];
val[1].buffer_type = MYSQL_TYPE_STRING;
val[1].buffer = (char*)&artist;
val[1].buffer_length = strLen;
val[1].length = &len[1];
val[1].is_null = &nullRes[1];
status = mysql_stmt_bind_result(stmt, val);
mysql_stmt_store_result(stmt);
status = mysql_stmt_fetch(stmt);
art.artist = artist;
}
status = mysql_stmt_next_result(stmt);
} }
std::cout << "done parsing artist record" << std::endl;
return art; model::Artist ArtistRepository::parseRecord(MYSQL_STMT *stmt) {
std::cout << "parsing artist record\n";
mysql_stmt_store_result(stmt);
model::Artist art;
auto metaBuff = metadataBuffer();
auto bindedValues = valueBind(art, metaBuff);
auto status = mysql_stmt_bind_result(stmt, bindedValues.get());
status = mysql_stmt_fetch(stmt);
art.artist = std::get<0>(metaBuff);
std::cout << "done parsing artist record\n";
return art;
}
} }
+64 -72
View File
@@ -6,91 +6,83 @@
#include "manager/DirectoryManager.h" #include "manager/DirectoryManager.h"
database::BaseRepository::BaseRepository() namespace database {
{ } BaseRepository::BaseRepository(const std::string& path) : path(path) {
intitalizeDetails();
database::BaseRepository::BaseRepository(const std::string& path) : path(path)
{
intitalizeDetails();
}
database::BaseRepository::BaseRepository(const model::BinaryPath& bConf)
{
initializeDetails(bConf);
}
bool database::BaseRepository::testConnection()
{
auto conn = mysql_init(nullptr);
if (!mysql_real_connect(conn, details.server.c_str(), details.username.c_str(),
details.password.c_str(), details.database.c_str(), 0,
nullptr, 0)) {
std::cout << "failed to connect to the database" << std::endl;
return false;
} }
mysql_close(conn); BaseRepository::BaseRepository(const model::BinaryPath& bConf) {
initializeDetails(bConf);
return true;
}
MYSQL* database::BaseRepository::setupMysqlConnection()
{
MYSQL *conn = mysql_init(nullptr);
if (!mysql_real_connect(conn, details.server.c_str(), details.username.c_str(),
details.password.c_str(), details.database.c_str(), 0, nullptr, 0)) {
std::cout << "connection error" << std::endl;
} }
return conn;
}
MYSQL* database::BaseRepository::setupMysqlConnection(model::DatabaseConnection details) bool BaseRepository::testConnection() {
{ auto conn = mysql_init(nullptr);
MYSQL *connection = mysql_init(NULL); if (!mysql_real_connect(conn, details.server.c_str(), details.username.c_str(),
details.password.c_str(), details.database.c_str(), 0, nullptr, 0)) {
std::cout << "failed to connect to the database\n";
return false;
}
// connect to the database with the details attached. mysql_close(conn);
if (!mysql_real_connect(connection,details.server.c_str(), details.username.c_str(), details.password.c_str(), details.database.c_str(), 0, NULL, 0)) {
printf("Conection error : %s\n", mysql_error(connection)); return true;
exit(1);
} }
return connection;
}
MYSQL_RES* database::BaseRepository::performMysqlQuery(MYSQL *conn, const std::string& query) MYSQL* BaseRepository::setupMysqlConnection() {
{ MYSQL *conn = mysql_init(nullptr);
// send the query to the database
if (mysql_query(conn, query.c_str()))
{
printf("MySQL query error : %s\n", mysql_error(conn));
exit(1);
}
return mysql_use_result(conn); if (!mysql_real_connect(conn, details.server.c_str(), details.username.c_str(),
} details.password.c_str(), details.database.c_str(), 0, nullptr, 0)) {
std::cout << "connection error\n";
}
return conn;
}
MYSQL* BaseRepository::setupMysqlConnection(model::DatabaseConnection details) {
MYSQL *connection = mysql_init(NULL);
// connect to the database with the details attached.
if (!mysql_real_connect(connection,details.server.c_str(), details.username.c_str(),
details.password.c_str(), details.database.c_str(), 0, NULL, 0)) {
std::cout << "Connection error: " << mysql_error(connection) << "\n";
std::exit(-1);
}
return connection;
}
void database::BaseRepository::intitalizeDetails() MYSQL_RES* BaseRepository::performMysqlQuery(MYSQL *conn, const std::string& query) {
{ // send the query to the database
auto databaseConfig = manager::DirectoryManager::databaseConfigContent(path); if (mysql_query(conn, query.c_str())) {
printf("MySQL query error : %s\n", mysql_error(conn));
std::cout << "MySQL query error : " << mysql_error(conn) << "\n";
std::exit(1);
}
return mysql_use_result(conn);
}
void BaseRepository::intitalizeDetails() {
auto databaseConfig = manager::DirectoryManager::databaseConfigContent(path);
details.database = databaseConfig["database"].get<std::string>(); details.database = databaseConfig["database"].get<std::string>();
details.password = databaseConfig["password"].get<std::string>(); details.password = databaseConfig["password"].get<std::string>();
details.server = databaseConfig["server"].get<std::string>(); details.server = databaseConfig["server"].get<std::string>();
details.username = databaseConfig["username"].get<std::string>(); details.username = databaseConfig["username"].get<std::string>();
} }
void database::BaseRepository::initializeDetails(const model::BinaryPath& bConf) void BaseRepository::initializeDetails(const model::BinaryPath& bConf)
{ {
auto databaseConfig = manager::DirectoryManager::databaseConfigContent(bConf); auto databaseConfig = manager::DirectoryManager::databaseConfigContent(bConf);
details.database = databaseConfig["database"].get<std::string>(); details.database = databaseConfig["database"].get<std::string>();
details.server = databaseConfig["server"].get<std::string>(); details.server = databaseConfig["server"].get<std::string>();
details.username = databaseConfig["username"].get<std::string>(); details.username = databaseConfig["username"].get<std::string>();
details.password = databaseConfig["password"].get<std::string>(); details.password = databaseConfig["password"].get<std::string>();
std::cout << "retrieved database details" << std::endl; std::cout << "retrieved database details\n";
}
} }
+254 -302
View File
@@ -6,369 +6,321 @@
#include <string> #include <string>
#include <cstring> #include <cstring>
database::CoverArtRepository::CoverArtRepository(const std::string& path) : BaseRepository(path) namespace database {
{ } CoverArtRepository::CoverArtRepository(const model::BinaryPath& bConf) :
BaseRepository(bConf) { }
database::CoverArtRepository::CoverArtRepository(const model::BinaryPath& bConf) : BaseRepository(bConf)
{ }
std::vector<model::Cover> database::CoverArtRepository::retrieveRecords() std::vector<model::Cover> CoverArtRepository::retrieveRecords() {
{ auto conn = setupMysqlConnection();
auto conn = setupMysqlConnection(); auto stmt = mysql_stmt_init(conn);
auto stmt = mysql_stmt_init(conn); const std::string query = "SELECT * FROM CoverArt";
const std::string query = "SELECT * FROM CoverArt";
mysql_stmt_prepare(stmt, query.c_str(), query.size()); mysql_stmt_prepare(stmt, query.c_str(), query.size());
mysql_stmt_execute(stmt); mysql_stmt_execute(stmt);
auto coverArts = parseRecords(stmt); auto coverArts = parseRecords(stmt);
mysql_stmt_close(stmt); mysql_stmt_close(stmt);
mysql_close(conn); mysql_close(conn);
return coverArts; return coverArts;
}
model::Cover database::CoverArtRepository::retrieveRecord(model::Cover& cov, type::CoverFilter filter = type::CoverFilter::id)
{
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
qry << "SELECT * FROM CoverArt WHERE ";
MYSQL_BIND params[1];
memset(params, 0, sizeof(params));
auto songTitleLength = cov.songTitle.size();
switch (filter) {
case type::CoverFilter::id:
qry << "CoverArtId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&cov.id;
params[0].length = 0;
params[0].is_null = 0;
break;
case type::CoverFilter::songTitle:
qry << "SongTitle = ?";
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)cov.songTitle.c_str();
params[0].length = &songTitleLength;
params[0].is_null = 0;
break;
default:
break;
} }
qry << " LIMIT 1"; model::Cover CoverArtRepository::retrieveRecord(model::Cover& cov,
type::CoverFilter filter = type::CoverFilter::id) {
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
const std::string query = qry.str(); qry << "SELECT * FROM CoverArt WHERE ";
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
status = mysql_stmt_bind_param(stmt, params);
status = mysql_stmt_execute(stmt);
auto covDb = parseRecord(stmt); MYSQL_BIND params[1];
memset(params, 0, sizeof(params));
mysql_stmt_close(stmt); auto songTitleLength = cov.songTitle.size();
mysql_close(conn); switch (filter) {
std::cout << "retrieved cover art record" << std::endl; case type::CoverFilter::id:
qry << "CoverArtId = ?";
return covDb; params[0].buffer_type = MYSQL_TYPE_LONG;
} params[0].buffer = (char*)&cov.id;
params[0].length = 0;
params[0].is_null = 0;
break;
case type::CoverFilter::songTitle:
qry << "SongTitle = ?";
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)cov.songTitle.c_str();
params[0].length = &songTitleLength;
params[0].is_null = 0;
break;
default:
break;
}
bool database::CoverArtRepository::doesCoverArtExist(const model::Cover& cover, type::CoverFilter filter) qry << " LIMIT 1";
{
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
MYSQL_BIND params[1]; const std::string query = qry.str();
memset(params, 0, sizeof(params)); auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
status = mysql_stmt_bind_param(stmt, params);
status = mysql_stmt_execute(stmt);
std::stringstream qry; auto covDb = parseRecord(stmt);
qry << "SELECT * FROM CoverArt WHERE ";
auto titleLength = cover.songTitle.size(); mysql_stmt_close(stmt);
switch (filter) { mysql_close(conn);
case type::CoverFilter::id: std::cout << "retrieved cover art record\n";
qry << "CoverArtId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG; return covDb;
params[0].buffer = (char*)&cover.id;
params[0].length = 0;
params[0].is_null = 0;
break;
case type::CoverFilter::songTitle:
qry << "SongTitle = ?";
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)cover.songTitle.c_str();
params[0].length = &titleLength;
params[0].is_null = 0;
break;
default:
break;
} }
qry << " LIMIT 1";
const auto query = qry.str(); bool CoverArtRepository::doesCoverArtExist(const model::Cover& cover,
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size()); type::CoverFilter filter) {
status = mysql_stmt_bind_param(stmt, params); auto conn = setupMysqlConnection();
status = mysql_stmt_execute(stmt); auto stmt = mysql_stmt_init(conn);
std::cout << "the query has been performed" << std::endl; MYSQL_BIND params[1];
memset(params, 0, sizeof(params));
mysql_stmt_store_result(stmt); std::stringstream qry;
auto rowCount = mysql_stmt_num_rows(stmt); qry << "SELECT * FROM CoverArt WHERE ";
mysql_stmt_close(stmt); auto titleLength = cover.songTitle.size();
mysql_close(conn); switch (filter) {
case type::CoverFilter::id:
qry << "CoverArtId = ?";
return (rowCount > 0) ? true : false; params[0].buffer_type = MYSQL_TYPE_LONG;
} params[0].buffer = (char*)&cover.id;
params[0].length = 0;
params[0].is_null = 0;
break;
case type::CoverFilter::songTitle:
qry << "SongTitle = ?";
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)cover.songTitle.c_str();
params[0].length = &titleLength;
params[0].is_null = 0;
break;
default:
break;
}
qry << " LIMIT 1";
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);
std::cout << "the query has been performed\n";
mysql_stmt_store_result(stmt);
auto rowCount = mysql_stmt_num_rows(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
return (rowCount > 0) ? true : false;
}
// TODO: change to prepared statement void CoverArtRepository::deleteRecord(const model::Cover& cov,
void database::CoverArtRepository::deleteRecord(const model::Cover& cov) type::CoverFilter filter) {
{ auto conn = setupMysqlConnection();
auto conn = setupMysqlConnection(); auto stmt = mysql_stmt_init(conn);
const std::string query("DELETE FROM CoverArt WHERE CoverArtId = " + std::to_string(cov.id)); std::cout << "delete CoverArt record\n";
std::stringstream qry;
qry << "DELETE FROM CoverArt WHERE ";
auto result = performMysqlQuery(conn, query); MYSQL_BIND params[1];
std::memset(params, 0, sizeof(params));
mysql_close(conn); switch (filter) {
} case type::CoverFilter::id:
qry << "CoverArtId = ?";
void database::CoverArtRepository::saveRecord(const model::Cover& cov) params[0].buffer_type = MYSQL_TYPE_LONG;
{ params[0].buffer = (char*)&cov.id;
std::cout << "saving cover art record"; params[0].length = 0;
auto conn = setupMysqlConnection(); params[0].is_null = 0;
auto stmt = mysql_stmt_init(conn); break;
default:
break;
}
MYSQL_BIND params[2]; const auto query = qry.str();
memset(params, 0, sizeof(params));
my_bool isNull;
const std::string query = "INSERT INTO CoverArt(SongTitle, ImagePath) VALUES(?, ?)"; auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
status = mysql_stmt_bind_param(stmt, params);
status = mysql_stmt_execute(stmt);
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size()); mysql_stmt_close(stmt);
mysql_close(conn);
params[0].buffer_type = MYSQL_TYPE_STRING; std::cout << "deleted CoverArt record\n";
params[0].buffer = (char*)cov.songTitle.c_str(); }
auto songTitleLength = cov.songTitle.size();
params[0].length = &songTitleLength;
params[0].is_null = 0;
params[1].buffer_type = MYSQL_TYPE_STRING; void CoverArtRepository::saveRecord(const model::Cover& cov) {
params[1].buffer = (char*)cov.imagePath.c_str(); std::cout << "saving cover art record";
auto imagePathLength = cov.imagePath.size(); auto conn = setupMysqlConnection();
params[1].length = &imagePathLength; auto stmt = mysql_stmt_init(conn);
params[1].is_null = 0;
status = mysql_stmt_bind_param(stmt, params); MYSQL_BIND params[2];
status = mysql_stmt_execute(stmt); memset(params, 0, sizeof(params));
my_bool isNull;
mysql_stmt_close(stmt); const std::string query = "INSERT INTO CoverArt(SongTitle, ImagePath) VALUES(?, ?)";
mysql_close(conn);
std::cout << "saved cover art record" << std::endl; auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
}
void database::CoverArtRepository::updateRecord(const model::Cover& cover) params[0].buffer_type = MYSQL_TYPE_STRING;
{ params[0].buffer = (char*)cov.songTitle.c_str();
std::stringstream qry; auto songTitleLength = cov.songTitle.size();
auto conn = setupMysqlConnection(); params[0].length = &songTitleLength;
auto stmt = mysql_stmt_init(conn); params[0].is_null = 0;
qry << "UPDATE CoverArt SET "; params[1].buffer_type = MYSQL_TYPE_STRING;
qry << "SongTitle = ?, "; params[1].buffer = (char*)cov.imagePath.c_str();
qry << "ImagePath = ? "; auto imagePathLength = cov.imagePath.size();
qry << "WHERE CoverArtId = ?"; params[1].length = &imagePathLength;
params[1].is_null = 0;
MYSQL_BIND params[3]; status = mysql_stmt_bind_param(stmt, params);
memset(params, 0, sizeof(params)); status = mysql_stmt_execute(stmt);
auto songTitleLength = cover.songTitle.size(); mysql_stmt_close(stmt);
auto imagePathLength = cover.imagePath.size(); mysql_close(conn);
params[0].buffer_type = MYSQL_TYPE_STRING; std::cout << "saved cover art record\n";
params[0].buffer = (char*)cover.songTitle.c_str(); }
params[0].length = &songTitleLength;
params[0].is_null = 0;
params[1].buffer_type = MYSQL_TYPE_STRING; void CoverArtRepository::updateRecord(const model::Cover& cover) {
params[1].buffer = (char*)cover.imagePath.c_str(); std::stringstream qry;
params[1].length = &imagePathLength; auto conn = setupMysqlConnection();
params[1].is_null = 0; auto stmt = mysql_stmt_init(conn);
params[2].buffer_type = MYSQL_TYPE_LONG; qry << "UPDATE CoverArt SET ";
params[2].buffer = (char*)&cover.id; qry << "SongTitle = ?, ";
params[2].length = 0; qry << "ImagePath = ? ";
params[2].is_null = 0; qry << "WHERE CoverArtId = ?";
const std::string query = qry.str(); MYSQL_BIND params[3];
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size()); memset(params, 0, sizeof(params));
status = mysql_stmt_bind_param(stmt, params);
status = mysql_stmt_execute(stmt);
mysql_stmt_close(stmt); auto songTitleLength = cover.songTitle.size();
mysql_close(conn); auto imagePathLength = cover.imagePath.size();
std::cout << "updated cover art record" << std::endl; 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\n";
}
std::vector<model::Cover> database::CoverArtRepository::parseRecords(MYSQL_STMT *stmt) std::vector<model::Cover> CoverArtRepository::parseRecords(MYSQL_STMT *stmt) {
{ mysql_stmt_store_result(stmt);
mysql_stmt_store_result(stmt); const auto rowCount = mysql_stmt_num_rows(stmt);
auto rowCount = mysql_stmt_num_rows(stmt); std::cout << "number of results " << rowCount << "\n";
std::cout << "number of results " << rowCount << std::endl;
std::vector<model::Cover> coverArts; std::vector<model::Cover> coverArts;
coverArts.reserve(rowCount); coverArts.reserve(rowCount);
const auto valAmt = 3; constexpr auto valAmt = 3;
unsigned long len[valAmt];
my_bool nullRes[valAmt];
for (auto status = 0; status == 0; status = mysql_stmt_next_result(stmt)) { for (auto status = 0; status == 0; status = mysql_stmt_next_result(stmt)) {
if (mysql_stmt_field_count(stmt) > 0) { if (mysql_stmt_field_count(stmt) > 0) {
model::Cover coverArt; model::Cover cover;
auto res = mysql_stmt_result_metadata(stmt); auto metaBuff = metadataBuffer();
auto fields = mysql_fetch_fields(res); auto bindedValues = valueBind(cover, metaBuff);
auto strLen = 1024; status = mysql_stmt_bind_result(stmt, bindedValues.get());
MYSQL_BIND val[valAmt]; std::cout << "fetching statement result\n";
memset(val, 0, sizeof(val));
char songTitle[strLen];
char imagePath[strLen];
val[0].buffer_type = MYSQL_TYPE_LONG;
val[0].buffer = (char*)&coverArt.id;
val[0].length = &len[0];
val[0].is_null = &nullRes[0];
val[1].buffer_type = MYSQL_TYPE_STRING;
val[1].buffer = (char*)songTitle;
val[1].buffer_length = strLen;
val[1].length = &len[1];
val[1].is_null = &nullRes[1];
val[2].buffer_type = MYSQL_TYPE_STRING;
val[2].buffer = (char*)imagePath;
val[2].buffer_length = strLen;
val[2].length = &len[2];
val[2].is_null = &nullRes[2];
status = mysql_stmt_bind_result(stmt, val);
while (true) {
std::cout << "fetching statement result" << std::endl;
status = mysql_stmt_fetch(stmt); status = mysql_stmt_fetch(stmt);
if (status == 1 || status == MYSQL_NO_DATA) { if (status == 1 || status == MYSQL_NO_DATA) break;
break;
}
coverArt.songTitle = songTitle; cover.songTitle = std::get<0>(metaBuff);
coverArt.imagePath = imagePath; cover.imagePath = std::get<1>(metaBuff);
coverArts.push_back(cover);
coverArts.push_back(std::move(coverArt));
} }
} std::cout << "fetching next result\n";
std::cout << "fetching next result" << std::endl;
}
return coverArts;
}
model::Cover database::CoverArtRepository::parseRecord(MYSQL_RES *results)
{
std::cout << "parsing record" << std::endl;
model::Cover cov;
auto fieldNum = mysql_num_fields(results);
MYSQL_ROW row = mysql_fetch_row(results);
for (auto i = 0; i != fieldNum; ++i) {
switch (i) {
case 0:
cov.id = std::stoi(row[i]);
break;
case 1:
cov.songTitle = row[i];
break;
case 2:
cov.imagePath = row[i];
break;
}
}
std::cout << "done parsing record" << std::endl;
return cov;
}
model::Cover database::CoverArtRepository::parseRecord(MYSQL_STMT *stmt)
{
std::cout << "parsing cover art record" << std::endl;
mysql_stmt_store_result(stmt);
model::Cover cover;
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 songTitle[strLen];
char imagePath[strLen];
val[0].buffer_type = MYSQL_TYPE_LONG;
val[0].buffer = (char*)&cover.id;
val[0].length = &len[0];
val[0].is_null = &nullRes[0];
val[1].buffer_type = MYSQL_TYPE_STRING;
val[1].buffer = (char*)&songTitle;
val[1].buffer_length = strLen;
val[1].length = &len[1];
val[1].is_null = &nullRes[1];
val[2].buffer_type = MYSQL_TYPE_STRING;
val[2].buffer = (char*)&imagePath;
val[2].buffer_length = strLen;
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);
cover.songTitle = songTitle;
cover.imagePath = imagePath;
} }
status = mysql_stmt_next_result(stmt); return coverArts;
} }
std::cout << "done parsing cover art record" << std::endl;
return cover; std::shared_ptr<MYSQL_BIND> CoverArtRepository::valueBind(model::Cover& cover,
std::tuple<char*, char*>& metadata) {
constexpr auto valueCount = 3;
constexpr auto wordLen = 1024;
std::shared_ptr<MYSQL_BIND> values((MYSQL_BIND*)
std::calloc(valueCount, sizeof(MYSQL_BIND)));
values.get()[0].buffer_type = MYSQL_TYPE_LONG;
values.get()[0].buffer = reinterpret_cast<char*>(&cover.id);
values.get()[1].buffer_type = MYSQL_TYPE_STRING;
values.get()[1].buffer = reinterpret_cast<char*>(std::get<0>(metadata));
values.get()[1].buffer_length = wordLen;
values.get()[2].buffer_type = MYSQL_TYPE_STRING;
values.get()[2].buffer = reinterpret_cast<char*>(std::get<1>(metadata));
values.get()[2].buffer_length = wordLen;
return values;
}
std::tuple<char*, char*> CoverArtRepository::metadataBuffer() {
constexpr auto wordLen = 1024;
char songTitle[wordLen];
char imagePath[wordLen];
return std::make_pair(songTitle, imagePath);
}
model::Cover CoverArtRepository::parseRecord(MYSQL_STMT *stmt) {
std::cout << "parsing cover art record\n";
mysql_stmt_store_result(stmt);
model::Cover cover;
auto metaBuff = metadataBuffer();
auto bindedValues = valueBind(cover, metaBuff);
auto status = mysql_stmt_bind_result(stmt, bindedValues.get());
status = mysql_stmt_fetch(stmt);
cover.songTitle = std::get<0>(metaBuff);
cover.imagePath = std::get<1>(metaBuff);
std::cout << "done parsing cover art record\n";
return cover;
}
} }
+319 -323
View File
@@ -5,355 +5,351 @@
#include <sstream> #include <sstream>
#include <cstring> #include <cstring>
database::GenreRepository::GenreRepository(const model::BinaryPath& bConf) namespace database {
: BaseRepository(bConf) GenreRepository::GenreRepository(const model::BinaryPath& bConf) : BaseRepository(bConf) { }
{ }
std::vector<model::Genre> database::GenreRepository::retrieveRecords() std::vector<model::Genre> GenreRepository::retrieveRecords() {
{ auto conn = setupMysqlConnection();
auto conn = setupMysqlConnection(); auto stmt = mysql_stmt_init(conn);
auto stmt = mysql_stmt_init(conn); const std::string query = "SELECT * FROM Genre";
const std::string query = "SELECT * FROM Genre";
mysql_stmt_prepare(stmt, query.c_str(), query.size()); mysql_stmt_prepare(stmt, query.c_str(), query.size());
mysql_stmt_execute(stmt); mysql_stmt_execute(stmt);
auto genres = parseRecords(stmt); auto genres = parseRecords(stmt);
mysql_stmt_close(stmt); mysql_stmt_close(stmt);
mysql_close(conn); mysql_close(conn);
return genres; return genres;
}
std::pair<model::Genre, int> database::GenreRepository::retrieveRecordWithSongCount(model::Genre& genre, type::GenreFilter filter = type::GenreFilter::id)
{
std::cout << "retrieving genre record with song count" << std::endl;
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
qry << "SELECT gnr.*, COUNT(*) AS SongCount FROM Genre gnr LEFT JOIN ";
qry << "Song sng ON gnr.GenreId=sng.GenreId WHERE ";
MYSQL_BIND params[1];
std::memset(params, 0, sizeof(params));
switch (filter) {
case type::GenreFilter::id:
qry << "sng.GenreId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&genre.id;
params[0].length = 0;
params[0].is_null = 0;
break;
default:
break;
} }
qry << " GROUP BY gnr.GenreId LIMIT 1"; std::pair<model::Genre, int> GenreRepository::retrieveRecordWithSongCount(
model::Genre& genre, type::GenreFilter filter = type::GenreFilter::id) {
std::cout << "retrieving genre record with song count\n";
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
const auto query = qry.str(); qry << "SELECT gnr.*, COUNT(*) AS SongCount FROM Genre gnr LEFT JOIN ";
qry << "Song sng ON gnr.GenreId=sng.GenreId WHERE ";
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size()); MYSQL_BIND params[1];
status = mysql_stmt_bind_param(stmt, params); std::memset(params, 0, sizeof(params));
status = mysql_stmt_execute(stmt);
auto gnrWSC = parseRecordWithSongCount(stmt); switch (filter) {
case type::GenreFilter::id:
qry << "sng.GenreId = ?";
mysql_stmt_close(stmt); params[0].buffer_type = MYSQL_TYPE_LONG;
mysql_close(conn); params[0].buffer = (char*)&genre.id;
params[0].length = 0;
params[0].is_null = 0;
break;
default:
break;
}
std::cout << "retrieved genre record with song count" << std::endl; qry << " GROUP BY gnr.GenreId LIMIT 1";
return gnrWSC; const auto query = qry.str();
}
model::Genre database::GenreRepository::retrieveRecord(model::Genre& genre, type::GenreFilter filter) auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
{ status = mysql_stmt_bind_param(stmt, params);
// TODO: change to prepared statement status = mysql_stmt_execute(stmt);
auto gnrWSC = parseRecordWithSongCount(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
std::cout << "retrieved genre record with song count\n";
return gnrWSC;
}
model::Genre GenreRepository::retrieveRecord(model::Genre& genre, type::GenreFilter filter) {
std::cout << "retrieving genre record\n";
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
qry << "SELECT gnr.* FROM Genre gnr WHERE ";
MYSQL_BIND params[1];
std::memset(params, 0, sizeof(params));
auto categoryLength = genre.category.size();
switch (filter) {
case type::GenreFilter::id:
qry << "gnr.GenreId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = reinterpret_cast<char*>(&genre.id);
params[0].length = 0;
params[0].is_null = 0;
break;
case type::GenreFilter::category:
qry << "gnr.Category = ?";
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = const_cast<char*>(genre.category.c_str());
params[0].length = &categoryLength;
params[0].is_null = 0;
break;
default:
break;
}
qry << " ORDER BY GenreId DESC LIMIT 1";
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);
genre = parseRecord(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
std::cout << "retrieved record\n";
return genre;
}
bool GenreRepository::doesGenreExist(const model::Genre& genre, type::GenreFilter filter) {
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
std::stringstream qry;
qry << "SELECT * FROM Genre WHERE ";
MYSQL_BIND params[1];
memset(params, 0, sizeof(params));
auto categoryLength = genre.category.size();
switch (filter) {
case type::GenreFilter::id:
qry << "GenreId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&genre.id;
params[0].length = 0;
params[0].is_null = 0;
break;
case type::GenreFilter::category:
qry << "Category = ?";
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)genre.category.c_str();
params[0].length = &categoryLength;
params[0].is_null = 0;
break;
default:
break;
}
qry << " LIMIT 1";
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);
std::cout << "the query has been performed\n";
mysql_stmt_store_result(stmt);
auto rowCount = mysql_stmt_num_rows(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
return (rowCount > 0) ? true : false;
}
void GenreRepository::saveRecord(const model::Genre& genre) {
std::cout << "inserting genre record\n";
auto conn = setupMysqlConnection();
MYSQL_STMT *stmt = mysql_stmt_init(conn);
const std::string query("INSERT INTO Genre(Category) VALUES(?)");
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
MYSQL_BIND params[1];
memset(params, 0, sizeof(params));
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)genre.category.c_str();
auto categoryLength = genre.category.size();
params[0].length = &categoryLength;
params[0].is_null = 0;
status = mysql_stmt_bind_param(stmt, params);
status = mysql_stmt_execute(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
std::cout << "inserted record\n";
}
void GenreRepository::deleteRecord(const model::Genre& genre,
type::GenreFilter filter = type::GenreFilter::id) {
std::cout << "deleting genre record\n";
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
qry << "DELETE FROM Genre WHERE ";
MYSQL_BIND params[1];
std::memset(params, 0, sizeof(params));
switch (filter) {
case type::GenreFilter::id:
qry << "GenreId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&genre.id;
params[0].length = 0;
params[0].is_null = 0;
break;
default:
break;
}
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);
mysql_stmt_close(stmt);
mysql_close(conn);
std::cout << "deleted genre record\n";
}
std::vector<model::Genre> GenreRepository::parseRecords(MYSQL_STMT *stmt) {
mysql_stmt_store_result(stmt);
std::vector<model::Genre> genres;
genres.reserve(mysql_stmt_num_rows(stmt));
constexpr auto valAmt = 2;
for (auto status = 0; status == 0; status = mysql_stmt_next_result(stmt)) {
if (mysql_stmt_field_count(stmt) > 0) {
model::Genre genre;
auto metaBuff = metadataBuffer();
auto bindedValues = valueBind(genre, metaBuff);
status = mysql_stmt_bind_result(stmt, bindedValues.get());
std::cout << "fetching statement result\n";
status = mysql_stmt_fetch(stmt);
if (status == 1 || status == MYSQL_NO_DATA) break;
genre.category = std::get<0>(metaBuff);
genres.push_back(genre);
}
std::cout << "fetching statement result\n";
}
return genres;
}
std::pair<model::Genre, int> GenreRepository::parseRecordWithSongCount(MYSQL_STMT *stmt) {
std::cout << "parsing genre record with song count\n";
mysql_stmt_store_result(stmt);
const auto rowCount = mysql_stmt_num_rows(stmt);
model::Genre genre;
int songCount = 0;
auto metaBuff = metadataBuffer();
auto val = valueBindWithSongCount(genre, metaBuff, songCount);
if (rowCount == 0) {
std::cout << "no results\n";
return std::make_pair(genre, songCount);
}
auto status = mysql_stmt_bind_result(stmt, val.get());
status = mysql_stmt_fetch(stmt);
genre.category = std::get<0>(metaBuff);
std::cout << "parsed genre record with song count\n";
return std::make_pair(genre, songCount);
}
std::shared_ptr<MYSQL_BIND> GenreRepository::valueBind(model::Genre& genre,
std::tuple<char*>& metadata) {
constexpr auto valueCount = 2;
constexpr auto wordLen = 1024;
std::shared_ptr<MYSQL_BIND> values((MYSQL_BIND*)
std::calloc(valueCount, sizeof(MYSQL_BIND)));
values.get()[0].buffer_type = MYSQL_TYPE_LONG;
values.get()[0].buffer = reinterpret_cast<char*>(&genre.id);
values.get()[1].buffer_type = MYSQL_TYPE_STRING;
values.get()[1].buffer = reinterpret_cast<char*>(std::get<0>(metadata));
values.get()[1].buffer_length = wordLen;
return values;
}
std::cout << "retrieving genre record" << std::endl; std::shared_ptr<MYSQL_BIND> GenreRepository::valueBindWithSongCount(model::Genre& genre,
std::stringstream qry; std::tuple<char*>& metadata, int& songCount) {
auto conn = setupMysqlConnection(); constexpr auto valueCount = 3;
qry << "SELECT gnr.* FROM Genre gnr WHERE "; constexpr auto wordLen = 1024;
std::shared_ptr<MYSQL_BIND> values((MYSQL_BIND*)
std::calloc(valueCount, sizeof(MYSQL_BIND)));
std::unique_ptr<char*> param; values.get()[0].buffer_type = MYSQL_TYPE_LONG;
switch (filter) { values.get()[0].buffer = reinterpret_cast<char*>(&genre.id);
case type::GenreFilter::id:
qry << "gnr.GenreId = " << genre.id; values.get()[1].buffer_type = MYSQL_TYPE_STRING;
break; values.get()[1].buffer = reinterpret_cast<char*>(std::get<0>(metadata));
case type::GenreFilter::category: values.get()[1].buffer_length = wordLen;
param = std::make_unique<char*>(new char[genre.category.size()]);
mysql_real_escape_string(conn, *param, genre.category.c_str(), genre.category.size()); values.get()[2].buffer_type = MYSQL_TYPE_LONG;
qry << "gnr.Category ='" << *param << "'"; values.get()[2].buffer = reinterpret_cast<char*>(&songCount);
break;
default: return values;
break;
} }
qry << " ORDER BY GenreId DESC LIMIT 1";
const auto query = qry.str(); std::tuple<char*> GenreRepository::metadataBuffer() {
auto results = performMysqlQuery(conn, query); constexpr auto wordLen = 1024;
char category[wordLen];
genre = parseRecord(results); return std::make_tuple(category);
mysql_close(conn);
std::cout << "retrieved record" << std::endl;
return genre;
}
bool database::GenreRepository::doesGenreExist(const model::Genre& genre, type::GenreFilter filter)
{
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
std::stringstream qry;
qry << "SELECT * FROM Genre WHERE ";
MYSQL_BIND params[1];
memset(params, 0, sizeof(params));
auto categoryLength = genre.category.size();
switch (filter) {
case type::GenreFilter::id:
qry << "GenreId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&genre.id;
params[0].length = 0;
params[0].is_null = 0;
break;
case type::GenreFilter::category:
qry << "Category = ?";
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)genre.category.c_str();
params[0].length = &categoryLength;
params[0].is_null = 0;
break;
default:
break;
} }
qry << " LIMIT 1";
const auto query = qry.str(); model::Genre GenreRepository::parseRecord(MYSQL_STMT *stmt) {
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size()); std::cout << "parsing genre record\n";
status = mysql_stmt_bind_param(stmt, params); mysql_stmt_store_result(stmt);
status = mysql_stmt_execute(stmt);
std::cout << "the query has been performed" << std::endl; model::Genre genre;
auto metaBuff = metadataBuffer();
mysql_stmt_store_result(stmt); auto bindedValues = valueBind(genre, metaBuff);
auto rowCount = mysql_stmt_num_rows(stmt); auto status = mysql_stmt_bind_result(stmt, bindedValues.get());
mysql_stmt_close(stmt);
mysql_close(conn);
return (rowCount > 0) ? true : false;
}
void database::GenreRepository::saveRecord(const model::Genre& genre)
{
std::cout << "inserting genre record" << std::endl;
auto conn = setupMysqlConnection();
MYSQL_STMT *stmt = mysql_stmt_init(conn);
const std::string query("INSERT INTO Genre(Category) VALUES(?)");
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
MYSQL_BIND params[1];
memset(params, 0, sizeof(params));
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)genre.category.c_str();
auto categoryLength = genre.category.size();
params[0].length = &categoryLength;
params[0].is_null = 0;
status = mysql_stmt_bind_param(stmt, params);
status = mysql_stmt_execute(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
std::cout << "inserted record" << std::endl;
}
void database::GenreRepository::deleteRecord(const model::Genre& genre, type::GenreFilter filter = type::GenreFilter::id)
{
// TODO: implement this
std::cout << "deleting genre record" << std::endl;
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
qry << "DELETE FROM Genre WHERE ";
MYSQL_BIND params[1];
std::memset(params, 0, sizeof(params));
switch (filter) {
case type::GenreFilter::id:
qry << "GenreId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&genre.id;
params[0].length = 0;
params[0].is_null = 0;
break;
default:
break;
}
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);
mysql_stmt_close(stmt);
mysql_close(conn);
std::cout << "deleted genre record" << std::endl;
}
std::vector<model::Genre> database::GenreRepository::parseRecords(MYSQL_STMT *stmt)
{
mysql_stmt_store_result(stmt);
std::vector<model::Genre> genres;
genres.reserve(mysql_stmt_num_rows(stmt));
if (mysql_stmt_field_count(stmt) == 0) {
std::cout << "field count is 0" << std::endl;
return genres;
}
model::Genre gnr;
const auto valAmt = 2;
unsigned long len[valAmt];
my_bool nullRes[valAmt];
auto res = mysql_stmt_result_metadata(stmt);
auto fields = mysql_fetch_fields(res);
const auto strLen = 1024;
char category[strLen];
MYSQL_BIND val[valAmt];
memset(val, 0, sizeof(val));
val[0].buffer_type = MYSQL_TYPE_LONG;
val[0].buffer = (char*)&gnr.id;
val[0].length = &len[0];
val[0].is_null = &nullRes[0];
val[1].buffer_type = MYSQL_TYPE_STRING;
val[1].buffer = (char*)category;
val[1].buffer_length = strLen;
val[1].length = &len[1];
val[1].is_null = &nullRes[1];
for (auto status = mysql_stmt_bind_result(stmt, val); status == 0;) {
std::cout << "fetching statement result" << std::endl;
status = mysql_stmt_fetch(stmt); status = mysql_stmt_fetch(stmt);
if (status == 0) { genre.category = std::get<0>(metaBuff);
gnr.category = category;
genres.push_back(std::move(gnr)); std::cout << "done parsing genre record\n";
}
return genre;
} }
return genres;
}
std::pair<model::Genre, int> database::GenreRepository::parseRecordWithSongCount(MYSQL_STMT *stmt)
{
std::cout << "parsing genre record with song count" << std::endl;
mysql_stmt_store_result(stmt);
const auto strLen = 1024;
const auto valAmt = 3;
unsigned long len[valAmt];
my_bool nullRes[valAmt];
model::Genre genre;
int songCount = 0;
if (mysql_stmt_num_rows(stmt) == 0) {
std::cout << "no results" << std::endl;
return std::make_pair(genre, songCount);
}
MYSQL_BIND val[valAmt];
std::memset(val, 0, sizeof(val));
char category[strLen] ;
val[0].buffer_type = MYSQL_TYPE_LONG;
val[0].buffer = (char*)&genre.id;
val[0].length = &len[0];
val[0].is_null = &nullRes[0];
val[1].buffer_type = MYSQL_TYPE_STRING;
val[1].buffer = (char*)category;
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*)&songCount;
val[2].length = &len[2];
val[2].is_null = &nullRes[2];
mysql_stmt_bind_result(stmt, val);
mysql_stmt_fetch(stmt);
genre.category = std::move(category);
std::cout << "parsed genre record with song count" << std::endl;
return std::make_pair(genre, songCount);
}
model::Genre database::GenreRepository::parseRecord(MYSQL_RES* results)
{
std::cout << "parsing genre record" << std::endl;
model::Genre genre;
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("GenreId") == 0) {
genre.id = std::stoi(row[i]);
}
if (field.compare("Category") == 0) {
genre.category = row[i];
}
}
std::cout << "parsed genre record" << std::endl;
return genre;
}
model::Genre database::GenreRepository::parseRecord(MYSQL_STMT *stmt)
{
// TODO: implement this
model::Genre genre;
return genre;
} }
File diff suppressed because it is too large Load Diff
+263 -265
View File
@@ -7,351 +7,349 @@
namespace database { namespace database {
UserRepository::UserRepository(const model::BinaryPath& bConf) : BaseRepository(bConf) { } UserRepository::UserRepository(const model::BinaryPath& bConf) : BaseRepository(bConf) { }
model::User UserRepository::retrieveUserRecord(model::User& user, model::User UserRepository::retrieveUserRecord(model::User& user,
type::UserFilter filter = type::UserFilter::username) type::UserFilter filter = type::UserFilter::username) {
{ std::stringstream qry;
std::stringstream qry; auto conn = setupMysqlConnection();
auto conn = setupMysqlConnection(); auto stmt = mysql_stmt_init(conn);
auto stmt = mysql_stmt_init(conn);
qry << "SELECT * FROM User WHERE "; qry << "SELECT * FROM User WHERE ";
MYSQL_BIND params[1]; MYSQL_BIND params[1];
std::memset(params, 0, sizeof(params)); std::memset(params, 0, sizeof(params));
auto userLength = user.username.size(); auto userLength = user.username.size();
switch (filter) { switch (filter) {
case type::UserFilter::id: case type::UserFilter::id:
break; qry << "UserId = ?";
case type::UserFilter::username:
qry << "Username = ?";
params[0].buffer_type = MYSQL_TYPE_STRING; params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)user.username.c_str(); params[0].buffer = (char*)&user.id;
params[0].length = &userLength; params[0].length = nullptr;
params[0].is_null = 0; params[0].is_null = 0;
break;
default: break;
break; case type::UserFilter::username:
qry << "Username = ?";
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)user.username.c_str();
params[0].length = &userLength;
params[0].is_null = 0;
break;
default:
break;
}
qry << " LIMIT 1";
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);
user = parseRecord(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
return user;
} }
qry << " LIMIT 1"; model::PassSec UserRepository::retrieverUserSaltRecord(model::PassSec& userSec,
type::SaltFilter filter) {
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
const auto query = qry.str(); qry << "SELECT * FROM Salt WHERE ";
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
status = mysql_stmt_bind_param(stmt, params);
status = mysql_stmt_execute(stmt);
user = parseRecord(stmt); MYSQL_BIND params[1];
std::memset(params, 0, sizeof(params));
mysql_stmt_close(stmt); switch (filter) {
mysql_close(conn); case type::SaltFilter::userId:
qry << "UserId = ?";
return user; params[0].buffer_type = MYSQL_TYPE_LONG;
} params[0].buffer = (char*)&userSec.userId;
params[0].length = 0;
params[0].is_null = 0;
break;
default:
break;
}
model::PassSec UserRepository::retrieverUserSaltRecord(model::PassSec& userSec, type::SaltFilter filter) qry << " LIMIT 1";
{
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
qry << "SELECT * FROM Salt WHERE "; 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);
MYSQL_BIND params[1]; userSec = parseSaltRecord(stmt);
std::memset(params, 0, sizeof(params));
switch (filter) { mysql_stmt_close(stmt);
case type::SaltFilter::userId: mysql_close(conn);
qry << "UserId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG; return userSec;
params[0].buffer = (char*)&userSec.userId;
params[0].length = 0;
params[0].is_null = 0;
break;
default:
break;
} }
qry << " LIMIT 1";
const auto query = qry.str(); bool UserRepository::doesUserRecordExist(const model::User& user, type::UserFilter filter) {
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size()); std::stringstream qry;
status = mysql_stmt_bind_param(stmt, params); auto conn = setupMysqlConnection();
status = mysql_stmt_execute(stmt); auto stmt = mysql_stmt_init(conn);
userSec = parseSaltRecord(stmt); qry << "SELECT * FROM User WHERE ";
mysql_stmt_close(stmt); MYSQL_BIND params[1];
mysql_close(conn); std::memset(params, 0, sizeof(params));
return userSec; auto userLength = user.username.size();
} switch (filter) {
case type::UserFilter::username:
qry << "Username = ?";
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)user.username.c_str();
params[0].length = &userLength;
params[0].is_null = 0;
break;
default:
break;
}
bool UserRepository::doesUserRecordExist(const model::User& user, type::UserFilter filter) qry << " LIMIT 1";
{
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
qry << "SELECT * FROM User WHERE "; 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);
MYSQL_BIND params[1]; mysql_stmt_store_result(stmt);
std::memset(params, 0, sizeof(params)); const auto rowCount = mysql_stmt_num_rows(stmt);
auto userLength = user.username.size(); mysql_stmt_close(stmt);
switch (filter) { mysql_close(conn);
case type::UserFilter::username:
qry << "Username = ?";
params[0].buffer_type = MYSQL_TYPE_STRING; return (rowCount > 0) ? true : false;
params[0].buffer = (char*)user.username.c_str();
params[0].length = &userLength;
params[0].is_null = 0;
break;
default:
break;
} }
qry << " LIMIT 1";
const auto query = qry.str(); void UserRepository::saveUserRecord(const model::User& user) {
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size()); std::cout << "inserting user record\n";
status = mysql_stmt_bind_param(stmt, params); auto conn = setupMysqlConnection();
status = mysql_stmt_execute(stmt); auto stmt = mysql_stmt_init(conn);
mysql_stmt_store_result(stmt); std::stringstream qry;
const auto rowCount = mysql_stmt_num_rows(stmt); qry << "INSERT INTO User(Firstname, Lastname, Phone, Email, Username, Password) ";
qry << "VALUES(?, ?, ?, ?, ?, ?)";
mysql_stmt_close(stmt); const auto query = qry.str();
mysql_close(conn); auto sizes = fetchUserLengths(user);
auto params = insertUserValues(user, sizes);
return (rowCount > 0) ? true : false; auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
} status = mysql_stmt_bind_param(stmt, params.get());
void UserRepository::saveUserRecord(const model::User& user) status = mysql_stmt_execute(stmt);
{
std::cout << "inserting user record" << std::endl;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
std::stringstream qry; mysql_stmt_close(stmt);
qry << "INSERT INTO User(Firstname, Lastname, Phone, Email, Username, Password) "; mysql_close(conn);
qry << "VALUES(?, ?, ?, ?, ?, ?)"; }
const auto query = qry.str(); void UserRepository::saveUserSalt(const model::PassSec& userSec) {
std::cout << "inserting user salt record\n";
auto sizes = fetchUserLengths(user); auto conn = setupMysqlConnection();
auto params = insertUserValues(user, sizes); auto stmt = mysql_stmt_init(conn);
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size()); std::stringstream qry;
status = mysql_stmt_bind_param(stmt, params.get()); qry << "INSERT INTO Salt(Salt, UserId) VALUES(?,?)";
status = mysql_stmt_execute(stmt);
mysql_stmt_close(stmt); const auto query = qry.str();
mysql_close(conn);
}
void UserRepository::saveUserSalt(const model::PassSec& userSec) auto sizes = fetchSaltLengths(userSec);
{ auto values = insertSaltValues(userSec, sizes);
std::cout << "inserting user salt record" << std::endl;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
std::stringstream qry; auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
qry << "INSERT INTO Salt(Salt, UserId) VALUES(?,?)"; status = mysql_stmt_bind_param(stmt, values.get());
status = mysql_stmt_execute(stmt);
const auto query = qry.str(); mysql_stmt_close(stmt);
mysql_close(conn);
auto sizes = fetchSaltLengths(userSec); }
auto values = insertSaltValues(userSec, sizes);
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
status = mysql_stmt_bind_param(stmt, values.get());
status = mysql_stmt_execute(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
}
std::shared_ptr<MYSQL_BIND> UserRepository::insertUserValues(const model::User& user, std::shared_ptr<MYSQL_BIND> UserRepository::insertUserValues(const model::User& user,
std::shared_ptr<UserRepository::UserLengths> lengths) std::shared_ptr<UserRepository::UserLengths> lengths) {
{ std::shared_ptr<MYSQL_BIND> values((MYSQL_BIND*) std::calloc(6, sizeof(MYSQL_BIND)));
std::shared_ptr<MYSQL_BIND> values((MYSQL_BIND*) std::calloc(6, sizeof(MYSQL_BIND)));
values.get()[0].buffer_type = MYSQL_TYPE_STRING; values.get()[0].buffer_type = MYSQL_TYPE_STRING;
values.get()[0].buffer = (char*)user.firstname.c_str(); values.get()[0].buffer = (char*)user.firstname.c_str();
values.get()[0].length = &(lengths->firstnameLength); values.get()[0].length = &(lengths->firstnameLength);
values.get()[0].is_null = 0; values.get()[0].is_null = 0;
values.get()[1].buffer_type = MYSQL_TYPE_STRING; values.get()[1].buffer_type = MYSQL_TYPE_STRING;
values.get()[1].buffer = (char*)user.lastname.c_str(); values.get()[1].buffer = (char*)user.lastname.c_str();
values.get()[1].length = &(lengths->lastnameLength); values.get()[1].length = &(lengths->lastnameLength);
values.get()[1].is_null = 0; values.get()[1].is_null = 0;
values.get()[2].buffer_type = MYSQL_TYPE_STRING; values.get()[2].buffer_type = MYSQL_TYPE_STRING;
values.get()[2].buffer = (char*)user.phone.c_str(); values.get()[2].buffer = (char*)user.phone.c_str();
values.get()[2].length = &(lengths->phoneLength); values.get()[2].length = &(lengths->phoneLength);
values.get()[2].is_null = 0; values.get()[2].is_null = 0;
values.get()[3].buffer_type = MYSQL_TYPE_STRING; values.get()[3].buffer_type = MYSQL_TYPE_STRING;
values.get()[3].buffer = (char*)user.email.c_str(); values.get()[3].buffer = (char*)user.email.c_str();
values.get()[3].length = &(lengths->emailLength); values.get()[3].length = &(lengths->emailLength);
values.get()[3].is_null = 0; values.get()[3].is_null = 0;
values.get()[4].buffer_type = MYSQL_TYPE_STRING; values.get()[4].buffer_type = MYSQL_TYPE_STRING;
values.get()[4].buffer = (char*)user.username.c_str(); values.get()[4].buffer = (char*)user.username.c_str();
values.get()[4].length = &(lengths->usernameLength); values.get()[4].length = &(lengths->usernameLength);
values.get()[4].is_null = 0; values.get()[4].is_null = 0;
values.get()[5].buffer_type = MYSQL_TYPE_STRING; values.get()[5].buffer_type = MYSQL_TYPE_STRING;
values.get()[5].buffer = (char*)user.password.c_str(); values.get()[5].buffer = (char*)user.password.c_str();
values.get()[5].length = &(lengths->passwordLength); values.get()[5].length = &(lengths->passwordLength);
values.get()[5].is_null = 0; values.get()[5].is_null = 0;
return values; return values;
} }
std::shared_ptr<MYSQL_BIND> UserRepository::insertSaltValues(const model::PassSec& passSec, std::shared_ptr<MYSQL_BIND> UserRepository::insertSaltValues(const model::PassSec& passSec,
std::shared_ptr<UserRepository::SaltLengths> lengths) std::shared_ptr<UserRepository::SaltLengths> lengths) {
{ std::shared_ptr<MYSQL_BIND> values((MYSQL_BIND*) std::calloc(6, sizeof(MYSQL_BIND)));
std::shared_ptr<MYSQL_BIND> values((MYSQL_BIND*) std::calloc(6, sizeof(MYSQL_BIND)));
values.get()[0].buffer_type = MYSQL_TYPE_STRING; values.get()[0].buffer_type = MYSQL_TYPE_STRING;
values.get()[0].buffer = (char*)passSec.hashPassword.c_str(); values.get()[0].buffer = (char*)passSec.hashPassword.c_str();
values.get()[0].length = &(lengths->saltLength); values.get()[0].length = &(lengths->saltLength);
values.get()[1].buffer_type = MYSQL_TYPE_LONG; values.get()[1].buffer_type = MYSQL_TYPE_LONG;
values.get()[1].buffer = (char*)&passSec.userId; values.get()[1].buffer = (char*)&passSec.userId;
return values;
}
return values; std::shared_ptr<MYSQL_BIND> UserRepository::valueBind(model::User& user,
} std::tuple<char*, char*, char*, char*, char*, char*>& us) {
std::shared_ptr<MYSQL_BIND> values((MYSQL_BIND*) std::calloc(7, sizeof(MYSQL_BIND)));
constexpr auto strLen = 1024;
std::shared_ptr<MYSQL_BIND> UserRepository::valueBind(model::User& user, values.get()[0].buffer_type = MYSQL_TYPE_LONG;
std::tuple<char*, char*, char*, char*, char*, char*>& us) values.get()[0].buffer = (char*)&user.id;
{
std::shared_ptr<MYSQL_BIND> values((MYSQL_BIND*) std::calloc(7, sizeof(MYSQL_BIND)));
constexpr auto strLen = 1024;
values.get()[0].buffer_type = MYSQL_TYPE_LONG; values.get()[1].buffer_type = MYSQL_TYPE_STRING;
values.get()[0].buffer = (char*)&user.id; values.get()[1].buffer = (char*)std::get<0>(us);
values.get()[1].buffer_length = strLen;
values.get()[1].buffer_type = MYSQL_TYPE_STRING; values.get()[2].buffer_type = MYSQL_TYPE_STRING;
values.get()[1].buffer = (char*)std::get<0>(us); values.get()[2].buffer = (char*)std::get<1>(us);
values.get()[1].buffer_length = strLen; values.get()[2].buffer_length = strLen;
values.get()[2].buffer_type = MYSQL_TYPE_STRING; values.get()[3].buffer_type = MYSQL_TYPE_STRING;
values.get()[2].buffer = (char*)std::get<1>(us); values.get()[3].buffer = (char*)std::get<2>(us);
values.get()[2].buffer_length = strLen; values.get()[3].buffer_length = strLen;
values.get()[3].buffer_type = MYSQL_TYPE_STRING;
values.get()[3].buffer = (char*)std::get<2>(us);
values.get()[3].buffer_length = strLen;
values.get()[4].buffer_type = MYSQL_TYPE_STRING; values.get()[4].buffer_type = MYSQL_TYPE_STRING;
values.get()[4].buffer = (char*)std::get<3>(us); values.get()[4].buffer = (char*)std::get<3>(us);
values.get()[4].buffer_length = strLen; values.get()[4].buffer_length = strLen;
values.get()[5].buffer_type = MYSQL_TYPE_STRING; values.get()[5].buffer_type = MYSQL_TYPE_STRING;
values.get()[5].buffer = (char*)std::get<4>(us); values.get()[5].buffer = (char*)std::get<4>(us);
values.get()[5].buffer_length = strLen; values.get()[5].buffer_length = strLen;
values.get()[6].buffer_type = MYSQL_TYPE_STRING; values.get()[6].buffer_type = MYSQL_TYPE_STRING;
values.get()[6].buffer = (char*)std::get<5>(us); values.get()[6].buffer = (char*)std::get<5>(us);
values.get()[6].buffer_length = strLen; values.get()[6].buffer_length = strLen;
return values; return values;
} }
std::shared_ptr<MYSQL_BIND> UserRepository::saltValueBind(model::PassSec& userSalt, char *salt) std::shared_ptr<MYSQL_BIND> UserRepository::saltValueBind(model::PassSec& userSalt,
{ char *salt) {
std::shared_ptr<MYSQL_BIND> values((MYSQL_BIND*) std::calloc(3, sizeof(MYSQL_BIND))); std::shared_ptr<MYSQL_BIND> values((MYSQL_BIND*) std::calloc(3, sizeof(MYSQL_BIND)));
constexpr auto strLen = 1024; constexpr auto strLen = 1024;
values.get()[0].buffer_type = MYSQL_TYPE_LONG; values.get()[0].buffer_type = MYSQL_TYPE_LONG;
values.get()[0].buffer = (char*)&userSalt.id; values.get()[0].buffer = (char*)&userSalt.id;
values.get()[1].buffer_type = MYSQL_TYPE_STRING; values.get()[1].buffer_type = MYSQL_TYPE_STRING;
values.get()[1].buffer = (char*)salt; values.get()[1].buffer = (char*)salt;
values.get()[1].buffer_length = strLen; values.get()[1].buffer_length = strLen;
values.get()[2].buffer_type = MYSQL_TYPE_LONG; values.get()[2].buffer_type = MYSQL_TYPE_LONG;
values.get()[2].buffer = (char*)&userSalt.userId; values.get()[2].buffer = (char*)&userSalt.userId;
return values; return values;
} }
std::shared_ptr<UserRepository::UserLengths> UserRepository::fetchUserLengths(const model::User& user) std::shared_ptr<UserRepository::UserLengths> UserRepository::fetchUserLengths(
{ const model::User& user) {
auto userLen = std::make_shared<UserLengths>(); auto userLen = std::make_shared<UserLengths>();
userLen->firstnameLength = user.firstname.size(); userLen->firstnameLength = user.firstname.size();
userLen->lastnameLength = user.lastname.size(); userLen->lastnameLength = user.lastname.size();
userLen->phoneLength = user.phone.size(); userLen->phoneLength = user.phone.size();
userLen->emailLength = user.email.size(); userLen->emailLength = user.email.size();
userLen->usernameLength = user.username.size(); userLen->usernameLength = user.username.size();
userLen->passwordLength = user.password.size(); userLen->passwordLength = user.password.size();
return userLen; return userLen;
} }
std::shared_ptr<UserRepository::SaltLengths> UserRepository::fetchSaltLengths(const model::PassSec& passSec) std::shared_ptr<UserRepository::SaltLengths> UserRepository::fetchSaltLengths(
{ const model::PassSec& passSec) {
auto saltLen = std::make_shared<SaltLengths>(); auto saltLen = std::make_shared<SaltLengths>();
saltLen->saltLength = passSec.salt.size(); saltLen->saltLength = passSec.salt.size();
return saltLen; return saltLen;
} }
std::tuple<char*, char*, char*, char*, char*, char*> UserRepository::fetchUV() std::tuple<char*, char*, char*, char*, char*, char*> UserRepository::fetchUV() {
{ char firstname[1024];
char firstname[1024]; char lastname[1024];
char lastname[1024]; char phone[1024];
char phone[1024]; char email[1024];
char email[1024]; char username[1024];
char username[1024]; char password[1024];
char password[1024];
return std::make_tuple(firstname, lastname, phone, email, username, password); return std::make_tuple(firstname, lastname, phone, email, username, password);
} }
model::User UserRepository::parseRecord(MYSQL_STMT *stmt) model::User UserRepository::parseRecord(MYSQL_STMT *stmt) {
{ model::User user;
model::User user; auto usv = fetchUV();
auto usv = fetchUV();
auto bindedValues = valueBind(user, usv); auto bindedValues = valueBind(user, usv);
auto status = mysql_stmt_bind_result(stmt, bindedValues.get()); auto status = mysql_stmt_bind_result(stmt, bindedValues.get());
status = mysql_stmt_fetch(stmt); status = mysql_stmt_fetch(stmt);
user.firstname = std::get<0>(usv); user.firstname = std::get<0>(usv);
user.lastname = std::get<1>(usv); user.lastname = std::get<1>(usv);
user.email = std::get<2>(usv); user.email = std::get<2>(usv);
user.phone = std::get<3>(usv); user.phone = std::get<3>(usv);
user.username = std::get<4>(usv); user.username = std::get<4>(usv);
user.password = std::get<5>(usv); user.password = std::get<5>(usv);
return user; return user;
} }
model::PassSec UserRepository::parseSaltRecord(MYSQL_STMT* stmt) model::PassSec UserRepository::parseSaltRecord(MYSQL_STMT* stmt) {
{ model::PassSec userSalt;
model::PassSec userSalt; char saltKey[1024];
char saltKey[1024];
auto bindedValues = saltValueBind(userSalt, saltKey);
auto bindedValues = saltValueBind(userSalt, saltKey); auto status = mysql_stmt_bind_result(stmt, bindedValues.get());
auto status = mysql_stmt_bind_result(stmt, bindedValues.get()); status = mysql_stmt_fetch(stmt);
status = mysql_stmt_fetch(stmt);
userSalt.salt = saltKey;
userSalt.salt = saltKey;
return userSalt;
return userSalt; }
}
} }
+272 -272
View File
@@ -6,337 +6,337 @@
#include <string> #include <string>
#include <cstring> #include <cstring>
namespace database {
database::YearRepository::YearRepository(const model::BinaryPath& bConf) YearRepository::YearRepository(const model::BinaryPath& bConf) : BaseRepository(bConf) { }
: BaseRepository(bConf)
{ }
std::vector<model::Year> database::YearRepository::retrieveRecords() std::vector<model::Year> YearRepository::retrieveRecords() {
{ auto conn = setupMysqlConnection();
auto conn = setupMysqlConnection(); auto stmt = mysql_stmt_init(conn);
auto stmt = mysql_stmt_init(conn); const std::string query = "SELECT * FROM Year";
const std::string query = "SELECT * FROM Year";
mysql_stmt_prepare(stmt, query.c_str(), query.size()); mysql_stmt_prepare(stmt, query.c_str(), query.size());
mysql_stmt_execute(stmt); mysql_stmt_execute(stmt);
auto yearRecs = parseRecords(stmt); auto yearRecs = parseRecords(stmt);
mysql_stmt_close(stmt); mysql_stmt_close(stmt);
mysql_close(conn); mysql_close(conn);
return yearRecs; return yearRecs;
}
std::pair<model::Year, int> database::YearRepository::retrieveRecordWithSongCount(model::Year& year, type::YearFilter filter = type::YearFilter::id)
{
std::cout << "retrieving year record with song count" << std::endl;
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
qry << "SELECT yr.*, COUNT(*) AS SongCount FROM Year yr LEFT JOIN ";
qry << "Song sng ON yr.YearId=sng.YearId WHERE ";
MYSQL_BIND params[1];
std::memset(params, 0, sizeof(params));
switch (filter) {
case type::YearFilter::id:
qry << "sng.YearId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&year.id;
params[0].length = 0;
params[0].is_null = 0;
break;
default:
break;
} }
qry << " GROUP BY yr.YearId LIMIT 1"; std::pair<model::Year, int> YearRepository::retrieveRecordWithSongCount(model::Year& year,
type::YearFilter filter) {
std::cout << "retrieving year record with song count\n";
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
const auto query = qry.str(); qry << "SELECT yr.*, COUNT(*) AS SongCount FROM Year yr LEFT JOIN ";
qry << "Song sng ON yr.YearId=sng.YearId WHERE ";
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size()); MYSQL_BIND params[1];
status = mysql_stmt_bind_param(stmt, params); std::memset(params, 0, sizeof(params));
status = mysql_stmt_execute(stmt);
auto yearWSC = parseRecordWithSongCount(stmt); switch (filter) {
case type::YearFilter::id:
qry << "sng.YearId = ?";
mysql_stmt_close(stmt); params[0].buffer_type = MYSQL_TYPE_LONG;
mysql_close(conn); params[0].buffer = (char*)&year.id;
params[0].length = 0;
params[0].is_null = 0;
break;
default:
break;
}
std::cout << "retrieved year record with song count" << std::endl; qry << " GROUP BY yr.YearId LIMIT 1";
return yearWSC; const auto query = qry.str();
}
model::Year database::YearRepository::retrieveRecord(model::Year& year, type::YearFilter filter) auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
{ status = mysql_stmt_bind_param(stmt, params);
// TODO: switch to prepared statements status = mysql_stmt_execute(stmt);
std::cout << "retrieving year record" << std::endl;
std::stringstream qry;
auto conn = setupMysqlConnection();
qry << "SELECT yr.* FROM Year yr WHERE ";
switch (filter) { auto yearWSC = parseRecordWithSongCount(stmt);
case type::YearFilter::id:
qry << "yr.YearId = " << year.id; mysql_stmt_close(stmt);
break; mysql_close(conn);
case type::YearFilter::year:
qry << "yr.Year = " << year.year; std::cout << "retrieved year record with song count\n";
break;
default: return yearWSC;
break;
} }
qry << " ORDER BY yr.YearId DESC LIMIT 1"; model::Year YearRepository::retrieveRecord(model::Year& year, type::YearFilter filter) {
std::cout << "retrieving year record\n";
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
qry << "SELECT yr.* FROM Year yr WHERE ";
const auto query = qry.str(); MYSQL_BIND params[1];
auto results = performMysqlQuery(conn, query); std::memset(params, 0, sizeof(0));
year = parseRecord(results); switch (filter) {
case type::YearFilter::id:
qry << "yr.YearId = ?";
mysql_close(conn); params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = reinterpret_cast<char*>(&year.id);
params[0].length = 0;
params[0].is_null = 0;
break;
case type::YearFilter::year:
qry << "yr.Year = ?";
std::cout << "retrieved record" << std::endl; params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = reinterpret_cast<char*>(&year.year);
params[0].length = 0;
params[0].is_null = 0;
break;
default:
break;
}
return year; qry << " ORDER BY yr.YearId DESC LIMIT 1";
}
bool database::YearRepository::doesYearExist(const model::Year& year, type::YearFilter filter) const auto query = qry.str();
{
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
std::stringstream qry; auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
qry << "SELECT * FROM Year WHERE "; status = mysql_stmt_bind_param(stmt, params);
status = mysql_stmt_execute(stmt);
MYSQL_BIND params[1]; year = parseRecord(stmt);
memset(params, 0, sizeof(params));
switch (filter) { mysql_stmt_close(stmt);
case type::YearFilter::id: mysql_close(conn);
qry << "YearId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG; std::cout << "retrieved record\n";
params[0].buffer = (char*)&year.id;
params[0].length = 0;
params[0].is_null = 0;
break;
case type::YearFilter::year:
qry << "Year = ?";
params[0].buffer_type = MYSQL_TYPE_LONG; return year;
params[0].buffer = (char*)&year.year;
params[0].length = 0;
params[0].is_null = 0;
break;
default:
break;
} }
qry << " LIMIT 1";
const auto query = qry.str(); bool YearRepository::doesYearExist(const model::Year& year, type::YearFilter filter) {
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size()); auto conn = setupMysqlConnection();
status = mysql_stmt_bind_param(stmt, params); auto stmt = mysql_stmt_init(conn);
status = mysql_stmt_execute(stmt);
std::cout << "the query has been performed" << std::endl; std::stringstream qry;
qry << "SELECT * FROM Year WHERE ";
mysql_stmt_store_result(stmt); MYSQL_BIND params[1];
auto rowCount = mysql_stmt_num_rows(stmt); memset(params, 0, sizeof(params));
mysql_stmt_close(stmt); switch (filter) {
mysql_close(conn); case type::YearFilter::id:
qry << "YearId = ?";
return (rowCount > 0) ? true : false; params[0].buffer_type = MYSQL_TYPE_LONG;
} params[0].buffer = (char*)&year.id;
params[0].length = 0;
params[0].is_null = 0;
break;
case type::YearFilter::year:
qry << "Year = ?";
void database::YearRepository::saveRecord(const model::Year& year) params[0].buffer_type = MYSQL_TYPE_LONG;
{ params[0].buffer = (char*)&year.year;
std::cout << "saving year record" << std::endl; params[0].length = 0;
params[0].is_null = 0;
break;
default:
break;
}
auto conn = setupMysqlConnection(); qry << " LIMIT 1";
MYSQL_STMT *stmt = mysql_stmt_init(conn);
const std::string query("INSERT INTO Year(Year) VALUES(?)"); 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);
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size()); std::cout << "the query has been performed\n";
MYSQL_BIND params[1]; mysql_stmt_store_result(stmt);
memset(params, 0 , sizeof(params)); auto rowCount = mysql_stmt_num_rows(stmt);
params[0].buffer_type = MYSQL_TYPE_LONG; mysql_stmt_close(stmt);
params[0].buffer = (char*)&year.year; mysql_close(conn);
params[0].length = 0;
params[0].is_null = 0;
status = mysql_stmt_bind_param(stmt, params); return (rowCount > 0) ? true : false;
status = mysql_stmt_execute(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
std::cout << "saved record" << std::endl;
}
void database::YearRepository::deleteYear(const model::Year& year, type::YearFilter filter = type::YearFilter::id)
{
std::cout << "deleting year record" << std::endl;
std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
qry << "DELETE FROM Year WHERE ";
MYSQL_BIND params[1];
std::memset(params, 0, sizeof(params));
switch (filter) {
case type::YearFilter::id:
qry << "YearId = ?";
params[0].buffer_type = MYSQL_TYPE_LONG;
params[0].buffer = (char*)&year.id;
params[0].length = 0;
params[0].is_null = 0;
break;
default:
break;
} }
const auto query = qry.str();
auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size()); void YearRepository::saveRecord(const model::Year& year) {
status = mysql_stmt_bind_param(stmt, params); std::cout << "saving year record\n";
status = mysql_stmt_execute(stmt);
mysql_stmt_close(stmt); auto conn = setupMysqlConnection();
mysql_close(conn); MYSQL_STMT *stmt = mysql_stmt_init(conn);
std::cout << "deleted year record" << std::endl; const std::string query("INSERT INTO Year(Year) VALUES(?)");
}
std::vector<model::Year> database::YearRepository::parseRecords(MYSQL_STMT *stmt) auto status = mysql_stmt_prepare(stmt, query.c_str(), query.size());
{
mysql_stmt_store_result(stmt);
std::vector<model::Year> yearRecs; MYSQL_BIND params[1];
yearRecs.reserve(mysql_stmt_num_rows(stmt)); memset(params, 0 , sizeof(params));
if (mysql_stmt_field_count(stmt) == 0) { params[0].buffer_type = MYSQL_TYPE_LONG;
std::cout << "field count is 0" << std::endl; params[0].buffer = (char*)&year.year;
return yearRecs; params[0].length = 0;
params[0].is_null = 0;
status = mysql_stmt_bind_param(stmt, params);
status = mysql_stmt_execute(stmt);
mysql_stmt_close(stmt);
mysql_close(conn);
std::cout << "saved record\n";
} }
model::Year yearRec; void YearRepository::deleteYear(const model::Year& year,
const auto valAmt = 2; type::YearFilter filter) {
unsigned long len[valAmt]; std::cout << "deleting year record\n";
my_bool nullRes[valAmt]; std::stringstream qry;
auto conn = setupMysqlConnection();
auto stmt = mysql_stmt_init(conn);
auto res = mysql_stmt_result_metadata(stmt); qry << "DELETE FROM Year WHERE ";
MYSQL_BIND val[valAmt]; MYSQL_BIND params[1];
memset(val, 0, sizeof(val)); std::memset(params, 0, sizeof(params));
val[0].buffer_type = MYSQL_TYPE_LONG; switch (filter) {
val[0].buffer = (char*)&yearRec.id; case type::YearFilter::id:
val[0].length = &len[0]; qry << "YearId = ?";
val[0].is_null = &nullRes[0];
val[1].buffer_type = MYSQL_TYPE_LONG; params[0].buffer_type = MYSQL_TYPE_LONG;
val[1].buffer = (char*)&yearRec.year; params[0].buffer = (char*)&year.id;
val[1].length = &len[1]; params[0].length = 0;
val[1].is_null = &nullRes[1]; params[0].is_null = 0;
break;
default:
break;
}
for (auto status = mysql_stmt_bind_result(stmt, val); status == 0;) { const auto query = qry.str();
std::cout << "fetching statement result" << std::endl;
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 << "deleted year record\n";
}
std::vector<model::Year> YearRepository::parseRecords(MYSQL_STMT *stmt) {
mysql_stmt_store_result(stmt);
const auto rowCount = mysql_stmt_num_rows(stmt);
std::vector<model::Year> yearRecs;
yearRecs.reserve(rowCount);
if (mysql_stmt_field_count(stmt) == 0) {
std::cout << "field count is 0\n";
return yearRecs;
}
const auto valAmt = 2;
unsigned long len[valAmt];
my_bool nullRes[valAmt];
for (auto status = 0; status == 0; status = mysql_stmt_next_result(stmt)) {
if (mysql_stmt_field_count(stmt) > 0) {
model::Year yearRec;
std::cout << "fetching statement result\n";
auto bindedValues = valueBind(yearRec);
status = mysql_stmt_bind_result(stmt, bindedValues.get());
status = mysql_stmt_fetch(stmt);
if (status == 1 || status == MYSQL_NO_DATA) break;
yearRecs.push_back(yearRec);
}
std::cout << "fetching next result\n";
}
return yearRecs;
}
std::pair<model::Year, int> YearRepository::parseRecordWithSongCount(MYSQL_STMT *stmt) {
std::cout << "parsing year record\n";
mysql_stmt_store_result(stmt);
model::Year year;
int songCount = 0;
if (mysql_stmt_num_rows(stmt) == 0) {
std::cout << "no results\n";
return std::make_pair(year, songCount);
}
auto val = valueBindWithSongCount(year, songCount);
auto status = mysql_stmt_bind_result(stmt, val.get());
status = mysql_stmt_fetch(stmt);
std::cout << "parsed year record from the database\n";
return std::make_pair(year, songCount);
}
std::shared_ptr<MYSQL_BIND> YearRepository::valueBind(model::Year& year) {
constexpr auto valueCount = 2;
std::shared_ptr<MYSQL_BIND> values((MYSQL_BIND*)
std::calloc(valueCount, sizeof(MYSQL_BIND)));
values.get()[0].buffer_type = MYSQL_TYPE_LONG;
values.get()[0].buffer = reinterpret_cast<char*>(&year.id);
values.get()[1].buffer_type = MYSQL_TYPE_LONG;
values.get()[1].buffer = reinterpret_cast<char*>(&year.year);
return values;
}
std::shared_ptr<MYSQL_BIND> YearRepository::valueBindWithSongCount(model::Year& year,
int& songCount) {
constexpr auto valueCount = 3;
std::shared_ptr<MYSQL_BIND> values((MYSQL_BIND*)
std::calloc(valueCount, sizeof(MYSQL_BIND)));
values.get()[0].buffer_type = MYSQL_TYPE_LONG;
values.get()[0].buffer = reinterpret_cast<char*>(&year.id);
values.get()[1].buffer_type = MYSQL_TYPE_LONG;
values.get()[1].buffer = reinterpret_cast<char*>(&year.year);
values.get()[2].buffer_type = MYSQL_TYPE_LONG;
values.get()[2].buffer = reinterpret_cast<char*>(&songCount);
return values;
}
model::Year YearRepository::parseRecord(MYSQL_STMT *stmt) {
std::cout << "parsing year record\n";
mysql_stmt_store_result(stmt);
model::Year year;
auto bindedValues = valueBind(year);
auto status = mysql_stmt_bind_result(stmt, bindedValues.get());
status = mysql_stmt_fetch(stmt); status = mysql_stmt_fetch(stmt);
if (status == 0) { std::cout << "done parsing year record\n";
yearRecs.push_back(std::move(yearRec));
} return year;
} }
return yearRecs;
}
std::pair<model::Year, int> database::YearRepository::parseRecordWithSongCount(MYSQL_STMT *stmt)
{
std::cout << "parsing year record" << std::endl;
mysql_stmt_store_result(stmt);
constexpr auto valAmt = 3;
unsigned long len[valAmt];
my_bool nullRes[valAmt];
model::Year year;
int songCount = 0;
if (mysql_stmt_num_rows(stmt) == 0) {
std::cout << "no results" << std::endl;
return std::make_pair(year, songCount);
}
MYSQL_BIND val[valAmt];
std::memset(val, 0, sizeof(val));
val[0].buffer_type = MYSQL_TYPE_LONG;
val[0].buffer = (char*)&year.id;
val[0].length = &len[0];
val[0].is_null = &nullRes[0];
val[1].buffer_type = MYSQL_TYPE_LONG;
val[1].buffer = (char*)&year.year;
val[1].length = &len[1];
val[1].is_null = &nullRes[1];
val[2].buffer_type = MYSQL_TYPE_LONG;
val[2].buffer = (char*)&songCount;
val[2].length = &len[2];
val[2].is_null = &nullRes[2];
mysql_stmt_bind_result(stmt, val);
mysql_stmt_fetch(stmt);
std::cout << "parsed year record from the database" << std::endl;
return std::make_pair(year, songCount);
}
model::Year database::YearRepository::parseRecord(MYSQL_RES *results)
{
std::cout << "parsing year record" << std::endl;
model::Year year;
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("YearId") == 0) {
year.id= std::stoi(row[i]);
}
if (field.compare("Year") == 0) {
year.year = std::stoi(row[i]);
}
}
std::cout << "parse year record" << std::endl;
return year;
}
model::Year database::YearRepository::parseRecord(MYSQL_STMT *stmt)
{
// TODO: imeplement this
// I really thought that I had already done this
model::Year year;
return year;
} }
+35 -2
View File
@@ -1,8 +1,8 @@
#include "dto/conversion/DtoConversions.h" #include "dto/conversion/DtoConversions.h"
namespace dto { namespace conversion { namespace dto { namespace conversion {
dto::LoginResultDto::ObjectWrapper DtoConversions::toLoginResultDto(const model::User& user, LoginResultDto::ObjectWrapper DtoConversions::toLoginResultDto(const model::User& user,
const model::Token& token) { const model::Token& token) {
auto logRes = dto::LoginResultDto::createShared(); auto logRes = dto::LoginResultDto::createShared();
logRes->username = user.username.c_str(); logRes->username = user.username.c_str();
logRes->token = token.accessToken.c_str(); logRes->token = token.accessToken.c_str();
@@ -24,16 +24,49 @@ 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;
result->track = (song.track != 0) ? song.track : 0;
result->disc = (song.disc != 0) ? song.disc : 0;
result->coverart_id = (song.coverArtId != 0) ? song.coverArtId : 0;
return result;
}
model::Song DtoConversions::toSong(dto::SongDto::ObjectWrapper& songDto) { model::Song DtoConversions::toSong(dto::SongDto::ObjectWrapper& songDto) {
model::Song song; model::Song song;
song.id = (songDto->id.getPtr() == nullptr) ? 0 : songDto->id->getValue(); song.id = (songDto->id.getPtr() == nullptr) ? 0 : songDto->id->getValue();
song.title = (songDto->title == nullptr) ? "" : songDto->title->c_str(); song.title = (songDto->title == nullptr) ? "" : songDto->title->c_str();
song.album = (songDto->album == nullptr) ? "" : songDto->album->c_str(); song.album = (songDto->album == nullptr) ? "" : songDto->album->c_str();
song.artist = (songDto->artist == nullptr) ? "" : songDto->artist->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.genre = (songDto->genre == nullptr) ? "" : songDto->genre->c_str();
song.year = (songDto->year.getPtr() == nullptr) ? 0 : songDto->year->getValue(); song.year = (songDto->year.getPtr() == nullptr) ? 0 : songDto->year->getValue();
song.track = (songDto->track.getPtr() == nullptr) ? 0 : songDto->track->getValue(); song.track = (songDto->track.getPtr() == nullptr) ? 0 : songDto->track->getValue();
song.disc = (songDto->disc.getPtr() == nullptr) ? 0 : songDto->disc->getValue(); song.disc = (songDto->disc.getPtr() == nullptr) ? 0 : songDto->disc->getValue();
song.coverArtId = (songDto->coverart_id.getPtr() == nullptr) ?
0 : songDto->coverart_id->getValue();
return song; return song;
} }
+47 -56
View File
@@ -6,76 +6,67 @@
#include "model/Models.h" #include "model/Models.h"
#include "type/AlbumFilter.h" #include "type/AlbumFilter.h"
manager::AlbumManager::AlbumManager(const model::BinaryPath& bConf) namespace manager {
: m_bConf(bConf) AlbumManager::AlbumManager(const model::BinaryPath& bConf) : m_bConf(bConf) { }
{ }
model::Album manager::AlbumManager::retrieveAlbum(model::Album& album) model::Album AlbumManager::retrieveAlbum(model::Album& album) {
{ database::AlbumRepository albRepo(m_bConf);
database::AlbumRepository albRepo(m_bConf); album = std::move(albRepo.retrieveRecord(album, type::AlbumFilter::title));
album = std::move(albRepo.retrieveRecord(album, type::AlbumFilter::title));
return album; return album;
} }
model::Album manager::AlbumManager::saveAlbum(const model::Song& song) model::Album AlbumManager::saveAlbum(const model::Song& song) {
{ model::Album album(song);
model::Album album;
album.title = song.album;
album.year = song.year;
database::AlbumRepository albRepo(m_bConf); database::AlbumRepository albRepo(m_bConf);
if (!albRepo.doesAlbumExists(album, type::AlbumFilter::title)) { // TODO: check for existence with the title and the artist
albRepo.saveAlbum(album); if (!albRepo.doesAlbumExists(album, type::AlbumFilter::title)) {
} else { albRepo.saveAlbum(album);
std::cout << "album record already exists in the database" << std::endl; } else {
std::cout << "album record already exists in the database\n";
}
return album;
} }
return album; void AlbumManager::deleteAlbum(const model::Song& song) {
} model::Album album(song);
database::AlbumRepository albRepo(m_bConf);
void manager::AlbumManager::deleteAlbum(const model::Song& song) auto albWSC = albRepo.retrieveRecordWithSongCount(album, type::AlbumFilter::id);
{
model::Album album(song);
database::AlbumRepository albRepo(m_bConf);
auto albWSC = albRepo.retrieveRecordWithSongCount(album, type::AlbumFilter::id);
if (albWSC.second > 1) { if (albWSC.second > 1) {
std::cout << "album still contain songs related to it, will not delete" << std::endl; std::cout << "album still contain songs related to it, will not delete\n";
return; return;
}
std::cout << "safe to delete the album record\n";
albRepo.deleteAlbum(album, type::AlbumFilter::id);
} }
std::cout << "safe to delete the album record" << std::endl; void AlbumManager::updateAlbum(model::Song& updatedSong,
albRepo.deleteAlbum(album, type::AlbumFilter::id); const model::Song& currSong) {
} model::Album album(updatedSong);
void manager::AlbumManager::updateAlbum(model::Song& updatedSong, database::AlbumRepository albRepo(m_bConf);
const model::Song& currSong) if (!albRepo.doesAlbumExists(album, type::AlbumFilter::title)) {
{ std::cout << "album record does not exist\n";
model::Album album; albRepo.saveAlbum(album);
album.title = updatedSong.album; } else {
album.year = updatedSong.year; std::cout << "album record already exists\n";
}
database::AlbumRepository albRepo(m_bConf); album = albRepo.retrieveRecord(album, type::AlbumFilter::title);
if (!albRepo.doesAlbumExists(album, type::AlbumFilter::title)) { updatedSong.albumId = album.id;
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); void AlbumManager::printAlbum(const model::Album& album) {
updatedSong.albumId = album.id; std::cout << "\nalbum record\n";
} std::cout << "id: " << album.id << "\n";
std::cout << "title: " << album.title << "\n";
void manager::AlbumManager::printAlbum(const model::Album& album) std::cout << "year: " << album.year << "\n";
{ }
std::cout << "\nalbum record" << std::endl;
std::cout << "id: " << album.id << std::endl;
std::cout << "title: " << album.title << std::endl;
std::cout << "year: " << album.year << std::endl;
} }
+53 -57
View File
@@ -5,76 +5,72 @@
#include "database/ArtistRepository.h" #include "database/ArtistRepository.h"
#include "type/ArtistFilter.h" #include "type/ArtistFilter.h"
manager::ArtistManager::ArtistManager(const model::BinaryPath& bConf) namespace manager {
: m_bConf(bConf)
{ } ArtistManager::ArtistManager(const model::BinaryPath& bConf) : m_bConf(bConf) { }
model::Artist manager::ArtistManager::retrieveArtist(model::Artist& artist) model::Artist ArtistManager::retrieveArtist(model::Artist& artist) {
{ std::cout << "retrieving artist record\n";
std::cout << "retrieving artist record" << std::endl; database::ArtistRepository artRepo(m_bConf);
database::ArtistRepository artRepo(m_bConf); std::cout << "initialized artist repo\n";
std::cout << "initialized artist repo" << std::endl; std::cout << artist.artist << "\n";
std::cout << artist.artist << std::endl; artist = artRepo.retrieveRecord(artist, type::ArtistFilter::artist);
artist = artRepo.retrieveRecord(artist, type::ArtistFilter::artist);
return artist; return artist;
}
model::Artist manager::ArtistManager::saveArtist(const model::Song& song)
{
model::Artist artist;
artist.artist = song.artist;
database::ArtistRepository artRepo(m_bConf);
if (!artRepo.doesArtistExist(artist, type::ArtistFilter::artist)) {
artRepo.saveRecord(artist);
} else {
std::cout << "artist already exists" << std::endl;
} }
return artist; model::Artist ArtistManager::saveArtist(const model::Song& song) {
} model::Artist artist;
artist.artist = song.artist;
database::ArtistRepository artRepo(m_bConf);
if (!artRepo.doesArtistExist(artist, type::ArtistFilter::artist)) {
artRepo.saveRecord(artist);
} else {
std::cout << "artist already exists\n";
}
return artist;
}
void manager::ArtistManager::deleteArtist(const model::Song& song) void ArtistManager::deleteArtist(const model::Song& song) {
{ model::Artist artist(song);
model::Artist artist(song);
database::ArtistRepository artRepo(m_bConf); database::ArtistRepository artRepo(m_bConf);
auto artWSC = artRepo.retrieveRecordWithSongCount(artist, type::ArtistFilter::id); auto artWSC = artRepo.retrieveRecordWithSongCount(artist, type::ArtistFilter::id);
if (artWSC.second > 1) { if (artWSC.second > 1) {
std::cout << "artist still contain songs related to it"; std::cout << "artist still contain songs related to it";
std::cout << ", not delete" << std::endl; std::cout << ", not delete\n";
return; return;
}
std::cout << "safe to delete the artist record\n";
artRepo.deleteArtist(artist, type::ArtistFilter::id);
} }
std::cout << "safe to delete the artist record" << std::endl; void ArtistManager::updateArtist(model::Song& updatedSong,
artRepo.deleteArtist(artist, type::ArtistFilter::id); const model::Song& currSong) {
} model::Artist artist;
artist.artist = updatedSong.artist;
void manager::ArtistManager::updateArtist(model::Song& updatedSong, database::ArtistRepository artRepo(m_bConf);
const model::Song& currSong) if (!artRepo.doesArtistExist(artist, type::ArtistFilter::artist)) {
{ std::cout << "artist record does not exist\n";
model::Artist artist; artRepo.saveRecord(artist);
artist.artist = updatedSong.artist; } else {
std::cout << "artist record already exists\n";
}
database::ArtistRepository artRepo(m_bConf); artist = artRepo.retrieveRecord(artist, type::ArtistFilter::artist);
if (!artRepo.doesArtistExist(artist, type::ArtistFilter::artist)) { updatedSong.artistId = artist.id;
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); void ArtistManager::printArtist(const model::Artist& artist) {
updatedSong.artistId = artist.id; std::cout << "\nartist record" << "\n";
} std::cout << "id: " << artist.id << "\n";
std::cout << "artist: " << artist.artist << "\n";
void manager::ArtistManager::printArtist(const model::Artist& artist) }
{
std::cout << "\nartist record" << std::endl;
std::cout << "id: " << artist.id << std::endl;
std::cout << "artist: " << artist.artist << std::endl;
} }
+98 -105
View File
@@ -11,132 +11,125 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
manager::CoverArtManager::CoverArtManager(const std::string& configPath) : path(configPath) namespace manager {
{ } CoverArtManager::CoverArtManager(const model::BinaryPath& bConf) : m_bConf(bConf) { }
manager::CoverArtManager::CoverArtManager(const model::BinaryPath& bConf) : m_bConf(bConf)
{ }
model::Cover manager::CoverArtManager::saveCover(const model::Song& song) model::Cover CoverArtManager::saveCover(const model::Song& song) {
{ auto pathConfigContent = DirectoryManager::pathConfigContent(m_bConf);
auto pathConfigContent = manager::DirectoryManager::pathConfigContent(m_bConf); auto stockCoverPath = DirectoryManager::configPath(m_bConf);
auto stockCoverPath = manager::DirectoryManager::configPath(m_bConf); stockCoverPath.append("/CoverArt.png");
stockCoverPath.append("/CoverArt.png");
utility::MetadataRetriever meta; utility::MetadataRetriever meta;
model::Cover cov; model::Cover cov;
cov.songTitle = song.title; cov.songTitle = song.title;
if (meta.songContainsCoverArt(song)) { if (meta.songContainsCoverArt(song)) {
cov.imagePath = createImagePath(song); cov.imagePath = createImagePath(song);
cov = meta.applyCoverArt(song, cov); cov = meta.applyCoverArt(song, cov);
} else { } else {
cov.imagePath = pathConfigContent["cover_root_path"].get<std::string>(); cov.imagePath = pathConfigContent["cover_root_path"].get<std::string>();
cov = meta.applyStockCoverArt(song, cov, stockCoverPath); cov = meta.applyStockCoverArt(song, cov, stockCoverPath);
}
database::CoverArtRepository covRepo(m_bConf);
if (!covRepo.doesCoverArtExist(cov, type::CoverFilter::songTitle)) {
std::cout << "saving image record to the database\n";
covRepo.saveRecord(cov);
} else {
std::cout << "cover art record already exists\n";
}
std::cout << "retrieving image record from database\n";
cov = covRepo.retrieveRecord(cov, type::CoverFilter::songTitle);
return cov;
} }
database::CoverArtRepository covRepo(m_bConf);
if (!covRepo.doesCoverArtExist(cov, type::CoverFilter::songTitle)) { std::pair<bool, std::string> CoverArtManager::defaultCover(
std::cout << "saving image record to the database" << std::endl; const model::Cover& cover) {
covRepo.saveRecord(cov);
} else { auto paths = DirectoryManager::pathConfigContent(m_bConf);
std::cout << "cover art record already exists" << std::endl; const auto coverArtPath =
paths[DirectoryManager::retrievePathType(
type::PathType::coverArt)].get<std::string>();
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);
}
} }
std::cout << "retrieving image record from database" << std::endl;
cov = covRepo.retrieveRecord(cov, type::CoverFilter::songTitle);
return cov; void CoverArtManager::deleteCover(const model::Song& song) {
} database::CoverArtRepository covRepo(m_bConf);
model::Cover cov(song.coverArtId);
std::pair<bool, std::string> manager::CoverArtManager::defaultCover( cov = covRepo.retrieveRecord(cov, type::CoverFilter::id);
const model::Cover& cover) { covRepo.deleteRecord(cov);
auto paths = manager::DirectoryManager::pathConfigContent(m_bConf); auto result = defaultCover(cov);
const auto coverArtPath = if (!result.first) {
paths[manager::DirectoryManager::retrievePathType( fs::remove(cov.imagePath);
type::PathType::coverArt)].get<std::string>(); std::cout << "deleting cover art\n";
const auto coverArtPath = result.second;
} else {
std::cout << "song contains the stock cover art, will not delete\n";
return;
}
auto stockCoverArtPath = coverArtPath; DirectoryManager::deleteDirectories(song, result.second);
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);
model::Cover cov(song.coverArtId);
cov = covRepo.retrieveRecord(cov, type::CoverFilter::id);
covRepo.deleteRecord(cov);
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;
return;
} }
manager::DirectoryManager::deleteDirectories(song, result.second); void CoverArtManager::updateCover(const model::Song& updatedSong,
} const model::Song& currSong) {
database::CoverArtRepository covRepo(m_bConf);
model::Cover cover(updatedSong.coverArtId);
cover = covRepo.retrieveRecord(cover, type::CoverFilter::id);
void manager::CoverArtManager::updateCover(const model::Song& updatedSong, auto result = defaultCover(cover);
const model::Song& currSong)
{
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;
}
if (result.first) { auto imagePath = createImagePath(updatedSong);
return;
fs::copy(cover.imagePath, imagePath);
fs::remove(cover.imagePath);
DirectoryManager::deleteDirectories(currSong, result.second);
} }
auto imagePath = createImagePath(updatedSong); void CoverArtManager::updateCoverRecord(const model::Song& updatedSong) {
model::Cover updatedCover(updatedSong);
auto updatedImagePath = createImagePath(updatedSong);
fs::copy(cover.imagePath, imagePath); database::CoverArtRepository covRepo(m_bConf);
fs::remove(cover.imagePath); covRepo.updateRecord(updatedCover);
manager::DirectoryManager::deleteDirectories(currSong, result.second);
}
void manager::CoverArtManager::updateCoverRecord(const model::Song& updatedSong)
{
model::Cover updatedCover(updatedSong);
auto updatedImagePath = createImagePath(updatedSong);
database::CoverArtRepository covRepo(m_bConf);
covRepo.updateRecord(updatedCover);
}
std::string manager::CoverArtManager::createImagePath(const model::Song& song)
{
auto imagePath = manager::DirectoryManager::createDirectoryProcess(
song, m_bConf, type::PathType::coverArt);
if (song.track != 0) {
imagePath.append("track");
auto trackNum = (song.track > 9) ?
std::to_string(song.track) : "0" + std::to_string(song.track);
imagePath.append(trackNum);
} else {
imagePath.append(song.title);
} }
imagePath.append(".png");
return imagePath;
std::string CoverArtManager::createImagePath(const model::Song& song) {
auto imagePath = DirectoryManager::createDirectoryProcess(
song, m_bConf, type::PathType::coverArt);
if (song.track != 0) {
imagePath.append("track");
auto trackNum = (song.track > 9) ?
std::to_string(song.track) : "0" + std::to_string(song.track);
imagePath.append(trackNum);
} else {
imagePath.append(song.title);
}
imagePath.append(".png");
return imagePath;
}
} }
+202 -152
View File
@@ -7,186 +7,236 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace manager {
std::string DirectoryManager::createDirectoryProcess(const model::Song& song,
const std::string& rootPath) {
auto currPath = fs::path(rootPath);
std::string manager::DirectoryManager::createDirectoryProcess(model::Song song, const std::string& rootPath) if (fs::exists(currPath)) {
{ std::cout << "path exists\n";
auto currPath = fs::path(rootPath); } else {
std::cout << "creating path\n";
fs::create_directory(currPath);
}
if (fs::exists(currPath)) { auto artPath = fs::path(currPath.string() + song.artist);
std::cout << "path exists" << std::endl; if (fs::exists(artPath)) {
} else { std::cout << "artist path exists\n";
std::cout << "creating path" << std::endl; } else {
fs::create_directory(currPath); std::cout << "creating artist path\n";
fs::create_directory(artPath);
}
auto albPath = fs::path(artPath.string() + "/" + song.albumArtist);
if (fs::exists(albPath)) {
std::cout << "album path exists\n";
} else {
std::cout << "creating album path\n";
fs::create_directory(albPath);
}
auto discPath = DirectoryManager().relativeDiscSongPathFilesystem(albPath, song);
if (fs::exists(discPath)) {
std::cout << "disc path exists\n";
} else {
std::cout << "creating disc path\n";
fs::create_directory(discPath);
}
return discPath.string() + "/";
} }
auto artPath = fs::path(currPath.string() + song.artist); std::string DirectoryManager::createDirectoryProcess(const model::Song& song,
if (fs::exists(artPath)) { const model::BinaryPath& bConf, type::PathType pType) {
std::cout << "artist path exists" << std::endl; auto path = pathConfigContent(bConf)[retrievePathType(pType)];
} else { auto rootPath = path.get<std::string>();
std::cout << "creating artist path" << std::endl; auto currPath = fs::path(rootPath);
fs::create_directory(artPath);
if (fs::exists(currPath)) {
std::cout << "path exists\n";
} else {
std::cout << "creating root music path\n";
fs::create_directory(currPath);
}
auto artPath = fs::path(currPath.string() + song.albumArtist);
if (fs::exists(artPath)) {
std::cout << "artist path exists\n";
} else {
std::cout << "creating artist path\n";
fs::create_directory(artPath);
}
auto albPath = fs::path(artPath.string() + "/" + song.album);
if (fs::exists(albPath)) {
std::cout << "album path exists\n";
} else {
std::cout << "creating album path\n";
fs::create_directory(albPath);
}
auto discPath = DirectoryManager().relativeDiscSongPathFilesystem(albPath, song);
if (fs::exists(discPath)) {
std::cout << "disc path exists\n";
} else {
std::cout << "creating disc path\n";
fs::create_directory(discPath);
}
return discPath.string() + "/";
} }
auto albPath = fs::path(artPath.string() + "/" + song.album); std::string DirectoryManager::configPath(std::string_view path) {
if (fs::exists(albPath)) { return fs::canonical(path).parent_path().string();
std::cout << "album path exists" << std::endl;
} else {
std::cout << "creating album path" << std::endl;
fs::create_directory(albPath);
} }
return albPath.string() + "/"; std::string DirectoryManager::configPath(const model::BinaryPath& bConf) {
} return fs::canonical(bConf.path).parent_path().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<std::string>();
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); std::string DirectoryManager::contentOfPath(const std::string& path) {
if (fs::exists(artPath)) { std::fstream a(path, std::ios::in);
std::cout << "artist path exists" << std::endl; std::stringstream s;
} else { s << a.rdbuf();
std::cout << "creating artist path" << std::endl; a.close();
fs::create_directory(artPath);
return s.str();
} }
auto albPath = fs::path(artPath.string() + "/" + song.album); std::string DirectoryManager::retrievePathType(type::PathType pType) {
if (fs::exists(albPath)) { std::string path;
std::cout << "album path exists" << std::endl; switch (pType) {
} else { case type::PathType::music:
std::cout << "creating album path" << std::endl; path = "root_music_path";
fs::create_directory(albPath); 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;
} }
return albPath.string() + "/";
}
std::string manager::DirectoryManager::configPath(std::string_view path) nlohmann::json DirectoryManager::credentialConfigContent(const model::BinaryPath& bConf) {
{ auto path = configPath(bConf);
return fs::canonical(path).parent_path().string(); path.append("/authcredentials.json");
}
std::string manager::DirectoryManager::configPath(const model::BinaryPath& bConf) return nlohmann::json::parse(contentOfPath(path));
{
return fs::canonical(bConf.path).parent_path().string();
}
std::string manager::DirectoryManager::contentOfPath(const std::string& path)
{
std::fstream a(path, std::ios::in);
std::stringstream s;
s << a.rdbuf();
a.close();
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 DirectoryManager::databaseConfigContent(const model::BinaryPath& bConf) {
} auto path = configPath(bConf);
path.append("/database.json");
return nlohmann::json::parse(contentOfPath(path));
}
nlohmann::json DirectoryManager::pathConfigContent(const model::BinaryPath& bConf) {
auto path = configPath(bConf);
path.append("/paths.json");
return nlohmann::json::parse(contentOfPath(path));
}
nlohmann::json manager::DirectoryManager::credentialConfigContent(const model::BinaryPath& bConf) void DirectoryManager::deleteDirectories(model::Song song, const std::string& rootPath) {
{ std::cout << "checking for empty directories to delete\n";
auto path = configPath(bConf); const std::string art(rootPath + std::string("/") + song.albumArtist);
path.append("/authcredentials.json"); const std::string alb(art + "/" + song.album);
const std::string disc(alb + "/" + std::to_string(song.disc) + "/");
return nlohmann::json::parse(contentOfPath(path)); auto discPath = fs::path(disc);
}
nlohmann::json manager::DirectoryManager::databaseConfigContent(const model::BinaryPath& bConf) if (fs::exists(discPath)) {
{ std::cout << "disc directory does not exist\n";
auto path = configPath(bConf); } else if (fs::is_empty(discPath)) {
path.append("/database.json"); fs::remove(discPath);
}
return nlohmann::json::parse(contentOfPath(path)); auto albPath = fs::path(alb);
}
nlohmann::json manager::DirectoryManager::pathConfigContent(const model::BinaryPath& bConf)
{
auto path = configPath(bConf);
path.append("/paths.json");
return nlohmann::json::parse(contentOfPath(path));
}
void manager::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 alb(art + "/" + song.album);
auto albPath = fs::path(alb);
if (!fs::exists(albPath)) { if (!fs::exists(albPath)) {
std::cout << "directory does not exists" << std::endl; std::cout << "directory does not exists\n";
} else if (fs::is_empty(albPath)) { } else if (fs::is_empty(albPath)) {
fs::remove(albPath); fs::remove(albPath);
} }
auto artPath = fs::path(art); auto artPath = fs::path(art);
if (!fs::exists(artPath)) { if (!fs::exists(artPath)) {
std::cout << "directory does not exists" << std::endl; std::cout << "directory does not exists\n";
return; return;
} else if (fs::is_empty(artPath)) { } else if (fs::is_empty(artPath)) {
fs::remove(artPath); fs::remove(artPath);
}
std::cout << "deleted empty directory or directories\n";
} }
std::cout << "deleted empty directory or directories" << std::endl; void DirectoryManager::deleteCoverArtFile(const std::string& covPath,
} const std::string& stockCoverPath) {
if (covPath.compare(stockCoverPath) == 0) {
void manager::DirectoryManager::deleteCoverArtFile(const std::string& covPath, const std::string& stockCoverPath) std::cout << "cover has stock cover art, will not deleted\n";
{ } else {
if (covPath.compare(stockCoverPath) == 0) { std::cout << "deleting song path\n";
std::cout << "cover has stock cover art, will not deleted" << std::endl; auto cov = fs::path(covPath);
} else { fs::remove(cov);
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;
auto songPath = fs::path(song.songPath);
if (!fs::exists(songPath)) {
std::cout << "song does not exists" << std::endl;
return;
} }
fs::remove(songPath);
std::cout << "deleted song" << std::endl;
}
fs::path DirectoryManager::relativeDiscSongPathFilesystem(const fs::path& albPath,
const model::Song& song) {
std::string albPathStr(albPath.string() + "/disc");
if (song.disc >= 10) {
albPathStr.append(std::to_string(song.disc));
} else {
albPathStr.append("0");
albPathStr.append(std::to_string(song.disc));
}
albPathStr.append("/");
auto relPath = fs::path(albPathStr.c_str());
return relPath;
}
std::string DirectoryManager::relativeDiscSongPath(const fs::path& albPath,
const model::Song& song) {
std::string albPathStr(albPath.string() + "/disc");
if (song.disc >= 10) {
albPathStr.append(std::to_string(song.disc));
} else {
albPathStr.append("0");
albPathStr.append(std::to_string(song.disc));
}
albPathStr.append("/");
return albPathStr;
}
void DirectoryManager::deleteSong(const model::Song song) {
std::cout << "deleting song\n";
auto songPath = fs::path(song.songPath);
if (!fs::exists(songPath)) {
std::cout << "song does not exists\n";
return;
}
fs::remove(songPath);
std::cout << "deleted song" << "\n";
}
}
+51 -56
View File
@@ -5,73 +5,68 @@
#include "database/GenreRepository.h" #include "database/GenreRepository.h"
#include "type/GenreFilter.h" #include "type/GenreFilter.h"
manager::GenreManager::GenreManager(const model::BinaryPath& bConf) namespace manager {
: m_bConf(bConf) GenreManager::GenreManager(const model::BinaryPath& bConf) : m_bConf(bConf) { }
{ }
model::Genre manager::GenreManager::retrieveGenre(model::Genre& genre) model::Genre GenreManager::retrieveGenre(model::Genre& genre) {
{ database::GenreRepository gnrRepo(m_bConf);
database::GenreRepository gnrRepo(m_bConf); genre = gnrRepo.retrieveRecord(genre, type::GenreFilter::category);
genre = gnrRepo.retrieveRecord(genre, type::GenreFilter::category);
return genre; return genre;
}
model::Genre manager::GenreManager::saveGenre(const model::Song& song)
{
model::Genre genre;
genre.category = song.genre;
database::GenreRepository gnrRepo(m_bConf);
if (!gnrRepo.doesGenreExist(genre, type::GenreFilter::category)) {
gnrRepo.saveRecord(genre);
} else {
std::cout << "genre record already exists" << std::endl;
} }
return genre; model::Genre GenreManager::saveGenre(const model::Song& song) {
} model::Genre genre;
genre.category = song.genre;
database::GenreRepository gnrRepo(m_bConf);
if (!gnrRepo.doesGenreExist(genre, type::GenreFilter::category)) {
gnrRepo.saveRecord(genre);
} else {
std::cout << "genre record already exists\n";
}
return genre;
void manager::GenreManager::deleteGenre(const model::Song& song)
{
model::Genre genre(song);
database::GenreRepository gnrRepo(m_bConf);
auto gnrWSC = gnrRepo.retrieveRecordWithSongCount(genre, type::GenreFilter::id);
if (gnrWSC.second > 1) {
std::cout << "genre still contain songs related to it";
std::cout << ", will not delete" << std::endl;
return;
} }
std::cout << "safe to delete the genre record" << std::endl;
gnrRepo.deleteRecord(genre, type::GenreFilter::id);
}
void manager::GenreManager::updateGenre(model::Song& updatedSong, void GenreManager::deleteGenre(const model::Song& song) {
const model::Song& currSong) model::Genre genre(song);
{
model::Genre genre;
genre.category = updatedSong.genre;
database::GenreRepository gnrRepo(m_bConf); database::GenreRepository gnrRepo(m_bConf);
if (!gnrRepo.doesGenreExist(genre, type::GenreFilter::category)) { auto gnrWSC = gnrRepo.retrieveRecordWithSongCount(genre, type::GenreFilter::id);
std::cout << "genre record does not exist" << std::endl;
gnrRepo.saveRecord(genre); if (gnrWSC.second > 1) {
} else { std::cout << "genre still contain songs related to it";
std::cout << "genre record already exists" << std::endl; std::cout << ", will not delete\n";
return;
}
std::cout << "safe to delete the genre record\n";
gnrRepo.deleteRecord(genre, type::GenreFilter::id);
} }
genre = gnrRepo.retrieveRecord(genre, type::GenreFilter::category); void GenreManager::updateGenre(model::Song& updatedSong,
updatedSong.genreId = genre.id; const model::Song& currSong) {
} model::Genre genre;
genre.category = updatedSong.genre;
void manager::GenreManager::printGenre(const model::Genre& genre) database::GenreRepository gnrRepo(m_bConf);
{ if (!gnrRepo.doesGenreExist(genre, type::GenreFilter::category)) {
std::cout << "genre record" << std::endl; std::cout << "genre record does not exist\n";
std::cout << "id: " << genre.id << std::endl; gnrRepo.saveRecord(genre);
std::cout << "category: " << genre.category << std::endl; } else {
std::cout << "genre record already exists\n";
}
genre = gnrRepo.retrieveRecord(genre, type::GenreFilter::category);
updatedSong.genreId = genre.id;
}
void GenreManager::printGenre(const model::Genre& genre) {
std::cout << "genre record\n";
std::cout << "id: " << genre.id << "\n";
std::cout << "category: " << genre.category << "\n";
}
} }
+368 -393
View File
@@ -5,7 +5,7 @@
#include <random> #include <random>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include "oatpp/web/protocol/http/outgoing/ChunkedBody.hpp" //#include "oatpp/web/protocol/http/outgoing/ChunkedBody.hpp"
#include "oatpp/web/server/api/ApiController.hpp" #include "oatpp/web/server/api/ApiController.hpp"
#include "database/CoverArtRepository.h" #include "database/CoverArtRepository.h"
@@ -21,427 +21,402 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
manager::SongManager::SongManager(std::string& x_path) namespace manager {
: exe_path(x_path) SongManager::SongManager(const model::BinaryPath& bConf) : m_bConf(bConf) { }
{ }
manager::SongManager::SongManager(const model::BinaryPath& bConf)
: m_bConf(bConf)
{ }
bool manager::SongManager::didSongChange(const model::Song& updatedSong, std::pair<bool, type::SongUpload> SongManager::saveSong(model::Song& song) {
const model::Song& currSong) saveSongTemp(song);
{ utility::MetadataRetriever meta;
if (!updatedSong.title.empty()) { auto data = std::move(song.data);
return true; song = meta.retrieveMetadata(song);
} song.data = std::move(data);
if (!updatedSong.artist.empty()) {
return true; database::SongRepository songRepo(m_bConf);
} if (songRepo.doesSongExist(song, type::SongFilter::titleAlbArtistAlbum)) {
if (!updatedSong.album.empty()) { std::cout << "\ntitle: " << song.title << "\nartist: " << song.artist << "\n";
return true; std::cout << "does not exist\n";
} return std::make_pair(false, type::SongUpload::AlreadyExist);
if (updatedSong.genre.empty()) { }
return true;
} saveMisc(song);
if (updatedSong.year != 0) {
return true; printSong(song);
songRepo.saveRecord(song);
song = songRepo.retrieveRecord(song, type::SongFilter::titleAlbArtistAlbum);
return std::make_pair(true, type::SongUpload::Successful);
} }
return false;
}
bool manager::SongManager::requiresFilesystemChange(const model::Song& updatedSong, bool SongManager::didSongChange(const model::Song& updatedSong,
const model::Song& currSong) const model::Song& currSong) {
{ if (!updatedSong.title.empty()) {
if (updatedSong.title.compare(currSong.title) != 0) { return true;
return true; }
} if (!updatedSong.artist.empty()) {
if (updatedSong.artist.compare(currSong.album) != 0) { return true;
return true; }
} if (!updatedSong.album.empty()) {
if (updatedSong.album.compare(currSong.genre) != 0) { return true;
return true; }
if (updatedSong.genre.empty()) {
return true;
}
if (updatedSong.year != 0) {
return true;
}
return false;
} }
return false; bool SongManager::requiresFilesystemChange(const model::Song& updatedSong,
const model::Song& currSong) {
if (updatedSong.title.compare(currSong.title) != 0) {
return true;
}
if (updatedSong.artist.compare(currSong.album) != 0) {
return true;
}
if (updatedSong.album.compare(currSong.genre) != 0) {
return true;
}
return false;
}
bool SongManager::deleteSong(model::Song& song) {
database::SongRepository songRepo(m_bConf);
if (!songRepo.doesSongExist(song, type::SongFilter::id)) {
std::cout << "song does not exist\n";
return false;
}
song = songRepo.retrieveRecord(song, type::SongFilter::id);
auto paths = DirectoryManager::pathConfigContent(m_bConf);
auto deleted = songRepo.deleteRecord(song);
if (!deleted) {
std::cout << "song not deleted from databases\n";
return deleted;
}
deleteMisc(song);
fs::remove(song.songPath);
DirectoryManager::deleteDirectories(song, paths["root_music_path"].get<std::string>());
return deleted;
}
bool SongManager::updateSong(model::Song& updatedSong) {
database::SongRepository songRepo(m_bConf);
model::Song currSong(updatedSong.id);
currSong = songRepo.retrieveRecord(currSong, type::SongFilter::id);
if (!didSongChange(updatedSong, currSong)) {
std::cout << "no change to the song\n";
return false;
}
assignMiscId(updatedSong, currSong);
auto changes = changesInSong(updatedSong, currSong);
utility::MetadataRetriever meta;
meta.updateMetadata(updatedSong, currSong);
assignMiscFields(changes, updatedSong, currSong);
if (requiresFilesystemChange(updatedSong, currSong)) {
modifySongOnFilesystem(updatedSong, currSong);
}
printSong(updatedSong);
printSong(currSong);
updateMisc(changes, updatedSong, currSong);
return true;
}
void SongManager::printSong(const model::Song& song) {
std::cout << "\nsong" << "\n";
std::cout << "title: " << song.title << "\n";
std::cout << "artist: " << song.artist << "\n";
std::cout << "album artist: " << song.albumArtist << "\n";
std::cout << "album: " << song.album << "\n";
std::cout << "genre: " << song.genre << "\n";
std::cout << "duration: " << song.duration << "\n";
std::cout << "year: " << song.year << "\n";
std::cout << "track: " << song.track << "\n";
std::cout << "disc: " << song.disc << "\n";
std::cout << "song path: " << song.songPath << "\n";
std::cout << "cover art id: " << song.coverArtId << "\n";
std::cout << "album id: " << song.albumId << "\n";
std::cout << "artist id: " << song.artistId << "\n";
std::cout << "genre id: " << song.genreId << "\n";
std::cout << "year id: " << song.yearId << "\n";
}
std::map<type::SongChanged, bool> SongManager::changesInSong(
const model::Song& updatedSong, const model::Song& currSong) {
std::map<type::SongChanged, bool> 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;
} }
void manager::SongManager::saveSong(model::Song& song) std::string SongManager::createSongPath(const model::Song& song) {
{ auto songPath = DirectoryManager::createDirectoryProcess(
saveSongTemp(song); song, m_bConf, type::PathType::music);
utility::MetadataRetriever meta;
auto data = std::move(song.data);
song = meta.retrieveMetadata(song.songPath);
song.data = std::move(data);
saveMisc(song); if (song.track != 0) {
songPath.append("track");
auto trackNum = (song.track > 9) ?
std::to_string(song.track) : "0" + std::to_string(song.track);
songPath.append(trackNum);
} else {
songPath.append(song.title);
}
songPath.append(".mp3");
printSong(song); return songPath;
database::SongRepository songRepo(m_bConf);
songRepo.saveRecord(song);
}
void manager::SongManager::deleteSong(model::Song& song)
{
database::SongRepository songRepo(m_bConf);
if (!songRepo.doesSongExist(song, type::SongFilter::id)) {
std::cout << "song does not exist" << std::endl;
return;
} }
song = songRepo.retrieveRecord(song, type::SongFilter::id);
auto paths = manager::DirectoryManager::pathConfigContent(m_bConf); // used to prevent empty values to appear in the updated song
void SongManager::assignMiscFields(
std::map<type::SongChanged, bool>& songChanges, model::Song& updatedSong,
const model::Song& currSong) {
std::cout << "assigning miscellanes fields to updated song\n";
updatedSong.track = currSong.track;
for (auto scIter = songChanges.begin(); scIter != songChanges.end(); ++scIter) {
type::SongChanged key = scIter->first;
bool changed = songChanges.at(key);
auto deleted = songRepo.deleteRecord(song); if (!changed) {
switch (key) {
if (!deleted) { case type::SongChanged::title:
std::cout << "song not deleted from databases" << std::endl; updatedSong.title = currSong.title;
return; std::cout << "title has not been changed\n";
} break;
deleteMisc(song); case type::SongChanged::artist:
updatedSong.artist = currSong.artist;
fs::remove(song.songPath); std::cout << "artist has not been changed\n";
manager::DirectoryManager::deleteDirectories(song, paths["root_music_path"].get<std::string>()); break;
} case type::SongChanged::album:
updatedSong.album = currSong.album;
void manager::SongManager::updateSong(model::Song& updatedSong) std::cout << "album has not been changed\n";
{ break;
database::SongRepository songRepo(m_bConf); case type::SongChanged::genre:
model::Song currSong(updatedSong.id); updatedSong.genre = currSong.genre;
std::cout << "genre has not been changed\n";
OATPP_ASSERT_HTTP(songRepo.doesSongExist(currSong, type::SongFilter::id) , oatpp::web::protocol::http::Status::CODE_404, "song does not exist"); break;
case::type::SongChanged::year:
currSong = songRepo.retrieveRecord(currSong, type::SongFilter::id); updatedSong.year = currSong.year;
if (!didSongChange(updatedSong, currSong)) { std::cout << "year has not been changed\n";
std::cout << "no change to the song" << std::endl; default:
return; break;
} }
assignMiscId(updatedSong, currSong);
auto changes = changesInSong(updatedSong, currSong);
utility::MetadataRetriever meta;
meta.updateMetadata(updatedSong, currSong);
assignMiscFields(changes, updatedSong, currSong);
if (requiresFilesystemChange(updatedSong, currSong)) {
modifySongOnFilesystem(updatedSong, currSong);
}
printSong(updatedSong);
printSong(currSong);
updateMisc(changes, updatedSong, currSong);
}
model::Song manager::SongManager::songDtoConv(dto::SongDto::ObjectWrapper& songDto)
{
std::cout << "coverting dto::SongDto to model::Song" << std::endl;
model::Song song;
song.id = songDto->id;
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.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();
song.disc = (songDto->disc.getPtr() == nullptr) ? 0 : songDto->disc->getValue();
return song;
}
void manager::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: " << song.album << std::endl;
std::cout << "genre: " << song.genre << std::endl;
std::cout << "duration: " << song.duration << std::endl;
std::cout << "year: " << song.year << std::endl;
std::cout << "track: " << song.track << std::endl;
std::cout << "disc: " << song.disc << std::endl;
std::cout << "song path: " << song.songPath << std::endl;
std::cout << "cover art id: " << song.coverArtId << std::endl;
std::cout << "album id: " << song.albumId << std::endl;
std::cout << "artist id: " << song.artistId << std::endl;
std::cout << "genre id: " << song.genreId << std::endl;
std::cout << "year id: " << song.yearId << std::endl;
}
std::map<type::SongChanged, bool> manager::SongManager::changesInSong(
const model::Song& updatedSong, const model::Song& currSong)
{
std::map<type::SongChanged, bool> 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;
}
std::string manager::SongManager::createSongPath(const model::Song& song)
{
auto songPath = manager::DirectoryManager::createDirectoryProcess(
song, m_bConf, type::PathType::music);
if (song.track != 0) {
songPath.append("track");
auto trackNum = (song.track > 9) ?
std::to_string(song.track) : "0" + std::to_string(song.track);
songPath.append(trackNum);
} else {
songPath.append(song.title);
}
songPath.append(".mp3");
return songPath;
}
// used to prevent empty values to appear in the updated song
void manager::SongManager::assignMiscFields(
std::map<type::SongChanged, bool>& songChanges, model::Song& updatedSong,
const model::Song& currSong)
{
std::cout << "assigning miscellanes fields to updated song" << std::endl;
updatedSong.track = currSong.track;
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 // used to dump miscellaneous id to the updated song
void manager::SongManager::assignMiscId(model::Song& updatedSong, void SongManager::assignMiscId(model::Song& updatedSong,
const model::Song& currSong) const model::Song& currSong) {
{ std::cout << "assigning miscellaneous Id's to updated song\n";
std::cout << "assigning miscellaneous Id's to updated song" << std::endl; updatedSong.artistId = currSong.artistId;
updatedSong.artistId = currSong.artistId; updatedSong.albumId = currSong.albumId;
updatedSong.albumId = currSong.albumId; updatedSong.genreId = currSong.genreId;
updatedSong.genreId = currSong.genreId; updatedSong.yearId = currSong.yearId;
updatedSong.yearId = currSong.yearId; updatedSong.coverArtId = currSong.coverArtId;
updatedSong.coverArtId = currSong.coverArtId;
}
// saves song to a temporary path
void manager::SongManager::saveSongTemp(model::Song& song)
{
auto config = manager::DirectoryManager::pathConfigContent(m_bConf);
auto tmpSongPath = config["temp_root_path"].get<std::string>();
std::random_device dev;
std::mt19937 rng(dev());
std::uniform_int_distribution<std::mt19937::result_type> dist(1,1000);
tmpSongPath.append(std::to_string(dist(rng)));
tmpSongPath.append(".mp3");
std::fstream s(tmpSongPath, std::fstream::binary | std::fstream::out);
s.write((char*)&song.data[0], song.data.size());
s.close();
song.songPath = tmpSongPath;
}
void manager::SongManager::saveMisc(model::Song& song)
{
CoverArtManager covMgr(m_bConf);
auto pathConfigContent = manager::DirectoryManager::pathConfigContent(m_bConf);
auto musicRootPath = pathConfigContent["root_music_path"].get<std::string>();
auto cov = covMgr.saveCover(song);
const auto songPath = createSongPath(song);
if (fs::exists(songPath)) {
std::cout << "deleting old song with the same metadata" << std::endl;
fs::remove(songPath);
}
std::cout << "copying song to the appropriate directory" << std::endl;
std::cout << song.songPath << std::endl;
std::cout << songPath << std::endl;
fs::copy(song.songPath, songPath);
fs::remove(song.songPath);
song.songPath = std::move(songPath);
std::cout << "copied song to the appropriate directory" << std::endl;
AlbumManager albMgr(m_bConf);
auto album = albMgr.saveAlbum(song);
album = albMgr.retrieveAlbum(album);
manager::AlbumManager::printAlbum(album);
ArtistManager artMgr(m_bConf);
auto artist = artMgr.saveArtist(song);
artist = artMgr.retrieveArtist(artist);
manager::ArtistManager::printArtist(artist);
GenreManager gnrMgr(m_bConf);
auto genre = gnrMgr.saveGenre(song);
genre = gnrMgr.retrieveGenre(genre);
manager::GenreManager::printGenre(genre);
YearManager yrMgr(m_bConf);
auto year = yrMgr.saveYear(song);
year = yrMgr.retrieveYear(year);
manager::YearManager::printYear(year);
song.coverArtId = cov.id;
song.albumId = album.id;
song.artistId = artist.id;
song.genreId = genre.id;
song.yearId = year.id;
std::cout << "done with miscellaneous database records" << std::endl;
}
void manager::SongManager::deleteMisc(const model::Song& song)
{
manager::CoverArtManager covMgr(m_bConf);
covMgr.deleteCover(song);
manager::AlbumManager albMgr(m_bConf);
albMgr.deleteAlbum(song);
manager::ArtistManager artMgr(m_bConf);
artMgr.deleteArtist(song);
manager::GenreManager gnrMgr(m_bConf);
gnrMgr.deleteGenre(song);
manager::YearManager yrMgr(m_bConf);
yrMgr.deleteYear(song);
}
// deletes miscellanes records
void manager::SongManager::deleteMiscExceptCoverArt(const model::Song& song)
{
manager::AlbumManager albMgr(m_bConf);
albMgr.deleteAlbum(song);
manager::ArtistManager artMgr(m_bConf);
artMgr.deleteArtist(song);
manager::GenreManager gnrMgr(m_bConf);
gnrMgr.deleteGenre(song);
manager::YearManager yrMgr(m_bConf);
yrMgr.deleteYear(song);
}
void manager::SongManager::updateMisc(
const std::map<type::SongChanged, bool>& 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 // saves song to a temporary path
if (titleChange || artistChange || albumChange) { void SongManager::saveSongTemp(model::Song& song) {
manager::CoverArtManager covMgr(m_bConf); auto config = DirectoryManager::pathConfigContent(m_bConf);
covMgr.updateCoverRecord(updatedSong);
auto tmpSongPath = config["temp_root_path"].get<std::string>();
std::random_device dev;
std::mt19937 rng(dev());
std::uniform_int_distribution<std::mt19937::result_type> dist(1,1000);
tmpSongPath.append(std::to_string(dist(rng)));
tmpSongPath.append(".mp3");
std::fstream s(tmpSongPath, std::fstream::binary | std::fstream::out);
s.write((char*)&song.data[0], song.data.size());
s.close();
song.songPath = tmpSongPath;
} }
database::SongRepository songRepo(m_bConf); void SongManager::saveMisc(model::Song& song) {
songRepo.updateRecord(updatedSong); CoverArtManager covMgr(m_bConf);
auto pathConfigContent = DirectoryManager::pathConfigContent(m_bConf);
auto musicRootPath = pathConfigContent["root_music_path"].get<std::string>();
deleteMiscExceptCoverArt(currSong); auto cov = covMgr.saveCover(song);
} const auto songPath = createSongPath(song);
void manager::SongManager::modifySongOnFilesystem(model::Song& updatedSong, if (fs::exists(songPath)) {
const model::Song& currSong) std::cout << "deleting old song with the same metadata\n";
{ fs::remove(songPath);
std::cout << "preparing to modify song" << std::endl; }
auto songPath = createSongPath(updatedSong); std::cout << "copying song to the appropriate directory\n";
updatedSong.songPath = std::move(songPath); std::cout << song.songPath << "\n";
std::cout << songPath << "\n";
fs::copy(song.songPath, songPath);
fs::remove(song.songPath);
song.songPath = std::move(songPath);
std::cout << "copied song to the appropriate directory\n";
std::cout << "new path " << updatedSong.songPath << std::endl; AlbumManager albMgr(m_bConf);
auto album = albMgr.saveAlbum(song);
album = albMgr.retrieveAlbum(album);
AlbumManager::printAlbum(album);
fs::copy(currSong.songPath, updatedSong.songPath); ArtistManager artMgr(m_bConf);
fs::remove(currSong.songPath); auto artist = artMgr.saveArtist(song);
artist = artMgr.retrieveArtist(artist);
ArtistManager::printArtist(artist);
auto paths = manager::DirectoryManager::pathConfigContent(m_bConf); GenreManager gnrMgr(m_bConf);
const auto musicRootPath = auto genre = gnrMgr.saveGenre(song);
paths[manager::DirectoryManager::retrievePathType( genre = gnrMgr.retrieveGenre(genre);
GenreManager::printGenre(genre);
YearManager yrMgr(m_bConf);
auto year = yrMgr.saveYear(song);
year = yrMgr.retrieveYear(year);
YearManager::printYear(year);
song.coverArtId = cov.id;
song.albumId = album.id;
song.artistId = artist.id;
song.genreId = genre.id;
song.yearId = year.id;
std::cout << "done with miscellaneous database records\n";
}
void SongManager::deleteMisc(const model::Song& song) {
CoverArtManager covMgr(m_bConf);
covMgr.deleteCover(song);
AlbumManager albMgr(m_bConf);
albMgr.deleteAlbum(song);
ArtistManager artMgr(m_bConf);
artMgr.deleteArtist(song);
GenreManager gnrMgr(m_bConf);
gnrMgr.deleteGenre(song);
YearManager yrMgr(m_bConf);
yrMgr.deleteYear(song);
}
// deletes miscellanes records
void SongManager::deleteMiscExceptCoverArt(const model::Song& song) {
AlbumManager albMgr(m_bConf);
albMgr.deleteAlbum(song);
ArtistManager artMgr(m_bConf);
artMgr.deleteArtist(song);
GenreManager gnrMgr(m_bConf);
gnrMgr.deleteGenre(song);
YearManager yrMgr(m_bConf);
yrMgr.deleteYear(song);
}
void SongManager::updateMisc(
const std::map<type::SongChanged, bool>& 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) {
ArtistManager artMgr(m_bConf);
artMgr.updateArtist(updatedSong, currSong);
}
if (albumChange) {
AlbumManager albMgr(m_bConf);
albMgr.updateAlbum(updatedSong, currSong);
}
if (genreChange) {
GenreManager gnrMgr(m_bConf);
gnrMgr.updateGenre(updatedSong, currSong);
}
if (yearChange) {
YearManager yrMgr(m_bConf);
yrMgr.updateYear(updatedSong, currSong);
}
// determins to update the cover art record
if (titleChange || artistChange || albumChange) {
CoverArtManager covMgr(m_bConf);
covMgr.updateCoverRecord(updatedSong);
}
database::SongRepository songRepo(m_bConf);
songRepo.updateRecord(updatedSong);
deleteMiscExceptCoverArt(currSong);
}
void SongManager::modifySongOnFilesystem(model::Song& updatedSong,
const model::Song& currSong) {
std::cout << "preparing to modify song\n";
auto songPath = createSongPath(updatedSong);
updatedSong.songPath = std::move(songPath);
std::cout << "new path " << updatedSong.songPath << "\n";
fs::copy(currSong.songPath, updatedSong.songPath);
fs::remove(currSong.songPath);
auto paths = DirectoryManager::pathConfigContent(m_bConf);
const auto musicRootPath =
paths[DirectoryManager::retrievePathType(
type::PathType::music)].get<std::string>(); type::PathType::music)].get<std::string>();
manager::DirectoryManager::deleteDirectories(currSong, musicRootPath); DirectoryManager::deleteDirectories(currSong, musicRootPath);
manager::CoverArtManager covMgr(m_bConf); CoverArtManager covMgr(m_bConf);
covMgr.updateCover(updatedSong, currSong); covMgr.updateCover(updatedSong, currSong);
}
} }
+128 -160
View File
@@ -14,191 +14,159 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace manager { namespace manager {
TokenManager::TokenManager() model::Token TokenManager::retrieveToken(const model::BinaryPath& bConf) {
{ auto cred = parseAuthCredentials(bConf);
} auto reqObj = createTokenBody(cred);
std::string uri{cred.uri};
uri.append("/");
uri.append(cred.endpoint);
model::Token TokenManager::retrieveToken(const model::BinaryPath& bConf) auto r = sendRequest(uri, reqObj);
{ auto postRes = nlohmann::json::parse(r.text);
auto cred = parseAuthCredentials(bConf);
auto reqObj = createTokenBody(cred);
std::string uri{cred.uri}; model::Token lr(std::move(postRes["access_token"].get<std::string>()),
uri.append("/"); std::move(postRes["token_type"].get<std::string>()),
uri.append(cred.endpoint); postRes["expires_in"].get<int>());
auto r = sendRequest(uri, reqObj); return lr;
auto postRes = nlohmann::json::parse(r.text);
model::Token lr(std::move(postRes["access_token"].get<std::string>()),
std::move(postRes["token_type"].get<std::string>()),
postRes["expires_in"].get<int>());
return lr;
}
bool TokenManager::isTokenValid(std::string& auth, type::Scope scope)
{
auto authPair = fetchAuthHeader(auth);
if (!std::get<0>(authPair)) {
std::cout << "no Bearer found" << std::endl;
return std::get<0>(authPair);
} }
auto authHeader = std::get<1>(authPair);
auto token = authHeader.at(authHeader.size()-1); bool TokenManager::isTokenValid(std::string& auth, type::Scope scope) {
auto authPair = fetchAuthHeader(auth);
auto scopes = extractScopes(jwt::decode(token)); if (!std::get<0>(authPair)) {
std::cout << "no Bearer found\n";
switch (scope) { return std::get<0>(authPair);
case type::Scope::upload: }
return tokenSupportsScope(scopes, "upload:songs");
case type::Scope::download: auto authHeader = std::get<1>(authPair);
return tokenSupportsScope(scopes, "download:songs");
case type::Scope::stream: auto token = authHeader.at(authHeader.size()-1);
return tokenSupportsScope(scopes, "stream:songs");
case type::Scope::deleteSong: auto scopes = extractScopes(jwt::decode(token));
return tokenSupportsScope(scopes, "delete:songs");
case type::Scope::updateSong: switch (scope) {
return tokenSupportsScope(scopes, "update:songs"); case type::Scope::upload:
case type::Scope::retrieveSong: return tokenSupportsScope(scopes, "upload:songs");
return tokenSupportsScope(scopes, "read:song_details"); case type::Scope::download:
case type::Scope::retrieveAlbum: return tokenSupportsScope(scopes, "download:songs");
return tokenSupportsScope(scopes, "read:albums"); case type::Scope::stream:
case type::Scope::retrieveArtist: return tokenSupportsScope(scopes, "stream:songs");
return tokenSupportsScope(scopes, "read:artists"); case type::Scope::deleteSong:
case type::Scope::retrieveGenre: return tokenSupportsScope(scopes, "delete:songs");
return tokenSupportsScope(scopes, "read:genre"); case type::Scope::updateSong:
case type::Scope::retrieveYear: return tokenSupportsScope(scopes, "update:songs");
return tokenSupportsScope(scopes, "read:year"); case type::Scope::retrieveSong:
case type::Scope::downloadCoverArt: return tokenSupportsScope(scopes, "read:song_details");
return tokenSupportsScope(scopes, "download:cover_art"); case type::Scope::retrieveAlbum:
default: return tokenSupportsScope(scopes, "read:albums");
break; case type::Scope::retrieveArtist:
return tokenSupportsScope(scopes, "read:artists");
case type::Scope::retrieveGenre:
return tokenSupportsScope(scopes, "read:genre");
case type::Scope::retrieveYear:
return tokenSupportsScope(scopes, "read:year");
case type::Scope::downloadCoverArt:
return tokenSupportsScope(scopes, "download:cover_art");
default:
break;
}
return false;
} }
return false; bool TokenManager::testAuth(const model::BinaryPath& bConf) {
} auto cred = parseAuthCredentials(bConf);
auto reqObj = createTokenBody(cred);
bool TokenManager::testAuth(const model::BinaryPath& bConf) std::string uri(cred.uri);
{ uri.append("/");
auto cred = parseAuthCredentials(bConf); uri.append(cred.endpoint);
auto reqObj = createTokenBody(cred);
std::string uri(cred.uri); auto response = sendRequest(uri, reqObj);
uri.append("/");
uri.append(cred.endpoint);
auto response = sendRequest(uri, reqObj); return (response.status_code == 200) ? true : false;
}
return (response.status_code == 200) ? true : false;
}
cpr::Response TokenManager::sendRequest(std::string_view uri, nlohmann::json& obj) cpr::Response TokenManager::sendRequest(std::string_view uri, nlohmann::json& obj) {
{ auto resp = cpr::Post(cpr::Url{uri}, cpr::Body{obj.dump()},
auto resp = cpr::Post(cpr::Url{uri}, cpr::Body{obj.dump()},
cpr::Header{{"Content-type", "application/json"}, cpr::Header{{"Content-type", "application/json"},
{"Connection", "keep-alive"}}); {"Connection", "keep-alive"}});
return resp; return resp;
}
nlohmann::json TokenManager::createTokenBody(const model::AuthCredentials& auth)
{
nlohmann::json obj;
obj["client_id"] = auth.clientId;
obj["client_secret"] = auth.clientSecret;
obj["audience"] = auth.apiIdentifier;
obj["grant_type"] = "client_credentials";
return obj;
}
[[depreacted("use the other function with the same name")]]
model::AuthCredentials TokenManager::parseAuthCredentials(std::string_view path)
{
auto exe_path = DirectoryManager::configPath(path);
exe_path.append("/authcredentials.json");
auto con = DirectoryManager::credentialConfigContent(exe_path);
model::AuthCredentials auth;
auth.uri = "https://";
auth.uri.append(con["domain"]);
auth.apiIdentifier = con["api_identifier"];
auth.clientId = con["client_id"];
auth.clientSecret = con["client_secret"];
auth.endpoint = "oauth/token";
return auth;
}
model::AuthCredentials TokenManager::parseAuthCredentials(const model::BinaryPath& bConf)
{
auto con = DirectoryManager::credentialConfigContent(bConf);
model::AuthCredentials auth;
auth.uri = "https://";
auth.uri.append(con["domain"]);
auth.apiIdentifier = con["api_identifier"];
auth.clientId = con["client_id"];
auth.clientSecret = con["client_secret"];
auth.endpoint = "oauth/token";
return auth;
}
std::vector<std::string> TokenManager::extractScopes(const jwt::decoded_jwt&& decoded)
{
std::vector<std::string> scopes;
for (auto& d : decoded.get_payload_claims()) {
if (d.first.compare("scope") == 0) {
std::cout << "found scope" << std::endl;
std::string allScopes(d.second.to_json().get<std::string>());
std::istringstream iss(allScopes);
scopes.assign(std::istream_iterator<std::string>(iss),
std::istream_iterator<std::string>());
}
} }
return scopes;
}
std::pair<bool, std::vector<std::string>> TokenManager::fetchAuthHeader(const std::string& auth) nlohmann::json TokenManager::createTokenBody(const model::AuthCredentials& auth) {
{ nlohmann::json obj;
std::istringstream iss(auth); obj["client_id"] = auth.clientId;
std::vector<std::string> authHeader{std::istream_iterator<std::string>(iss), obj["client_secret"] = auth.clientSecret;
std::istream_iterator<std::string>() obj["audience"] = auth.apiIdentifier;
}; obj["grant_type"] = "client_credentials";
bool foundBearer = false; return obj;
if (std::any_of(authHeader.begin(), authHeader.end(),
[&](std::string_view word) {
return (word.compare("Bearer") == 0);
})) {
std::cout << "Bearer found" << std::endl;
foundBearer = true;
} }
return std::make_pair(foundBearer, authHeader);
} model::AuthCredentials TokenManager::parseAuthCredentials(const model::BinaryPath& bConf) {
auto con = DirectoryManager::credentialConfigContent(bConf);
model::AuthCredentials auth;
auth.uri = "https://";
auth.uri.append(con["domain"]);
auth.apiIdentifier = con["api_identifier"];
auth.clientId = con["client_id"];
auth.clientSecret = con["client_secret"];
auth.endpoint = "oauth/token";
return auth;
}
bool TokenManager::tokenSupportsScope(const std::vector<std::string>& scopes, std::vector<std::string> TokenManager::extractScopes(const jwt::decoded_jwt&& decoded) {
const std::string&& scope) std::vector<std::string> scopes;
{
return std::any_of(scopes.begin(), scopes.end(), for (auto& d : decoded.get_payload_claims()) {
[&](std::string_view foundScope) { if (d.first.compare("scope") == 0) {
return (foundScope.compare(scope) == 0); std::cout << "found scope\n";
}); std::string allScopes(d.second.to_json().get<std::string>());
} std::istringstream iss(allScopes);
scopes.assign(std::istream_iterator<std::string>(iss),
std::istream_iterator<std::string>());
}
}
return scopes;
}
std::pair<bool, std::vector<std::string>> TokenManager::fetchAuthHeader(const std::string& auth) {
std::istringstream iss(auth);
std::vector<std::string> authHeader{std::istream_iterator<std::string>(iss),
std::istream_iterator<std::string>()
};
bool foundBearer = false;
if (std::any_of(authHeader.begin(), authHeader.end(),
[&](std::string_view word) {
return (word.compare("Bearer") == 0); })) {
std::cout << "Bearer found\n";
foundBearer = true;
}
return std::make_pair(foundBearer, authHeader);
}
bool TokenManager::tokenSupportsScope(const std::vector<std::string>& scopes,
const std::string&& scope) {
return std::any_of(scopes.begin(), scopes.end(),
[&](std::string_view foundScope) {
return (foundScope.compare(scope) == 0);
});
}
} }
+48 -48
View File
@@ -7,70 +7,70 @@
#include "utility/PasswordEncryption.h" #include "utility/PasswordEncryption.h"
namespace manager { namespace manager {
UserManager::UserManager(const model::BinaryPath& bConf) : m_bConf(bConf) { } UserManager::UserManager(const model::BinaryPath& bConf) : m_bConf(bConf) { }
model::RegisterResult UserManager::registerUser(model::User& user) { model::RegisterResult UserManager::registerUser(model::User& user) {
model::RegisterResult result; model::RegisterResult result;
result.username = user.username; result.username = user.username;
printUser(user); printUser(user);
utility::PasswordEncryption passEnc; utility::PasswordEncryption passEnc;
auto hashed = passEnc.hashPassword(user); auto hashed = passEnc.hashPassword(user);
user.password = hashed.hashPassword; user.password = hashed.hashPassword;
database::UserRepository usrRepo(m_bConf); database::UserRepository usrRepo(m_bConf);
usrRepo.saveUserRecord(user); usrRepo.saveUserRecord(user);
model::User usr; model::User usr;
usr.username = user.username; usr.username = user.username;
std::cout << usr.username << std::endl; std::cout << usr.username << "\n";
usr = usrRepo.retrieveUserRecord(usr, type::UserFilter::username); usr = usrRepo.retrieveUserRecord(usr, type::UserFilter::username);
hashed.hashPassword = usr.password; hashed.hashPassword = usr.password;
hashed.userId = usr.id; hashed.userId = usr.id;
usrRepo.saveUserSalt(hashed); usrRepo.saveUserSalt(hashed);
result.registered = true; result.registered = true;
result.message = "successfully registered"; result.message = "successfully registered";
printUser(usr); printUser(usr);
return result; return result;
} }
bool UserManager::doesUserExist(const model::User& user) { bool UserManager::doesUserExist(const model::User& user) {
database::UserRepository userRepo(m_bConf); database::UserRepository userRepo(m_bConf);
return userRepo.doesUserRecordExist(user, type::UserFilter::username); return userRepo.doesUserRecordExist(user, type::UserFilter::username);
} }
bool UserManager::validatePassword(const model::User& user) { bool UserManager::validatePassword(const model::User& user) {
database::UserRepository userRepo(m_bConf); database::UserRepository userRepo(m_bConf);
model::User usr; model::User usr;
usr.username = user.username; usr.username = user.username;
usr = userRepo.retrieveUserRecord(usr, type::UserFilter::username); usr = userRepo.retrieveUserRecord(usr, type::UserFilter::username);
model::PassSec userSec; model::PassSec userSec;
userSec.userId = usr.id; userSec.userId = usr.id;
userSec = userRepo.retrieverUserSaltRecord(userSec, type::SaltFilter::userId); userSec = userRepo.retrieverUserSaltRecord(userSec, type::SaltFilter::userId);
userSec.hashPassword = usr.password; userSec.hashPassword = usr.password;
utility::PasswordEncryption passEnc; utility::PasswordEncryption passEnc;
return passEnc.isPasswordValid(user, userSec); return passEnc.isPasswordValid(user, userSec);
} }
void UserManager::printUser(const model::User& user) { void UserManager::printUser(const model::User& user) {
std::cout << "\nuser info" << std::endl; std::cout << "\nuser info\n";
std::cout << "id: " << user.id << std::endl; std::cout << "id: " << user.id << "\n";
std::cout << "firstname: " << user.firstname << std::endl; std::cout << "firstname: " << user.firstname << "\n";
std::cout << "lastname: " << user.lastname << std::endl; std::cout << "lastname: " << user.lastname << "\n";
std::cout << "phone: " << user.phone << std::endl; std::cout << "phone: " << user.phone << "\n";
std::cout << "email: " << user.email << std::endl; std::cout << "email: " << user.email << "\n";
std::cout << "username: " << user.username << std::endl; std::cout << "username: " << user.username << "\n";
std::cout << "password: " << user.password<< std::endl; std::cout << "password: " << user.password<< "\n";
} }
} }
+49 -54
View File
@@ -5,72 +5,67 @@
#include "database/YearRepository.h" #include "database/YearRepository.h"
#include "type/YearFilter.h" #include "type/YearFilter.h"
manager::YearManager::YearManager(const model::BinaryPath& bConf) namespace manager {
: m_bConf(bConf) YearManager::YearManager(const model::BinaryPath& bConf) : m_bConf(bConf) { }
{ }
model::Year manager::YearManager::retrieveYear(model::Year& year) model::Year YearManager::retrieveYear(model::Year& year) {
{ database::YearRepository yearRepo(m_bConf);
database::YearRepository yearRepo(m_bConf); year = yearRepo.retrieveRecord(year, type::YearFilter::year);
year = yearRepo.retrieveRecord(year, type::YearFilter::year);
return year; return year;
}
model::Year manager::YearManager::saveYear(const model::Song& song)
{
model::Year year;
year.year = song.year;
database::YearRepository yearRepo(m_bConf);
if (!yearRepo.doesYearExist(year, type::YearFilter::year)) {
yearRepo.saveRecord(year);
} else {
std::cout << "year record already exists in the database" << std::endl;
} }
return year; model::Year YearManager::saveYear(const model::Song& song) {
} model::Year year;
year.year = song.year;
void manager::YearManager::deleteYear(const model::Song& song) database::YearRepository yearRepo(m_bConf);
{ if (!yearRepo.doesYearExist(year, type::YearFilter::year)) {
model::Year year(song); yearRepo.saveRecord(year);
} else {
std::cout << "year record already exists in the database\n";
}
database::YearRepository yrRepo(m_bConf); return year;
auto yrWSC = yrRepo.retrieveRecordWithSongCount(year, type::YearFilter::id);
if (yrWSC.second > 1) {
std::cout << "year still contain songs related to it";
std::cout << ", will not delete" << std::endl;
return;
} }
std::cout << "safe to delete the year record" << std::endl; void YearManager::deleteYear(const model::Song& song) {
yrRepo.deleteYear(year, type::YearFilter::id); model::Year year(song);
}
void manager::YearManager::updateYear(model::Song& updatedSong, database::YearRepository yrRepo(m_bConf);
const model::Song& currSong) auto yrWSC = yrRepo.retrieveRecordWithSongCount(year, type::YearFilter::id);
{
model::Year year;
year.year = updatedSong.year;
database::YearRepository albRepo(m_bConf); if (yrWSC.second > 1) {
if (!albRepo.doesYearExist(year, type::YearFilter::year)) { std::cout << "year still contain songs related to it";
std::cout << "year record does not exist" << std::endl; std::cout << ", will not delete\n";
albRepo.saveRecord(year); return;
} else { }
std::cout << "year record already exists" << std::endl;
std::cout << "safe to delete the year record\n";
yrRepo.deleteYear(year, type::YearFilter::id);
} }
year = albRepo.retrieveRecord(year, type::YearFilter::year); void YearManager::updateYear(model::Song& updatedSong,
updatedSong.yearId = year.id; const model::Song& currSong) {
} model::Year year;
year.year = updatedSong.year;
void manager::YearManager::printYear(const model::Year& year) database::YearRepository albRepo(m_bConf);
{ if (!albRepo.doesYearExist(year, type::YearFilter::year)) {
std::cout << "\nyear record" << std::endl; std::cout << "year record does not exist\n";
std::cout << "id: " << year.id << std::endl; albRepo.saveRecord(year);
std::cout << "year: " << year.year << std::endl; } else {
std::cout << "year record already exists\n";
}
year = albRepo.retrieveRecord(year, type::YearFilter::year);
updatedSong.yearId = year.id;
}
void YearManager::printYear(const model::Year& year) {
std::cout << "\nyear record\n";
std::cout << "id: " << year.id << "\n";
std::cout << "year: " << year.year << "\n";
}
} }
+5 -6
View File
@@ -1,10 +1,9 @@
#include "utility/ImageFile.h" #include "utility/ImageFile.h"
utility::ImageFile::ImageFile(const char *file) : TagLib::File(file) namespace utility {
{ ImageFile::ImageFile(const char *file) : TagLib::File(file) { }
}
TagLib::ByteVector utility::ImageFile::data() TagLib::ByteVector ImageFile::data() {
{ return readBlock(length());
return readBlock(length()); }
} }
+169 -149
View File
@@ -6,10 +6,6 @@
#include <sstream> #include <sstream>
#include <string.h> #include <string.h>
#include <attachedpictureframe.h>
#include <fileref.h>
#include <mpegfile.h>
#include <tag.h>
#include "manager/DirectoryManager.h" #include "manager/DirectoryManager.h"
#include "utility/ImageFile.h" #include "utility/ImageFile.h"
@@ -17,161 +13,185 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
model::Song utility::MetadataRetriever::retrieveMetadata(std::string& songPath) namespace utility {
{ model::Song MetadataRetriever::retrieveMetadata(model::Song& song) {
TagLib::FileRef file(songPath.c_str()); TagLib::MPEG::File sameFile(song.songPath.c_str());
model::Song song; auto tag = sameFile.ID3v2Tag();
song.title = file.tag()->title().toCString(); song.title = tag->title().toCString(true);
song.artist = file.tag()->artist().toCString(); song.artist = tag->artist().toCString(true);
song.album = file.tag()->album().toCString(); song.album = tag->album().toCString(true);
song.genre = file.tag()->genre().toCString(); song.genre = tag->genre().toCString(true);
song.year = file.tag()->year(); song.year = tag->year();
song.track = file.tag()->track(); song.track = tag->track();
song.duration = file.audioProperties()->lengthInSeconds();
song.songPath = songPath;
// TODO: Move over to this eventually since at the moment song.duration = sameFile.audioProperties()->lengthInSeconds();
// I am only targetting mp3 files
// Used to retrieve disc
TagLib::MPEG::File sameFile(songPath.c_str());
auto tag = sameFile.ID3v2Tag();
auto frame = tag->frameList("TPOS"); constexpr auto id3DiscName = "TPOS";
if (frame.isEmpty()) { constexpr auto id3AlbumArtistName = "TPE2";
song.disc = 1;
// TODO: set default disc to 1 if none found auto discFrame = tag->frameList(id3DiscName);
} else { auto albumArtistFrame = tag->frameList(id3AlbumArtistName);
song.disc = std::stoi(frame.front()->toString().toCString()); 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(discFrame.front()->toString().toCString());
}
if (albumArtistFrame.isEmpty()) {
TagLib::ID3v2::TextIdentificationFrame *emptyFrame =
new TagLib::ID3v2::TextIdentificationFrame(id3AlbumArtistName);
tag->addFrame(emptyFrame);
emptyFrame->setText(song.artist.c_str());
sameFile.save();
} else {
song.albumArtist = albumArtistFrame.front()->toString().toCString(true);
}
return song;
}
model::Cover MetadataRetriever::updateCoverArt(const model::Song& song, model::Cover& cov,
const std::string& stockCoverPath) {
TagLib::MPEG::File sngF(song.songPath.c_str());
auto tag = sngF.ID3v2Tag();
auto frameList = tag->frameListMap()["APIC"];
if (frameList.isEmpty()) {
cov.imagePath.append("CoverArt.png");
if (!fs::exists(cov.imagePath)) {
std::cout << "copying stock cover path\n";
fs::copy(stockCoverPath, cov.imagePath);
}
ImageFile stockImg(cov.imagePath.c_str());
TagLib::ID3v2::AttachedPictureFrame *pic =
new TagLib::ID3v2::AttachedPictureFrame;
pic->setPicture(stockImg.data());
pic->setType(TagLib::ID3v2::AttachedPictureFrame::FrontCover);
tag->addFrame(pic);
sngF.save();
std::cout << "applied stock cover art\n";
} else {
auto frame = dynamic_cast<TagLib::ID3v2::AttachedPictureFrame*>(
frameList.front());
auto imgPath = manager::DirectoryManager::createDirectoryProcess(song, cov.imagePath);
imgPath.append(song.title);
imgPath.append(".png");
cov.imagePath = imgPath;
std::fstream imgSave(cov.imagePath, std::ios::out |
std::ios::binary);
imgSave.write(frame->picture().data(), frame->picture().size());
imgSave.close();
std::cout << "saved to " << cov.imagePath << "\n";
}
return cov;
}
// tags song with the stock cover art
model::Cover MetadataRetriever::applyStockCoverArt(
const model::Song& song, model::Cover& cov,
const std::string& stockCoverPath) {
TagLib::MPEG::File songFile(song.songPath.c_str());
auto tag = songFile.ID3v2Tag();
ImageFile stockImg(cov.imagePath.c_str());
TagLib::ID3v2::AttachedPictureFrame *pic =
new TagLib::ID3v2::AttachedPictureFrame;
pic->setPicture(stockImg.data());
pic->setType(TagLib::ID3v2::AttachedPictureFrame::FrontCover);
tag->addFrame(pic);
songFile.save();
std::cout << "applied stock cover art\n";
delete pic;
return cov;
}
// extracts cover art from the song and save it to the
// appropriate directory
model::Cover MetadataRetriever::applyCoverArt(const model::Song& song,
model::Cover& cov) {
TagLib::MPEG::File songFile(song.songPath.c_str());
auto tag = songFile.ID3v2Tag();
auto frameList = tag->frameListMap()["APIC"];
auto frame = dynamic_cast<TagLib::ID3v2::AttachedPictureFrame*>(
frameList.front());
std::fstream imgSave(cov.imagePath, std::ios::out |
std::ios::binary);
imgSave.write(frame->picture().data(), frame->picture().size());
imgSave.close();
std::cout << "saved to " << cov.imagePath << "\n";
return cov;
} }
return song; bool MetadataRetriever::songContainsCoverArt(const model::Song& song) {
} TagLib::MPEG::File songFile(song.songPath.c_str());
auto tag = songFile.ID3v2Tag();
auto frameList = tag->frameListMap()["APIC"];
model::Cover utility::MetadataRetriever::updateCoverArt(const model::Song& song, model::Cover& cov, const std::string& stockCoverPath) return !frameList.isEmpty();
{
TagLib::MPEG::File sngF(song.songPath.c_str());
auto tag = sngF.ID3v2Tag();
auto frameList = tag->frameListMap()["APIC"];
if (frameList.isEmpty()) {
cov.imagePath.append("CoverArt.png");
if (!fs::exists(cov.imagePath)) {
std::cout << "copying stock cover path" << std::endl;
fs::copy(stockCoverPath, cov.imagePath);
}
ImageFile stockImg(cov.imagePath.c_str());
TagLib::ID3v2::AttachedPictureFrame *pic =
new TagLib::ID3v2::AttachedPictureFrame;
pic->setPicture(stockImg.data());
pic->setType(TagLib::ID3v2::AttachedPictureFrame::FrontCover);
tag->addFrame(pic);
sngF.save();
std::cout << "applied stock cover art" << std::endl;
} else {
auto frame = dynamic_cast<TagLib::ID3v2::AttachedPictureFrame*>(
frameList.front());
auto imgPath = manager::DirectoryManager::createDirectoryProcess(song, cov.imagePath);
imgPath.append(song.title);
imgPath.append(".png");
cov.imagePath = imgPath;
std::fstream imgSave(cov.imagePath, std::ios::out |
std::ios::binary);
imgSave.write(frame->picture().data(), frame->picture().size());
imgSave.close();
std::cout << "saved to " << cov.imagePath << std::endl;
} }
return cov;
}
// tags song with the stock cover art void MetadataRetriever::updateMetadata(model::Song& sngUpdated, const model::Song& sngOld) {
model::Cover utility::MetadataRetriever::applyStockCoverArt( std::cout << "updating metadata\n";
const model::Song& song, model::Cover& cov, TagLib::MPEG::File file(sngOld.songPath.c_str());
const std::string& stockCoverPath) auto tag = file.ID3v2Tag();
{
TagLib::MPEG::File songFile(song.songPath.c_str());
auto tag = songFile.ID3v2Tag();
utility::ImageFile stockImg(cov.imagePath.c_str());
TagLib::ID3v2::AttachedPictureFrame *pic =
new TagLib::ID3v2::AttachedPictureFrame;
pic->setPicture(stockImg.data());
pic->setType(TagLib::ID3v2::AttachedPictureFrame::FrontCover);
tag->addFrame(pic);
songFile.save();
std::cout << "applied stock cover art" << std::endl;
delete pic;
return cov;
}
// extracts cover art from the song and save it to the
// appropriate directory
model::Cover utility::MetadataRetriever::applyCoverArt(const model::Song& song,
model::Cover& cov)
{
TagLib::MPEG::File songFile(song.songPath.c_str());
auto tag = songFile.ID3v2Tag();
auto frameList = tag->frameListMap()["APIC"];
auto frame = dynamic_cast<TagLib::ID3v2::AttachedPictureFrame*>(
frameList.front());
std::fstream imgSave(cov.imagePath, std::ios::out |
std::ios::binary);
imgSave.write(frame->picture().data(), frame->picture().size());
imgSave.close();
std::cout << "saved to " << cov.imagePath << std::endl;
return cov;
}
bool utility::MetadataRetriever::songContainsCoverArt(const model::Song& song)
{
TagLib::MPEG::File songFile(song.songPath.c_str());
auto tag = songFile.ID3v2Tag();
auto frameList = tag->frameListMap()["APIC"];
return !frameList.isEmpty();
}
void utility::MetadataRetriever::updateMetadata(model::Song& sngUpdated, const model::Song& sngOld)
{
std::cout<<"updating metadata"<<std::endl;
TagLib::FileRef file(sngOld.songPath.c_str());
if (sngUpdated.title.size() > 0) { if (sngUpdated.title.size() > 0) {
file.tag()->setTitle(sngUpdated.title); file.tag()->setTitle(sngUpdated.title);
} }
if (sngUpdated.artist.size() > 0) { if (sngUpdated.artist.size() > 0) {
file.tag()->setArtist(sngUpdated.artist); file.tag()->setArtist(sngUpdated.artist);
} }
if (sngUpdated.album.size() > 0) { if (sngUpdated.albumArtist.size() > 0) {
file.tag()->setAlbum(sngUpdated.album); constexpr auto frameId = "TPE2";
} auto albumArtistFrame = tag->frameList(frameId);
if (sngUpdated.genre.size() > 0) { if (albumArtistFrame.isEmpty()) {
file.tag()->setGenre(sngUpdated.genre); TagLib::ID3v2::TextIdentificationFrame *frame =
} new TagLib::ID3v2::TextIdentificationFrame(frameId);
if (sngUpdated.year > 0) { frame->setText(sngUpdated.albumArtist.c_str());
file.tag()->setYear(sngUpdated.year); tag->addFrame(frame);
} } else {
albumArtistFrame.front()->setText(sngUpdated.albumArtist.c_str());
}
// TODO: functionality to update the track number and disc number file.save();
}
if (sngUpdated.album.size() > 0) {
file.tag()->setAlbum(sngUpdated.album);
}
if (sngUpdated.genre.size() > 0) {
file.tag()->setGenre(sngUpdated.genre);
}
if (sngUpdated.year > 0) {
file.tag()->setYear(sngUpdated.year);
}
file.save(); // TODO: functionality to update the track number and disc number
file.save();
}
} }
+27 -33
View File
@@ -7,42 +7,36 @@
namespace utility { namespace utility {
model::PassSec PasswordEncryption::hashPassword(const model::User& user) model::PassSec PasswordEncryption::hashPassword(const model::User& user) {
{ model::PassSec passSec;
model::PassSec passSec;
std::unique_ptr<char[]> salt(new char[BCRYPT_HASHSIZE]); std::unique_ptr<char[]> salt(new char[BCRYPT_HASHSIZE]);
std::unique_ptr<char[]> hash(new char[BCRYPT_HASHSIZE]); std::unique_ptr<char[]> hash(new char[BCRYPT_HASHSIZE]);
std::cout << "generating salt" << std::endl; std::cout << "generating salt\n";
bcrypt_gensalt(saltSize(), salt.get()); bcrypt_gensalt(saltSize(), salt.get());
std::cout << "hashing password" << std::endl; std::cout << "hashing password\n";
bcrypt_hashpw(user.password.c_str(), salt.get(), hash.get()); bcrypt_hashpw(user.password.c_str(), salt.get(), hash.get());
passSec.salt = salt.get(); passSec.salt = salt.get();
passSec.hashPassword = hash.get(); passSec.hashPassword = hash.get();
std::cout << "hash: " << passSec.hashPassword << std::endl; std::cout << "hash: " << passSec.hashPassword << "\n";
std::cout << "salt: " << passSec.salt << std::endl; std::cout << "salt: " << passSec.salt << "\n";
return passSec; return passSec;
} }
bool PasswordEncryption::isPasswordValid(const model::User& user, const model::PassSec& userSalt) bool PasswordEncryption::isPasswordValid(const model::User& user,
{ const model::PassSec& userSalt) {
std::unique_ptr<char[]> passwordHashed(new char[BCRYPT_HASHSIZE]); std::unique_ptr<char[]> passwordHashed(new char[BCRYPT_HASHSIZE]);
bcrypt_hashpw(user.password.c_str(), userSalt.salt.c_str(), passwordHashed.get()); bcrypt_hashpw(user.password.c_str(), userSalt.salt.c_str(), passwordHashed.get());
return (userSalt.hashPassword.compare(passwordHashed.get()) == 0) ? true : false; return (userSalt.hashPassword.compare(passwordHashed.get()) == 0) ? true : false;
} }
int PasswordEncryption::saltSize() constexpr int PasswordEncryption::saltSize() noexcept { return 10000; }
{
constexpr auto size = 10000;
return size;
}
} }
+66 -81
View File
@@ -11,103 +11,88 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace verify { namespace verify {
bool Initialization::skipVerification(const std::string& argument) {
bool Initialization::skipVerification(const std::string& argument) return argument.compare("--noverify") == 0;
{
return argument.compare("--noverify") == 0;
}
// verifies if the configuration settings are valid
void Initialization::checkIcarus(const model::BinaryPath& bConf)
{
auto auth = confirmConfigAuth(bConf);
auto database = confirmConfigDatabase(bConf);
auto path = confirmConfigPaths(bConf);
if ((auth && database && path) == false) {
failedConfirmation();
} }
std::cout << "icarus check passed" << std::endl;
} // verifies if the configuration settings are valid
void Initialization::checkIcarus(const model::BinaryPath& bConf) {
auto auth = confirmConfigAuth(bConf);
auto database = confirmConfigDatabase(bConf);
auto path = confirmConfigPaths(bConf);
if ((!auth) && (!database) && (!path)) {
failedConfirmation();
}
std::cout << "icarus check passed\n";
}
// verifies that the authorization settings are not the default values // verifies that the authorization settings are not the default values
bool Initialization::confirmConfigAuth(const model::BinaryPath& bConf) bool Initialization::confirmConfigAuth(const model::BinaryPath& bConf) {
{ manager::TokenManager tokMgr;
manager::TokenManager tokMgr;
return tokMgr.testAuth(bConf); return tokMgr.testAuth(bConf);
} }
// verifies if database connectivity can be established // verifies if database connectivity can be established
bool Initialization::confirmConfigDatabase(const model::BinaryPath& bConf) bool Initialization::confirmConfigDatabase(const model::BinaryPath& bConf) {
{ database::BaseRepository baseRepo(bConf);
database::BaseRepository baseRepo(bConf);
return baseRepo.testConnection(); return baseRepo.testConnection();
} }
// verifies if the paths found in the config files exists // verifies if the paths found in the config files exists
bool Initialization::confirmConfigPaths(const model::BinaryPath& bConf) bool Initialization::confirmConfigPaths(const model::BinaryPath& bConf) {
{ using manager::DirectoryManager;
auto pathConfig = manager::DirectoryManager::pathConfigContent(bConf); auto pathConfig = DirectoryManager::pathConfigContent(bConf);
const auto rootMusicPath = const auto rootMusicPath =
pathConfig pathConfig
[manager:: [DirectoryManager::retrievePathType
DirectoryManager::
retrievePathType
(type::PathType::music)].get<std::string>(); (type::PathType::music)].get<std::string>();
const auto archiveRootPath = const auto archiveRootPath =
pathConfig pathConfig
[manager:: [DirectoryManager::retrievePathType
DirectoryManager::
retrievePathType
(type::PathType::archive)].get<std::string>(); (type::PathType::archive)].get<std::string>();
const auto tempRootPath = const auto tempRootPath =
pathConfig pathConfig
[manager:: [DirectoryManager::retrievePathType
DirectoryManager::
retrievePathType
(type::PathType::temp)].get<std::string>(); (type::PathType::temp)].get<std::string>();
const auto coverRootPath = const auto coverRootPath =
pathConfig pathConfig
[manager:: [DirectoryManager::retrievePathType
DirectoryManager::
retrievePathType
(type::PathType::coverArt)].get<std::string>(); (type::PathType::coverArt)].get<std::string>();
if (!fs::exists(rootMusicPath)) { if (!fs::exists(rootMusicPath)) {
std::cout << "root music path is not properly configured" << std::endl; std::cout << "root music path is not properly configured\n";
return false; return false;
}
if (!fs::exists(archiveRootPath)) {
std::cout << "archive root path is not properly configured\n";
return false;
}
if (!fs::exists(tempRootPath)) {
std::cout << "temp root path is not properly configured\n";
return false;
}
if (!fs::exists(coverRootPath)) {
std::cout << "cover art root path is not properly configured\n";
return false;
}
return true;
} }
if (!fs::exists(archiveRootPath)) {
std::cout << "archive root path is not properly configured" << std::endl;
return false;
}
if (!fs::exists(tempRootPath)) { // confirmation failed
std::cout << "temp root path is not properly configured" << std::endl; void Initialization::failedConfirmation() {
return false; std::cout << "configuration confirmation failed. check your settings\n";
std::exit(-1);
} }
if (!fs::exists(coverRootPath)) {
std::cout << "cover art root path is not properly configured" << std::endl;
return false;
}
return true;
}
// confirmation failed
void Initialization::failedConfirmation()
{
std::cout << "configuration confirmation failed. check your settings" << std::endl;
std::exit(-1);
}
} }