diff --git a/src/libs/database/CMakeLists.txt b/src/libs/database/CMakeLists.txt
index 3b2bf1d2..af4a6a1b 100644
--- a/src/libs/database/CMakeLists.txt
+++ b/src/libs/database/CMakeLists.txt
@@ -8,6 +8,7 @@ add_library(lmsdatabase SHARED
impl/Listen.cpp
impl/MediaLibrary.cpp
impl/Migration.cpp
+ impl/PlayQueue.cpp
impl/TrackArtistLink.cpp
impl/TrackFeatures.cpp
impl/TrackList.cpp
diff --git a/src/libs/database/impl/Migration.cpp b/src/libs/database/impl/Migration.cpp
index d1a0a1ce..179ca438 100644
--- a/src/libs/database/impl/Migration.cpp
+++ b/src/libs/database/impl/Migration.cpp
@@ -35,7 +35,7 @@ namespace lms::db
{
namespace
{
- static constexpr Version LMS_DATABASE_VERSION{ 71 };
+ static constexpr Version LMS_DATABASE_VERSION{ 72 };
}
VersionInfo::VersionInfo()
@@ -907,6 +907,31 @@ SELECT
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
}
+ void migrateFromV71(Session& session)
+ {
+ // Add a file name/stem in tracks
+ utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "playqueue" (
+ "id" integer primary key autoincrement,
+ "version" integer not null,
+ "name" text not null,
+ "current_index" integer not null,
+ "current_position_in_track" integer,
+ "last_modified_date_time" text,
+ "user_id" bigint,
+ constraint "fk_playqueue_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred
+))");
+
+ utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "playqueue_track" (
+ "playqueue_id" bigint,
+ "track_id" bigint not null,
+ primary key ("playqueue_id", "track_id"),
+ constraint "fk_playqueue_track_key1" foreign key ("playqueue_id") references "playqueue" ("id") on delete cascade deferrable initially deferred,
+ constraint "fk_playqueue_track_key2" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred
+))");
+ utils::executeCommand(*session.getDboSession(), R"(CREATE INDEX "playqueue_track_playqueue" on "playqueue_track" ("playqueue_id"))");
+ utils::executeCommand(*session.getDboSession(), R"(CREATE INDEX "playqueue_track_track" on "playqueue_track" ("track_id"))");
+ }
+
bool doDbMigration(Session& session)
{
constexpr std::string_view outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" };
@@ -954,6 +979,7 @@ SELECT
{ 68, migrateFromV68 },
{ 69, migrateFromV69 },
{ 70, migrateFromV70 },
+ { 71, migrateFromV71 },
};
bool migrationPerformed{};
diff --git a/src/libs/database/impl/PlayQueue.cpp b/src/libs/database/impl/PlayQueue.cpp
new file mode 100644
index 00000000..e08efbab
--- /dev/null
+++ b/src/libs/database/impl/PlayQueue.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 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 "database/PlayQueue.hpp"
+
+#include
+
+#include "database/Directory.hpp"
+#include "database/MediaLibrary.hpp"
+#include "database/Release.hpp"
+#include "database/Session.hpp"
+#include "database/Track.hpp"
+#include "database/User.hpp"
+
+#include "IdTypeTraits.hpp"
+#include "StringViewTraits.hpp"
+#include "Utils.hpp"
+
+namespace lms::db
+{
+ PlayQueue::PlayQueue(const ObjectPtr& user, std::string_view name)
+ : _name{ name }
+ , _user{ getDboPtr(user) }
+ {
+ }
+
+ PlayQueue::pointer PlayQueue::create(Session& session, const ObjectPtr& user, std::string_view name)
+ {
+ return session.getDboSession()->add(std::unique_ptr{ new PlayQueue{ user, name } });
+ }
+
+ std::size_t PlayQueue::getCount(Session& session)
+ {
+ session.checkReadTransaction();
+
+ return utils::fetchQuerySingleResult(session.getDboSession()->query("SELECT COUNT(*) FROM playqueue"));
+ }
+
+ PlayQueue::pointer PlayQueue::find(Session& session, PlayQueueId id)
+ {
+ session.checkReadTransaction();
+
+ return utils::fetchQuerySingleResult(session.getDboSession()->query>("SELECT p from playqueue p").where("p.id = ?").bind(id));
+ }
+
+ PlayQueue::pointer PlayQueue::find(Session& session, UserId userId, std::string_view name)
+ {
+ session.checkReadTransaction();
+
+ auto query{ session.getDboSession()->query>("SELECT p from playqueue p") };
+ query.where("p.user_id = ?").bind(userId);
+ query.where("p.name = ?").bind(name);
+
+ return utils::fetchQuerySingleResult(query);
+ }
+
+ void PlayQueue::clear()
+ {
+ _tracks.clear();
+ }
+
+ void PlayQueue::addTrack(const ObjectPtr