From abcf9e5b4983ec4f0aa291458c97be8dc458e665 Mon Sep 17 00:00:00 2001 From: emeric Date: Sun, 1 Oct 2023 16:48:33 +0200 Subject: [PATCH] extracted bookmark handling --- src/libs/subsonic/CMakeLists.txt | 2 + src/libs/subsonic/impl/Bookmark.cpp | 104 ++++++++++++++++++ src/libs/subsonic/impl/Bookmark.hpp | 32 ++++++ src/libs/subsonic/impl/SubsonicResource.cpp | 101 +---------------- src/libs/subsonic/impl/responses/Album.cpp | 3 +- src/libs/subsonic/impl/responses/Artist.cpp | 1 + src/libs/subsonic/impl/responses/Bookmark.cpp | 43 ++++++++ src/libs/subsonic/impl/responses/Bookmark.hpp | 33 ++++++ src/libs/subsonic/impl/responses/Song.cpp | 3 +- 9 files changed, 224 insertions(+), 98 deletions(-) create mode 100644 src/libs/subsonic/impl/Bookmark.cpp create mode 100644 src/libs/subsonic/impl/Bookmark.hpp create mode 100644 src/libs/subsonic/impl/responses/Bookmark.cpp create mode 100644 src/libs/subsonic/impl/responses/Bookmark.hpp 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