diff --git a/src/libs/subsonic/CMakeLists.txt b/src/libs/subsonic/CMakeLists.txt index 873facdd..92faa800 100644 --- a/src/libs/subsonic/CMakeLists.txt +++ b/src/libs/subsonic/CMakeLists.txt @@ -1,8 +1,9 @@ add_library(lmssubsonic SHARED impl/entrypoints/Bookmarks.cpp + impl/entrypoints/MediaLibraryScanning.cpp impl/entrypoints/MediaRetrieval.cpp - impl/entrypoints/Scan.cpp + impl/entrypoints/Searching.cpp impl/entrypoints/UserManagement.cpp impl/responses/Album.cpp impl/responses/Artist.cpp diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index c551bf09..14da8c5c 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -47,8 +47,9 @@ #include "utils/Utils.hpp" #include "entrypoints/Bookmarks.hpp" +#include "entrypoints/MediaLibraryScanning.hpp" #include "entrypoints/MediaRetrieval.hpp" -#include "entrypoints/Scan.hpp" +#include "entrypoints/Searching.hpp" #include "entrypoints/UserManagement.hpp" #include "responses/Artist.hpp" #include "responses/Album.hpp" @@ -1028,84 +1029,6 @@ handleGetSongsByGenreRequest(RequestContext& context) return response; } -static -Response -handleSearchRequestCommon(RequestContext& context, bool id3) -{ - // Mandatory params - std::string queryString {getMandatoryParameterAs(context.parameters, "query")}; - std::string_view query {queryString}; - - // Symfonium adds extra "" - if (context.clientInfo.name == "Symfonium") - query = StringUtils::stringTrim(query, "\""); - - std::vector keywords {StringUtils::splitString(query, " ")}; - - // Optional params - std::size_t artistCount {getParameterAs(context.parameters, "artistCount").value_or(20)}; - std::size_t artistOffset {getParameterAs(context.parameters, "artistOffset").value_or(0)}; - std::size_t albumCount {getParameterAs(context.parameters, "albumCount").value_or(20)}; - std::size_t albumOffset {getParameterAs(context.parameters, "albumOffset").value_or(0)}; - std::size_t songCount {getParameterAs(context.parameters, "songCount").value_or(20)}; - std::size_t songOffset {getParameterAs(context.parameters, "songOffset").value_or(0)}; - - auto transaction {context.dbSession.createSharedTransaction()}; - - User::pointer user {User::find(context.dbSession, context.userId)}; - if (!user) - throw UserNotAuthorizedError {}; - - Response response {Response::createOkResponse(context.serverProtocolVersion)}; - Response::Node& searchResult2Node {response.createNode(id3 ? "searchResult3" : "searchResult2")}; - - if (artistCount > 0) - { - Artist::FindParameters params; - params.setKeywords(keywords); - params.setSortMethod(ArtistSortMethod::BySortName); - params.setRange({artistOffset, artistCount}); - - RangeResults artistIds {Artist::find(context.dbSession, params)}; - for (const ArtistId artistId : artistIds.results) - { - const auto artist {Artist::find(context.dbSession, artistId)}; - searchResult2Node.addArrayChild("artist", createArtistNode(artist, context.dbSession, user, id3)); - } - } - - if (albumCount > 0) - { - Release::FindParameters params; - params.setKeywords(keywords); - params.setSortMethod(ReleaseSortMethod::Name); - params.setRange({albumOffset, albumCount}); - - RangeResults releaseIds {Release::find(context.dbSession, params)}; - for (const ReleaseId releaseId : releaseIds.results) - { - const auto release {Release::find(context.dbSession, releaseId)}; - searchResult2Node.addArrayChild("album", createAlbumNode(release, context.dbSession, user, id3)); - } - } - - if (songCount > 0) - { - Track::FindParameters params; - params.setKeywords(keywords); - params.setRange({songOffset, songCount}); - - RangeResults trackIds {Track::find(context.dbSession, params)}; - for (const TrackId trackId : trackIds.results) - { - const auto track {Track::find(context.dbSession, trackId)}; - searchResult2Node.addArrayChild("song", createSongNode(track, context.dbSession, user)); - } - } - - return response; -} - struct StarParameters { std::vector artistIds; @@ -1145,20 +1068,6 @@ handleStarRequest(RequestContext& context) return Response::createOkResponse(context.serverProtocolVersion); } -static -Response -handleSearch2Request(RequestContext& context) -{ - return handleSearchRequestCommon(context, false /* no id3 */); -} - -static -Response -handleSearch3Request(RequestContext& context) -{ - return handleSearchRequestCommon(context, true /* id3 */); -} - static Response handleUnstarRequest(RequestContext& context) @@ -1410,7 +1319,6 @@ static std::unordered_map mediaRetrieval {"/getCoverArt", handleGetCoverArt}, }; - void SubsonicResource::handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) { diff --git a/src/libs/subsonic/impl/entrypoints/Scan.cpp b/src/libs/subsonic/impl/entrypoints/MediaLibraryScanning.cpp similarity index 53% rename from src/libs/subsonic/impl/entrypoints/Scan.cpp rename to src/libs/subsonic/impl/entrypoints/MediaLibraryScanning.cpp index b006e6b1..b5087db3 100644 --- a/src/libs/subsonic/impl/entrypoints/Scan.cpp +++ b/src/libs/subsonic/impl/entrypoints/MediaLibraryScanning.cpp @@ -17,7 +17,7 @@ * along with LMS. If not, see . */ -#include "Scan.hpp" +#include "MediaLibraryScanning.hpp" #include "services/scanner/IScannerService.hpp" #include "utils/Service.hpp" @@ -26,44 +26,43 @@ namespace API::Subsonic::Scan { using namespace Scanner; - static - Response::Node - createStatusResponseNode() + namespace { - Response::Node statusResponse; - - const IScannerService::Status scanStatus {Service::get()->getStatus()}; - - statusResponse.setAttribute("scanning", scanStatus.currentState == IScannerService::State::InProgress); - if (scanStatus.currentState == IScannerService::State::InProgress) + Response::Node + createStatusResponseNode() { - std::size_t count{}; + Response::Node statusResponse; - if (scanStatus.currentScanStepStats && scanStatus.currentScanStepStats->currentStep == ScanStep::ScanningFiles) - count = scanStatus.currentScanStepStats->processedElems; + const IScannerService::Status scanStatus{ Service::get()->getStatus() }; - statusResponse.setAttribute("count", count); + statusResponse.setAttribute("scanning", scanStatus.currentState == IScannerService::State::InProgress); + if (scanStatus.currentState == IScannerService::State::InProgress) + { + std::size_t count{}; + + if (scanStatus.currentScanStepStats && scanStatus.currentScanStepStats->currentStep == ScanStep::ScanningFiles) + count = scanStatus.currentScanStepStats->processedElems; + + statusResponse.setAttribute("count", count); + } + + return statusResponse; } - - return statusResponse; } - - Response - handleGetScanStatus(RequestContext& context) + Response handleGetScanStatus(RequestContext& context) { - Response response {Response::createOkResponse(context.serverProtocolVersion)}; + Response response{ Response::createOkResponse(context.serverProtocolVersion) }; response.addNode("scanStatus", createStatusResponseNode()); return response; } - Response - handleStartScan(RequestContext& context) + Response handleStartScan(RequestContext& context) { Service::get()->requestImmediateScan(false); - Response response {Response::createOkResponse(context.serverProtocolVersion)}; + Response response{ Response::createOkResponse(context.serverProtocolVersion) }; response.addNode("scanStatus", createStatusResponseNode()); return response; diff --git a/src/libs/subsonic/impl/entrypoints/Scan.hpp b/src/libs/subsonic/impl/entrypoints/MediaLibraryScanning.hpp similarity index 100% rename from src/libs/subsonic/impl/entrypoints/Scan.hpp rename to src/libs/subsonic/impl/entrypoints/MediaLibraryScanning.hpp diff --git a/src/libs/subsonic/impl/entrypoints/Searching.cpp b/src/libs/subsonic/impl/entrypoints/Searching.cpp new file mode 100644 index 00000000..59f3cfdd --- /dev/null +++ b/src/libs/subsonic/impl/entrypoints/Searching.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2020 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 "Searching.hpp" + +#include "services/database/Artist.hpp" +#include "services/database/Release.hpp" +#include "services/database/Session.hpp" +#include "services/database/Track.hpp" +#include "services/database/User.hpp" +#include "responses/Album.hpp" +#include "responses/Artist.hpp" +#include "responses/Song.hpp" +#include "ParameterParsing.hpp" + +#include "ParameterParsing.hpp" + +namespace API::Subsonic +{ + using namespace Database; + + namespace + { + Response handleSearchRequestCommon(RequestContext& context, bool id3) + { + // Mandatory params + std::string queryString{ getMandatoryParameterAs(context.parameters, "query") }; + std::string_view query{ queryString }; + + // Symfonium adds extra "" + if (context.clientInfo.name == "Symfonium") + query = StringUtils::stringTrim(query, "\""); + + std::vector keywords{ StringUtils::splitString(query, " ") }; + + // Optional params + std::size_t artistCount{ getParameterAs(context.parameters, "artistCount").value_or(20) }; + std::size_t artistOffset{ getParameterAs(context.parameters, "artistOffset").value_or(0) }; + std::size_t albumCount{ getParameterAs(context.parameters, "albumCount").value_or(20) }; + std::size_t albumOffset{ getParameterAs(context.parameters, "albumOffset").value_or(0) }; + std::size_t songCount{ getParameterAs(context.parameters, "songCount").value_or(20) }; + std::size_t songOffset{ getParameterAs(context.parameters, "songOffset").value_or(0) }; + + auto transaction{ context.dbSession.createSharedTransaction() }; + + User::pointer user{ User::find(context.dbSession, context.userId) }; + if (!user) + throw UserNotAuthorizedError{}; + + Response response{ Response::createOkResponse(context.serverProtocolVersion) }; + Response::Node& searchResult2Node{ response.createNode(id3 ? "searchResult3" : "searchResult2") }; + + if (artistCount > 0) + { + Artist::FindParameters params; + params.setKeywords(keywords); + params.setSortMethod(ArtistSortMethod::BySortName); + params.setRange({ artistOffset, artistCount }); + + RangeResults artistIds{ Artist::find(context.dbSession, params) }; + for (const ArtistId artistId : artistIds.results) + { + const auto artist{ Artist::find(context.dbSession, artistId) }; + searchResult2Node.addArrayChild("artist", createArtistNode(artist, context.dbSession, user, id3)); + } + } + + if (albumCount > 0) + { + Release::FindParameters params; + params.setKeywords(keywords); + params.setSortMethod(ReleaseSortMethod::Name); + params.setRange({ albumOffset, albumCount }); + + RangeResults releaseIds{ Release::find(context.dbSession, params) }; + for (const ReleaseId releaseId : releaseIds.results) + { + const auto release{ Release::find(context.dbSession, releaseId) }; + searchResult2Node.addArrayChild("album", createAlbumNode(release, context.dbSession, user, id3)); + } + } + + if (songCount > 0) + { + Track::FindParameters params; + params.setKeywords(keywords); + params.setRange({ songOffset, songCount }); + + RangeResults trackIds{ Track::find(context.dbSession, params) }; + for (const TrackId trackId : trackIds.results) + { + const auto track{ Track::find(context.dbSession, trackId) }; + searchResult2Node.addArrayChild("song", createSongNode(track, context.dbSession, user)); + } + } + + return response; + } + } + + Response handleSearch2Request(RequestContext& context) + { + return handleSearchRequestCommon(context, false /* no id3 */); + } + + Response handleSearch3Request(RequestContext& context) + { + return handleSearchRequestCommon(context, true /* id3 */); + } + +} \ No newline at end of file diff --git a/src/libs/subsonic/impl/entrypoints/Searching.hpp b/src/libs/subsonic/impl/entrypoints/Searching.hpp new file mode 100644 index 00000000..1c8b00ac --- /dev/null +++ b/src/libs/subsonic/impl/entrypoints/Searching.hpp @@ -0,0 +1,29 @@ +/* + * 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 "RequestContext.hpp" +#include "SubsonicResponse.hpp" + +namespace API::Subsonic +{ + Response handleSearch2Request(RequestContext& context); + Response handleSearch3Request(RequestContext& context); +}