diff --git a/src/libs/subsonic/impl/SubsonicResource.cpp b/src/libs/subsonic/impl/SubsonicResource.cpp index 322a5817..6d1a6091 100644 --- a/src/libs/subsonic/impl/SubsonicResource.cpp +++ b/src/libs/subsonic/impl/SubsonicResource.cpp @@ -479,7 +479,7 @@ userToResponseNode(const User::pointer& user) Response::Node userNode; userNode.setAttribute("username", user->getLoginName()); - userNode.setAttribute("scrobblingEnabled", false); + userNode.setAttribute("scrobblingEnabled", true); userNode.setAttribute("adminRole", user->isAdmin()); userNode.setAttribute("settingsRole", true); userNode.setAttribute("downloadRole", true); @@ -704,11 +704,13 @@ Response handleGetAlbumListRequestCommon(const RequestContext& context, bool id3) { // Mandatory params - std::string type {getMandatoryParameterAs(context.parameters, "type")}; + const std::string type {getMandatoryParameterAs(context.parameters, "type")}; // Optional params - std::size_t size {getParameterAs(context.parameters, "size").value_or(10)}; - std::size_t offset {getParameterAs(context.parameters, "offset").value_or(0)}; + const std::size_t size {getParameterAs(context.parameters, "size").value_or(10)}; + const std::size_t offset {getParameterAs(context.parameters, "offset").value_or(0)}; + + const Range range {offset, size}; std::vector releases; @@ -718,36 +720,14 @@ handleGetAlbumListRequestCommon(const RequestContext& context, bool id3) if (!user) throw UserNotAuthorizedError {}; - if (type == "random") + if (type == "alphabeticalByName") { - // Random results are paginated, but there is no acceptable way to handle the pagination params without repeating some albums - releases = Release::getAllRandom(context.dbSession, {}, size); - } - else if (type == "newest") - { - bool moreResults {}; - releases = Release::getLastWritten(context.dbSession, std::nullopt, {}, Range {offset, size}, moreResults); - } - else if (type == "alphabeticalByName") - { - releases = Release::getAll(context.dbSession, Range {offset, size}); + releases = Release::getAll(context.dbSession, range); } else if (type == "alphabeticalByArtist") { releases = Release::getAllOrderedByArtist(context.dbSession, offset, size); } - else if (type == "byYear") - { - int fromYear {getMandatoryParameterAs(context.parameters, "fromYear")}; - int toYear {getMandatoryParameterAs(context.parameters, "toYear")}; - - releases = Release::getByYear(context.dbSession, fromYear, toYear, offset, size); - } - else if (type == "starred") - { - bool moreResults {}; - releases = Release::getStarred(context.dbSession, user, {}, Range {offset, size}, moreResults); - } else if (type == "byGenre") { // Mandatory param @@ -760,10 +740,42 @@ handleGetAlbumListRequestCommon(const RequestContext& context, bool id3) if (cluster) { bool more; - releases = Release::getByFilter(context.dbSession, {cluster.id()}, {}, Range {offset, size}, more); + releases = Release::getByFilter(context.dbSession, {cluster.id()}, {}, range, more); } } } + else if (type == "byYear") + { + int fromYear {getMandatoryParameterAs(context.parameters, "fromYear")}; + int toYear {getMandatoryParameterAs(context.parameters, "toYear")}; + + releases = Release::getByYear(context.dbSession, fromYear, toYear, range); + } + else if (type == "frequent") + { + bool moreResults {}; + releases = user->getPlayedTrackList(context.dbSession)->getTopReleases({}, range, moreResults); + } + else if (type == "newest") + { + bool moreResults {}; + releases = Release::getLastWritten(context.dbSession, std::nullopt, {}, range, moreResults); + } + else if (type == "random") + { + // Random results are paginated, but there is no acceptable way to handle the pagination params without repeating some albums + releases = Release::getAllRandom(context.dbSession, {}, size); + } + else if (type == "recent") + { + bool moreResults {}; + releases = user->getPlayedTrackList(context.dbSession)->getReleasesReverse({}, range, moreResults); + } + else if (type == "starred") + { + bool moreResults {}; + releases = Release::getStarred(context.dbSession, user, {}, range, moreResults); + } else throw NotImplementedGenericError {}; @@ -1547,6 +1559,34 @@ handleUnstarRequest(RequestContext& context) return Response::createOkResponse(context); } +static +Response +handleScrobble(RequestContext& context) +{ + const std::vector ids {getMandatoryMultiParametersAs(context.parameters, "id")}; + // TODO handle time in some way (need underlying refacto) + + if (!std::all_of(std::cbegin(ids), std::cend(ids), [](const Id& id) { return id.type == Id::Type::Track; })) + throw BadParameterGenericError {"id"}; + + auto transaction {context.dbSession.createUniqueTransaction()}; + + User::pointer user {User::getByLoginName(context.dbSession, context.userName)}; + if (!user) + throw RequestedDataNotFoundError {}; + + for (Id id : ids) + { + Track::pointer track {Track::getById(context.dbSession, id.value)}; + if (!track) + continue; + + TrackListEntry::create(context.dbSession, track, user->getPlayedTrackList(context.dbSession)); + } + + return Response::createOkResponse(context); +} + static Response handleUpdateUserRequest(RequestContext& context) @@ -1824,10 +1864,10 @@ static std::unordered_map requestEntryPoints {"getAvatar", {handleNotImplemented, false}}, // Media annotation - {"star", {handleStarRequest, false}}, - {"unstar", {handleUnstarRequest, false}}, + {"star", {handleStarRequest, false}}, + {"unstar", {handleUnstarRequest, false}}, {"setRating", {handleNotImplemented, false}}, - {"scrobble", {handleNotImplemented, false}}, + {"scrobble", {handleScrobble, false}}, // Sharing {"getShares", {handleNotImplemented, false}},