diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt
index 8b305be3..df12a1c7 100644
--- a/src/tools/CMakeLists.txt
+++ b/src/tools/CMakeLists.txt
@@ -1,3 +1,4 @@
+add_subdirectory(db-generator)
add_subdirectory(cover)
add_subdirectory(metadata)
add_subdirectory(recommendation)
diff --git a/src/tools/db-generator/CMakeLists.txt b/src/tools/db-generator/CMakeLists.txt
new file mode 100644
index 00000000..1794b2c9
--- /dev/null
+++ b/src/tools/db-generator/CMakeLists.txt
@@ -0,0 +1,10 @@
+
+add_executable(lms-db-generator
+ LmsDbGenerator.cpp
+ )
+
+target_link_libraries(lms-db-generator PRIVATE
+ lmsdatabase
+ lmsutils
+ Boost::program_options
+ )
diff --git a/src/tools/db-generator/LmsDbGenerator.cpp b/src/tools/db-generator/LmsDbGenerator.cpp
new file mode 100644
index 00000000..f4700fe3
--- /dev/null
+++ b/src/tools/db-generator/LmsDbGenerator.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2023 Emeric Poupon
+ *
+ * This file is part of LMS.
+ *
+ * LMS is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LMS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with LMS. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "services/database/Db.hpp"
+#include "services/database/Artist.hpp"
+#include "services/database/Cluster.hpp"
+#include "services/database/Release.hpp"
+#include "services/database/Session.hpp"
+#include "services/database/Track.hpp"
+#include "services/database/TrackArtistLink.hpp"
+
+#include "utils/IConfig.hpp"
+#include "utils/Random.hpp"
+#include "utils/StreamLogger.hpp"
+#include "utils/Service.hpp"
+
+namespace
+{
+ struct GeneratorParameters
+ {
+ std::size_t releaseCount{ 100 };
+ std::size_t trackCountPerRelease{ 10 };
+ float compilationRatio{ 0.1 };
+ std::size_t genreCountPerTrack{ 3 };
+ std::size_t moodCountPerTrack{ 3 };
+ std::size_t genreCount{ 50 };
+ std::size_t moodCount{ 25 };
+ std::filesystem::path trackPath;
+ };
+
+ struct GenerationContext
+ {
+ Database::Session& session;
+ std::vector genres;
+ std::vector moods;
+ GenerationContext(Database::Session& _session) : session{ _session } {}
+ };
+
+ Database::Cluster::pointer generateCluster(Database::Session& session, Database::ClusterType::pointer clusterType)
+ {
+ const std::string clusterName{ clusterType->getName() + "-" + std::string{ UUID::generate().getAsString() } };
+ return session.create(clusterType, clusterName);
+ }
+
+ Database::Artist::pointer generateArtist(Database::Session& session)
+ {
+ const UUID artistMBID{ UUID::generate() };
+ const std::string artistName{ "Artist-" + std::string{ UUID::generate().getAsString() } };
+ return session.create(artistName, artistMBID);
+ }
+
+ void generateRelease(const GeneratorParameters& params, GenerationContext& context)
+ {
+ using namespace Database;
+
+ const UUID releaseMBID{ UUID::generate() };
+ const std::string releaseName{ "Release-" + std::string{ UUID::generate().getAsString() } };
+ Release::pointer release{ context.session.create(releaseName, releaseMBID) };
+
+ Artist::pointer artist{ generateArtist(context.session) };
+
+ for (std::size_t i{}; i < params.trackCountPerRelease; ++i)
+ {
+ Track::pointer track{ context.session.create