diff --git a/src/libs/subsonic/CMakeLists.txt b/src/libs/subsonic/CMakeLists.txt
index fdfbd598..25c82747 100644
--- a/src/libs/subsonic/CMakeLists.txt
+++ b/src/libs/subsonic/CMakeLists.txt
@@ -2,8 +2,10 @@
add_library(lmssubsonic SHARED
impl/responses/Album.cpp
impl/responses/Artist.cpp
+ impl/responses/Bookmark.cpp
impl/responses/Song.cpp
impl/ProtocolVersion.cpp
+ impl/Bookmark.cpp
impl/Scan.cpp
impl/Stream.cpp
impl/SubsonicId.cpp
diff --git a/src/libs/subsonic/impl/Bookmark.cpp b/src/libs/subsonic/impl/Bookmark.cpp
new file mode 100644
index 00000000..6ef1d075
--- /dev/null
+++ b/src/libs/subsonic/impl/Bookmark.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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 "Bookmark.hpp"
+
+#include "services/database/Session.hpp"
+#include "services/database/User.hpp"
+#include "services/database/Track.hpp"
+#include "services/database/TrackBookmark.hpp"
+#include "responses/Bookmark.hpp"
+#include "responses/Song.hpp"
+#include "ParameterParsing.hpp"
+#include "SubsonicId.hpp"
+
+namespace API::Subsonic
+{
+ using namespace Database;
+
+ Response handleGetBookmarks(RequestContext& context)
+ {
+ auto transaction{ context.dbSession.createSharedTransaction() };
+
+ User::pointer user{ User::find(context.dbSession, context.userId) };
+ if (!user)
+ throw UserNotAuthorizedError{};
+
+ const auto bookmarkIds{ TrackBookmark::find(context.dbSession, user->getId(), Range {}) };
+
+ Response response{ Response::createOkResponse(context.serverProtocolVersion) };
+ Response::Node& bookmarksNode{ response.createNode("bookmarks") };
+
+ for (const TrackBookmarkId bookmarkId : bookmarkIds.results)
+ {
+ const TrackBookmark::pointer bookmark{ TrackBookmark::find(context.dbSession, bookmarkId) };
+ Response::Node bookmarkNode{ createBookmarkNode(bookmark) };
+ bookmarkNode.addArrayChild("entry", createSongNode(bookmark->getTrack(), context.dbSession, user));
+
+ bookmarksNode.addArrayChild("bookmark", std::move(bookmarkNode));
+ }
+
+ return response;
+ }
+
+ Response handleCreateBookmark(RequestContext& context)
+ {
+ // Mandatory params
+ TrackId trackId{ getMandatoryParameterAs(context.parameters, "id") };
+ unsigned long position{ getMandatoryParameterAs(context.parameters, "position") };
+ const std::optional comment{ getParameterAs(context.parameters, "comment") };
+
+ auto transaction{ context.dbSession.createUniqueTransaction() };
+
+ const User::pointer user{ User::find(context.dbSession, context.userId) };
+ if (!user)
+ throw UserNotAuthorizedError{};
+
+ const Track::pointer track{ Track::find(context.dbSession, trackId) };
+ if (!track)
+ throw RequestedDataNotFoundError{};
+
+ // Replace any existing bookmark
+ auto bookmark{ TrackBookmark::find(context.dbSession, user->getId(), trackId) };
+ if (!bookmark)
+ bookmark = context.dbSession.create(user, track);
+
+ bookmark.modify()->setOffset(std::chrono::milliseconds{ position });
+ if (comment)
+ bookmark.modify()->setComment(*comment);
+
+ return Response::createOkResponse(context.serverProtocolVersion);
+ }
+
+ Response handleDeleteBookmark(RequestContext& context)
+ {
+ // Mandatory params
+ TrackId trackId{ getMandatoryParameterAs(context.parameters, "id") };
+
+ auto transaction{ context.dbSession.createUniqueTransaction() };
+
+ auto bookmark{ TrackBookmark::find(context.dbSession, context.userId, trackId) };
+ if (!bookmark)
+ throw RequestedDataNotFoundError{};
+
+ bookmark.remove();
+
+ return Response::createOkResponse(context.serverProtocolVersion);
+ }
+}
\ No newline at end of file
diff --git a/src/libs/subsonic/impl/Bookmark.hpp b/src/libs/subsonic/impl/Bookmark.hpp
new file mode 100644
index 00000000..14d95742
--- /dev/null
+++ b/src/libs/subsonic/impl/Bookmark.hpp
@@ -0,0 +1,32 @@
+/*
+ * 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 .
+ */
+
+#pragma once
+
+#include
+
+#include "RequestContext.hpp"
+#include "SubsonicResponse.hpp"
+
+namespace API::Subsonic
+{
+ Response handleGetBookmarks(RequestContext& context);
+ Response handleCreateBookmark(RequestContext& context);
+ Response handleDeleteBookmark(RequestContext& context);
+}
diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp
index 38d5dd28..182820d3 100644
--- a/src/libs/subsonic/impl/SubsonicResource.cpp
+++ b/src/libs/subsonic/impl/SubsonicResource.cpp
@@ -46,6 +46,11 @@
#include "utils/Service.hpp"
#include "utils/String.hpp"
#include "utils/Utils.hpp"
+
+#include "responses/Artist.hpp"
+#include "responses/Album.hpp"
+#include "responses/Song.hpp"
+#include "Bookmark.hpp"
#include "ParameterParsing.hpp"
#include "ProtocolVersion.hpp"
#include "RequestContext.hpp"
@@ -54,10 +59,6 @@
#include "SubsonicId.hpp"
#include "SubsonicResponse.hpp"
-#include "responses/Artist.hpp"
-#include "responses/Album.hpp"
-#include "responses/Song.hpp"
-
using namespace Database;
static const std::string_view genreClusterName {"GENRE"};
@@ -186,22 +187,6 @@ checkUserTypeIsAllowed(RequestContext& context, EnumSet allo
throw UserNotAuthorizedError {};
}
-static
-Response::Node
-trackBookmarkToResponseNode(const TrackBookmark::pointer& trackBookmark)
-{
- Response::Node trackBookmarkNode;
-
- trackBookmarkNode.setAttribute("position", trackBookmark->getOffset().count());
- if (!trackBookmark->getComment().empty())
- trackBookmarkNode.setAttribute("comment", trackBookmark->getComment());
- trackBookmarkNode.setAttribute("created", reportedDummyDate);
- trackBookmarkNode.setAttribute("changed", reportedDummyDate);
- trackBookmarkNode.setAttribute("username", trackBookmark->getUser()->getLoginName());
-
- return trackBookmarkNode;
-}
-
static
Response::Node
clusterToResponseNode(const Cluster::pointer& cluster)
@@ -1550,82 +1535,6 @@ handleUpdatePlaylistRequest(RequestContext& context)
return Response::createOkResponse(context.serverProtocolVersion);
}
-static
-Response
-handleGetBookmarks(RequestContext& context)
-{
- auto transaction {context.dbSession.createSharedTransaction()};
-
- User::pointer user {User::find(context.dbSession, context.userId)};
- if (!user)
- throw UserNotAuthorizedError {};
-
- const auto bookmarkIds {TrackBookmark::find(context.dbSession, user->getId(), Range {})};
-
- Response response {Response::createOkResponse(context.serverProtocolVersion)};
- Response::Node& bookmarksNode {response.createNode("bookmarks")};
-
- for (const TrackBookmarkId bookmarkId : bookmarkIds.results)
- {
- const TrackBookmark::pointer bookmark {TrackBookmark::find(context.dbSession, bookmarkId)};
- Response::Node bookmarkNode {trackBookmarkToResponseNode(bookmark)};
- bookmarkNode.addArrayChild("entry", createSongNode(bookmark->getTrack(), context.dbSession, user));
-
- bookmarksNode.addArrayChild("bookmark", std::move(bookmarkNode));
- }
-
- return response ;
-}
-
-static
-Response
-handleCreateBookmark(RequestContext& context)
-{
- // Mandatory params
- TrackId trackId {getMandatoryParameterAs(context.parameters, "id")};
- unsigned long position {getMandatoryParameterAs(context.parameters, "position")};
- const std::optional comment {getParameterAs(context.parameters, "comment")};
-
- auto transaction {context.dbSession.createUniqueTransaction()};
-
- const User::pointer user {User::find(context.dbSession, context.userId)};
- if (!user)
- throw UserNotAuthorizedError {};
-
- const Track::pointer track {Track::find(context.dbSession, trackId)};
- if (!track)
- throw RequestedDataNotFoundError {};
-
- // Replace any existing bookmark
- auto bookmark {TrackBookmark::find(context.dbSession, user->getId(), trackId)};
- if (!bookmark)
- bookmark = context.dbSession.create(user, track);
-
- bookmark.modify()->setOffset(std::chrono::milliseconds {position});
- if (comment)
- bookmark.modify()->setComment(*comment);
-
- return Response::createOkResponse(context.serverProtocolVersion);
-}
-
-static
-Response
-handleDeleteBookmark(RequestContext& context)
-{
- // Mandatory params
- TrackId trackId {getMandatoryParameterAs(context.parameters, "id")};
-
- auto transaction {context.dbSession.createUniqueTransaction()};
-
- auto bookmark {TrackBookmark::find(context.dbSession, context.userId, trackId)};
- if (!bookmark)
- throw RequestedDataNotFoundError {};
-
- bookmark.remove();
-
- return Response::createOkResponse(context.serverProtocolVersion);
-}
-
static
Response
handleNotImplemented(RequestContext&)
diff --git a/src/libs/subsonic/impl/responses/Album.cpp b/src/libs/subsonic/impl/responses/Album.cpp
index 57de6642..7c6697e0 100644
--- a/src/libs/subsonic/impl/responses/Album.cpp
+++ b/src/libs/subsonic/impl/responses/Album.cpp
@@ -17,6 +17,8 @@
* along with LMS. If not, see .
*/
+#include "responses/Album.hpp"
+
#include "services/database/Cluster.hpp"
#include "services/database/Artist.hpp"
#include "services/database/Release.hpp"
@@ -25,7 +27,6 @@
#include "utils/Service.hpp"
#include "utils/String.hpp"
-#include "responses/Album.hpp"
#include "responses/Artist.hpp"
#include "SubsonicId.hpp"
diff --git a/src/libs/subsonic/impl/responses/Artist.cpp b/src/libs/subsonic/impl/responses/Artist.cpp
index f4b8448a..269a2543 100644
--- a/src/libs/subsonic/impl/responses/Artist.cpp
+++ b/src/libs/subsonic/impl/responses/Artist.cpp
@@ -18,6 +18,7 @@
*/
#include "responses/Artist.hpp"
+
#include "services/database/Artist.hpp"
#include "services/database/Release.hpp"
#include "services/database/User.hpp"
diff --git a/src/libs/subsonic/impl/responses/Bookmark.cpp b/src/libs/subsonic/impl/responses/Bookmark.cpp
new file mode 100644
index 00000000..5dfb2c35
--- /dev/null
+++ b/src/libs/subsonic/impl/responses/Bookmark.cpp
@@ -0,0 +1,43 @@
+/*
+ * 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 "responses/Bookmark.hpp"
+
+#include "services/database/TrackBookmark.hpp"
+#include "services/database/User.hpp"
+#include "SubsonicResponse.hpp"
+
+namespace API::Subsonic
+{
+ static const std::string_view reportedDummyDate{ "2000-01-01T00:00:00" };
+
+ Response::Node createBookmarkNode(const Database::ObjectPtr& trackBookmark)
+ {
+ Response::Node trackBookmarkNode;
+
+ trackBookmarkNode.setAttribute("position", trackBookmark->getOffset().count());
+ if (!trackBookmark->getComment().empty())
+ trackBookmarkNode.setAttribute("comment", trackBookmark->getComment());
+ trackBookmarkNode.setAttribute("created", reportedDummyDate);
+ trackBookmarkNode.setAttribute("changed", reportedDummyDate);
+ trackBookmarkNode.setAttribute("username", trackBookmark->getUser()->getLoginName());
+
+ return trackBookmarkNode;
+ }
+}
\ No newline at end of file
diff --git a/src/libs/subsonic/impl/responses/Bookmark.hpp b/src/libs/subsonic/impl/responses/Bookmark.hpp
new file mode 100644
index 00000000..ea36ee41
--- /dev/null
+++ b/src/libs/subsonic/impl/responses/Bookmark.hpp
@@ -0,0 +1,33 @@
+/*
+ * 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 .
+ */
+
+#pragma once
+
+#include "services/database/Object.hpp"
+#include "SubsonicResponse.hpp"
+
+namespace Database
+{
+ class TrackBookmark;
+}
+
+namespace API::Subsonic
+{
+ Response::Node createBookmarkNode(const Database::ObjectPtr& bookmark);
+}
\ No newline at end of file
diff --git a/src/libs/subsonic/impl/responses/Song.cpp b/src/libs/subsonic/impl/responses/Song.cpp
index 8e9fa183..ad229397 100644
--- a/src/libs/subsonic/impl/responses/Song.cpp
+++ b/src/libs/subsonic/impl/responses/Song.cpp
@@ -17,6 +17,8 @@
* along with LMS. If not, see .
*/
+#include "responses/Song.hpp"
+
#include
#include "services/database/Artist.hpp"
@@ -28,7 +30,6 @@
#include "utils/Service.hpp"
#include "utils/String.hpp"
#include "responses/Artist.hpp"
-#include "responses/Song.hpp"
#include "SubsonicId.hpp"
namespace API::Subsonic