Synchronize starred tracks with ListenBrainz 'loves'. fixes #163
This commit is contained in:
@@ -136,7 +136,8 @@ createQuery(Session& session, const Artist::FindParameters& params)
|
||||
assert(params.scrobbler);
|
||||
query.join("starred_artist s_a ON s_a.artist_id = a.id")
|
||||
.where("s_a.user_id = ?").bind(params.starringUser)
|
||||
.where("s_a.scrobbler = ?").bind(*params.scrobbler);
|
||||
.where("s_a.scrobbler = ?").bind(*params.scrobbler)
|
||||
.where("s_a.scrobbling_state <> ?").bind(ScrobblingState::PendingRemove);
|
||||
}
|
||||
|
||||
if (!params.clusters.empty())
|
||||
|
||||
@@ -571,6 +571,17 @@ CREATE TABLE IF NOT EXISTS "track_artist_link_backup" (
|
||||
session.getDboSession().execute("ALTER TABLE track_artist_link_backup RENAME TO track_artist_link");
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
migrateFromV34(Session& session)
|
||||
{
|
||||
// Add scrobbling state
|
||||
// By default, everythin needs to be sent
|
||||
session.getDboSession().execute("ALTER TABLE starred_artist ADD scrobbling_state INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(/*ScrobblingState::PendingAdd*/0)) + ")");
|
||||
session.getDboSession().execute("ALTER TABLE starred_release ADD scrobbling_state INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(/*ScrobblingState::PendingAdd*/0)) + ")");
|
||||
session.getDboSession().execute("ALTER TABLE starred_track ADD scrobbling_state INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(/*ScrobblingState::PendingAdd*/0)) + ")");
|
||||
}
|
||||
|
||||
void
|
||||
doDbMigration(Session& session)
|
||||
{
|
||||
@@ -611,6 +622,7 @@ CREATE TABLE IF NOT EXISTS "track_artist_link_backup" (
|
||||
{31, migrateFromV31},
|
||||
{32, migrateFromV32},
|
||||
{33, migrateFromV33},
|
||||
{34, migrateFromV34},
|
||||
};
|
||||
|
||||
while (1)
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Database
|
||||
class Session;
|
||||
|
||||
using Version = std::size_t;
|
||||
static constexpr Version LMS_DATABASE_VERSION {34};
|
||||
static constexpr Version LMS_DATABASE_VERSION {35};
|
||||
class VersionInfo
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -64,7 +64,8 @@ createQuery(Session& session, const Release::FindParameters& params)
|
||||
assert(params.scrobbler);
|
||||
query.join("starred_release s_r ON s_r.release_id = r.id")
|
||||
.where("s_r.user_id = ?").bind(params.starringUser)
|
||||
.where("s_r.scrobbler = ?").bind(*params.scrobbler);
|
||||
.where("s_r.scrobbler = ?").bind(*params.scrobbler)
|
||||
.where("s_r.scrobbling_state <> ?").bind(ScrobblingState::PendingRemove);
|
||||
}
|
||||
|
||||
if (params.artist.isValid())
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <Wt/Dbo/WtSqlTraits.h>
|
||||
|
||||
#include "services/database/Artist.hpp"
|
||||
#include "services/database/Session.hpp"
|
||||
#include "services/database/User.hpp"
|
||||
#include "IdTypeTraits.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <Wt/Dbo/WtSqlTraits.h>
|
||||
|
||||
#include "services/database/Release.hpp"
|
||||
#include "services/database/Session.hpp"
|
||||
#include "services/database/User.hpp"
|
||||
#include "IdTypeTraits.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <Wt/Dbo/WtSqlTraits.h>
|
||||
|
||||
#include "services/database/Track.hpp"
|
||||
#include "services/database/Session.hpp"
|
||||
#include "services/database/User.hpp"
|
||||
#include "IdTypeTraits.hpp"
|
||||
#include "Utils.hpp"
|
||||
@@ -60,6 +61,23 @@ namespace Database
|
||||
.resultValue();
|
||||
}
|
||||
|
||||
RangeResults<StarredTrackId>
|
||||
StarredTrack::find(Session& session, const FindParameters& params)
|
||||
{
|
||||
session.checkSharedLocked();
|
||||
|
||||
auto query {session.getDboSession().query<StarredTrackId>("SELECT DISTINCT s_t.id FROM starred_track s_t")};
|
||||
|
||||
if (params.scrobbler)
|
||||
query.where("s_t.scrobbler = ?").bind(*params.scrobbler);
|
||||
if (params.scrobblingState)
|
||||
query.where("s_t.scrobbling_state = ?").bind(*params.scrobblingState);
|
||||
if (params.user.isValid())
|
||||
query.where("s_t.user_id = ?").bind(params.user);
|
||||
|
||||
return execQuery(query, params.range);
|
||||
}
|
||||
|
||||
StarredTrack::pointer
|
||||
StarredTrack::create(Session& session, ObjectPtr<Track> track, ObjectPtr<User> user, Scrobbler scrobbler)
|
||||
{
|
||||
|
||||
@@ -56,7 +56,8 @@ createQuery(Session& session, const Track::FindParameters& params)
|
||||
assert(params.scrobbler);
|
||||
query.join("starred_track s_t ON s_t.track_id = t.id")
|
||||
.where("s_t.user_id = ?").bind(params.starringUser)
|
||||
.where("s_t.scrobbler = ?").bind(*params.scrobbler);
|
||||
.where("s_t.scrobbler = ?").bind(*params.scrobbler)
|
||||
.where("s_t.scrobbling_state <> ?").bind(ScrobblingState::PendingRemove);
|
||||
}
|
||||
|
||||
if (!params.clusters.empty())
|
||||
|
||||
@@ -23,14 +23,11 @@
|
||||
#include <Wt/Dbo/Dbo.h>
|
||||
|
||||
#include "services/database/ArtistId.hpp"
|
||||
#include "services/database/IdType.hpp"
|
||||
#include "services/database/Object.hpp"
|
||||
#include "services/database/Session.hpp"
|
||||
#include "services/database/StarredArtistId.hpp"
|
||||
#include "services/database/Types.hpp"
|
||||
#include "services/database/UserId.hpp"
|
||||
|
||||
LMS_DECLARE_IDTYPE(StarredArtistId)
|
||||
|
||||
namespace Database
|
||||
{
|
||||
class Artist;
|
||||
@@ -56,15 +53,18 @@ namespace Database
|
||||
ObjectPtr<User> getUser() const { return _user; }
|
||||
Scrobbler getScrobbler() const { return _scrobbler; }
|
||||
const Wt::WDateTime& getDateTime() const { return _dateTime; }
|
||||
ScrobblingState getScrobblingState() const { return _scrobblingState; }
|
||||
|
||||
// Setters
|
||||
void setDateTime(const Wt::WDateTime& dateTime);
|
||||
void setScrobblingState(ScrobblingState state) { _scrobblingState = state; }
|
||||
|
||||
template<class Action>
|
||||
void persist(Action& a)
|
||||
{
|
||||
Wt::Dbo::field(a, _scrobbler, "scrobbler");
|
||||
Wt::Dbo::field(a, _dateTime, "date_time");
|
||||
Wt::Dbo::field(a, _scrobbler, "scrobbler");
|
||||
Wt::Dbo::field(a, _scrobblingState, "scrobbling_state");
|
||||
Wt::Dbo::field(a, _dateTime, "date_time");
|
||||
|
||||
Wt::Dbo::belongsTo(a, _artist, "artist", Wt::Dbo::OnDeleteCascade);
|
||||
Wt::Dbo::belongsTo(a, _user, "user", Wt::Dbo::OnDeleteCascade);
|
||||
@@ -72,6 +72,7 @@ namespace Database
|
||||
|
||||
private:
|
||||
Scrobbler _scrobbler; // for which scrobbler
|
||||
ScrobblingState _scrobblingState {ScrobblingState::PendingAdd};
|
||||
Wt::WDateTime _dateTime; // when it was starred
|
||||
|
||||
Wt::Dbo::ptr<Artist> _artist;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "services/database/IdType.hpp"
|
||||
|
||||
LMS_DECLARE_IDTYPE(StarredArtistId)
|
||||
|
||||
@@ -22,15 +22,12 @@
|
||||
#include <Wt/WDateTime.h>
|
||||
#include <Wt/Dbo/Dbo.h>
|
||||
|
||||
#include "services/database/ReleaseId.hpp"
|
||||
#include "services/database/IdType.hpp"
|
||||
#include "services/database/Object.hpp"
|
||||
#include "services/database/Session.hpp"
|
||||
#include "services/database/ReleaseId.hpp"
|
||||
#include "services/database/StarredReleaseId.hpp"
|
||||
#include "services/database/Types.hpp"
|
||||
#include "services/database/UserId.hpp"
|
||||
|
||||
LMS_DECLARE_IDTYPE(StarredReleaseId)
|
||||
|
||||
namespace Database
|
||||
{
|
||||
class Release;
|
||||
@@ -56,15 +53,18 @@ namespace Database
|
||||
ObjectPtr<User> getUser() const { return _user; }
|
||||
Scrobbler getScrobbler() const { return _scrobbler; }
|
||||
const Wt::WDateTime& getDateTime() const { return _dateTime; }
|
||||
ScrobblingState getScrobblingState() const { return _scrobblingState; }
|
||||
|
||||
// Setters
|
||||
void setDateTime(const Wt::WDateTime& dateTime);
|
||||
void setScrobblingState(ScrobblingState state) { _scrobblingState = state; }
|
||||
|
||||
template<class Action>
|
||||
void persist(Action& a)
|
||||
{
|
||||
Wt::Dbo::field(a, _scrobbler, "scrobbler");
|
||||
Wt::Dbo::field(a, _dateTime, "date_time");
|
||||
Wt::Dbo::field(a, _scrobbler, "scrobbler");
|
||||
Wt::Dbo::field(a, _scrobblingState, "scrobbling_state");
|
||||
Wt::Dbo::field(a, _dateTime, "date_time");
|
||||
|
||||
Wt::Dbo::belongsTo(a, _release, "release", Wt::Dbo::OnDeleteCascade);
|
||||
Wt::Dbo::belongsTo(a, _user, "user", Wt::Dbo::OnDeleteCascade);
|
||||
@@ -72,6 +72,7 @@ namespace Database
|
||||
|
||||
private:
|
||||
Scrobbler _scrobbler; // for which scrobbler
|
||||
ScrobblingState _scrobblingState {ScrobblingState::PendingAdd};
|
||||
Wt::WDateTime _dateTime; // when it was starred
|
||||
|
||||
Wt::Dbo::ptr<Release> _release;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "services/database/IdType.hpp"
|
||||
|
||||
LMS_DECLARE_IDTYPE(StarredReleaseId)
|
||||
|
||||
@@ -23,13 +23,11 @@
|
||||
#include <Wt/Dbo/Dbo.h>
|
||||
|
||||
#include "services/database/TrackId.hpp"
|
||||
#include "services/database/IdType.hpp"
|
||||
#include "services/database/Object.hpp"
|
||||
#include "services/database/Session.hpp"
|
||||
#include "services/database/StarredTrackId.hpp"
|
||||
#include "services/database/Types.hpp"
|
||||
#include "services/database/UserId.hpp"
|
||||
|
||||
LMS_DECLARE_IDTYPE(StarredTrackId)
|
||||
#include "utils/EnumSet.hpp"
|
||||
|
||||
namespace Database
|
||||
{
|
||||
@@ -43,10 +41,23 @@ namespace Database
|
||||
StarredTrack() = default;
|
||||
StarredTrack(ObjectPtr<Track> track, ObjectPtr<User> user, Scrobbler scrobbler);
|
||||
|
||||
struct FindParameters
|
||||
{
|
||||
std::optional<Scrobbler> scrobbler; // for this scrobbler
|
||||
std::optional<ScrobblingState> scrobblingState; // and these states
|
||||
UserId user; // and this user
|
||||
Range range;
|
||||
|
||||
FindParameters& setScrobbler(Scrobbler _scrobbler, ScrobblingState _scrobblingState) { scrobbler = _scrobbler; scrobblingState = _scrobblingState; return *this; }
|
||||
FindParameters& setUser(UserId _user) {user = _user; return *this; }
|
||||
FindParameters& setRange(Range _range) {range = _range; return *this; }
|
||||
};
|
||||
|
||||
// Search utility
|
||||
static std::size_t getCount(Session& session);
|
||||
static pointer find(Session& session, StarredTrackId id);
|
||||
static pointer find(Session& session, TrackId trackId, UserId userId, Scrobbler scrobbler);
|
||||
static RangeResults<StarredTrackId> find(Session& session, const FindParameters& findParams);
|
||||
|
||||
// Create utility
|
||||
static pointer create(Session& session, ObjectPtr<Track> track, ObjectPtr<User> user, Scrobbler scrobbler);
|
||||
@@ -56,15 +67,18 @@ namespace Database
|
||||
ObjectPtr<User> getUser() const { return _user; }
|
||||
Scrobbler getScrobbler() const { return _scrobbler; }
|
||||
const Wt::WDateTime& getDateTime() const { return _dateTime; }
|
||||
ScrobblingState getScrobblingState() const { return _scrobblingState; }
|
||||
|
||||
// Setters
|
||||
void setDateTime(const Wt::WDateTime& dateTime);
|
||||
void setScrobblingState(ScrobblingState state) { _scrobblingState = state; }
|
||||
|
||||
template<class Action>
|
||||
void persist(Action& a)
|
||||
{
|
||||
Wt::Dbo::field(a, _scrobbler, "scrobbler");
|
||||
Wt::Dbo::field(a, _dateTime, "date_time");
|
||||
Wt::Dbo::field(a, _scrobbler, "scrobbler");
|
||||
Wt::Dbo::field(a, _scrobblingState, "scrobbling_state");
|
||||
Wt::Dbo::field(a, _dateTime, "date_time");
|
||||
|
||||
Wt::Dbo::belongsTo(a, _track, "track", Wt::Dbo::OnDeleteCascade);
|
||||
Wt::Dbo::belongsTo(a, _user, "user", Wt::Dbo::OnDeleteCascade);
|
||||
@@ -72,6 +86,7 @@ namespace Database
|
||||
|
||||
private:
|
||||
Scrobbler _scrobbler; // for which scrobbler
|
||||
ScrobblingState _scrobblingState {ScrobblingState::PendingAdd};
|
||||
Wt::WDateTime _dateTime; // when it was starred
|
||||
|
||||
Wt::Dbo::ptr<Track> _track;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "services/database/IdType.hpp"
|
||||
|
||||
LMS_DECLARE_IDTYPE(StarredTrackId)
|
||||
|
||||
@@ -64,6 +64,24 @@ TEST_F(DatabaseFixture, StarredArtist)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DatabaseFixture, StarredArtist_PendingDestroy)
|
||||
{
|
||||
ScopedArtist artist {session, "MyArtist"};
|
||||
ScopedUser user {session, "MyUser"};
|
||||
ScopedStarredArtist starredArtist {session, artist.lockAndGet(), user.lockAndGet(), Scrobbler::Internal};
|
||||
|
||||
{
|
||||
auto transaction {session.createUniqueTransaction()};
|
||||
|
||||
auto artists {Artist::find(session, Artist::FindParameters {}.setStarringUser(user.getId(), Scrobbler::Internal))};
|
||||
EXPECT_EQ(artists.results.size(), 1);
|
||||
|
||||
starredArtist.get().modify()->setScrobblingState(ScrobblingState::PendingRemove);
|
||||
artists = Artist::find(session, Artist::FindParameters {}.setStarringUser(user.getId(), Scrobbler::Internal));
|
||||
EXPECT_EQ(artists.results.size(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DatabaseFixture, StarredArtist_dateTime)
|
||||
{
|
||||
ScopedArtist artist1 {session, "MyArtist1"};
|
||||
|
||||
@@ -64,6 +64,24 @@ TEST_F(DatabaseFixture, StarredRelease)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DatabaseFixture, Starredrelease_PendingDestroy)
|
||||
{
|
||||
ScopedRelease release {session, "MyRelease"};
|
||||
ScopedUser user {session, "MyUser"};
|
||||
ScopedStarredRelease starredRelease {session, release.lockAndGet(), user.lockAndGet(), Scrobbler::Internal};
|
||||
|
||||
{
|
||||
auto transaction {session.createUniqueTransaction()};
|
||||
|
||||
auto releases {Release::find(session, Release::FindParameters {}.setStarringUser(user.getId(), Scrobbler::Internal))};
|
||||
EXPECT_EQ(releases.results.size(), 1);
|
||||
|
||||
starredRelease.get().modify()->setScrobblingState(ScrobblingState::PendingRemove);
|
||||
releases = Release::find(session, Release::FindParameters {}.setStarringUser(user.getId(), Scrobbler::Internal));
|
||||
EXPECT_EQ(releases.results.size(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DatabaseFixture, StarredRelease_dateTime)
|
||||
{
|
||||
ScopedRelease release1 {session, "MyRelease1"};
|
||||
|
||||
@@ -64,6 +64,24 @@ TEST_F(DatabaseFixture, StarredTrack)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DatabaseFixture, Starredtrack_PendingDestroy)
|
||||
{
|
||||
ScopedTrack track {session, "MyTrack"};
|
||||
ScopedUser user {session, "MyUser"};
|
||||
ScopedStarredTrack starredTrack {session, track.lockAndGet(), user.lockAndGet(), Scrobbler::Internal};
|
||||
|
||||
{
|
||||
auto transaction {session.createUniqueTransaction()};
|
||||
|
||||
auto tracks {Track::find(session, Track::FindParameters {}.setStarringUser(user.getId(), Scrobbler::Internal))};
|
||||
EXPECT_EQ(tracks.results.size(), 1);
|
||||
|
||||
starredTrack.get().modify()->setScrobblingState(ScrobblingState::PendingRemove);
|
||||
tracks = Track::find(session, Track::FindParameters {}.setStarringUser(user.getId(), Scrobbler::Internal));
|
||||
EXPECT_EQ(tracks.results.size(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DatabaseFixture, StarredTrack_dateTime)
|
||||
{
|
||||
ScopedTrack track1 {session, "MyTrack1"};
|
||||
|
||||
Reference in New Issue
Block a user