1 /*
2  * Copyright (C) 2021 Emeric Poupon
3  *
4  * This file is part of LMS.
5  *
6  * LMS is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * LMS is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with LMS.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "Scrobbling.hpp"
21 
22 #include "database/Db.hpp"
23 #include "database/Session.hpp"
24 #include "database/TrackList.hpp"
25 #include "database/User.hpp"
26 
27 #include "internal/InternalScrobbler.hpp"
28 #include "listenbrainz/ListenBrainzScrobbler.hpp"
29 
30 namespace Scrobbling
31 {
32 	std::unique_ptr<IScrobbling>
createScrobbling(Database::Db & db)33 	createScrobbling(Database::Db& db)
34 	{
35 		return std::make_unique<Scrobbling>(db);
36 	}
37 
Scrobbling(Database::Db & db)38 	Scrobbling::Scrobbling(Database::Db& db)
39 		: _db {db}
40 	{
41 		_scrobblers.emplace(Database::Scrobbler::Internal, std::make_unique<InternalScrobbler>(_db));
42 		_scrobblers.emplace(Database::Scrobbler::ListenBrainz, std::make_unique<ListenBrainzScrobbler>(_db));
43 	}
44 
45 	void
listenStarted(const Listen & listen)46 	Scrobbling::listenStarted(const Listen& listen)
47 	{
48 		if (auto scrobbler {getUserScrobbler(listen.userId)})
49 			_scrobblers[*scrobbler]->listenStarted(listen);
50 	}
51 
52 	void
listenFinished(const Listen & listen,std::optional<std::chrono::seconds> duration)53 	Scrobbling::listenFinished(const Listen& listen, std::optional<std::chrono::seconds> duration)
54 	{
55 		if (auto scrobbler {getUserScrobbler(listen.userId)})
56 			_scrobblers[*scrobbler]->listenFinished(listen, duration);
57 	}
58 
59 	void
addListen(const Listen & listen,Wt::WDateTime timePoint)60 	Scrobbling::addListen(const Listen& listen, Wt::WDateTime timePoint)
61 	{
62 		if (auto scrobbler {getUserScrobbler(listen.userId)})
63 			_scrobblers[*scrobbler]->addListen(listen, timePoint);
64 	}
65 
66 	std::optional<Database::Scrobbler>
getUserScrobbler(Database::IdType userId)67 	Scrobbling::getUserScrobbler(Database::IdType userId)
68 	{
69 		std::optional<Database::Scrobbler> scrobbler;
70 
71 		Database::Session& session {_db.getTLSSession()};
72 		auto transaction {session.createSharedTransaction()};
73 		if (const Database::User::pointer user {Database::User::getById(session, userId)})
74 			scrobbler = user->getScrobbler();
75 
76 		return scrobbler;
77 	}
78 
79 	std::vector<Wt::Dbo::ptr<Database::Artist>>
getRecentArtists(Database::Session & session,Wt::Dbo::ptr<Database::User> user,const std::set<Database::IdType> & clusterIds,std::optional<Database::TrackArtistLinkType> linkType,std::optional<Database::Range> range,bool & moreResults)80 	Scrobbling::getRecentArtists(Database::Session& session,
81 										Wt::Dbo::ptr<Database::User> user,
82 										const std::set<Database::IdType>& clusterIds,
83 										std::optional<Database::TrackArtistLinkType> linkType,
84 										std::optional<Database::Range> range,
85 										bool& moreResults)
86 	{
87 		const Wt::Dbo::ptr<Database::TrackList> history {getListensTrackList(session, user)};
88 
89 		std::vector<Wt::Dbo::ptr<Database::Artist>> res;
90 		if (history)
91 			res = history->getArtistsReverse(clusterIds, linkType, range, moreResults);
92 
93 		return res;
94 	}
95 
96 	std::vector<Wt::Dbo::ptr<Database::Release>>
getRecentReleases(Database::Session & session,Wt::Dbo::ptr<Database::User> user,const std::set<Database::IdType> & clusterIds,std::optional<Database::Range> range,bool & moreResults)97 	Scrobbling::getRecentReleases(Database::Session& session,
98 										Wt::Dbo::ptr<Database::User> user,
99 										const std::set<Database::IdType>& clusterIds,
100 										std::optional<Database::Range> range,
101 										bool& moreResults)
102 	{
103 		const Wt::Dbo::ptr<Database::TrackList> history {getListensTrackList(session, user)};
104 
105 		std::vector<Wt::Dbo::ptr<Database::Release>> res;
106 		if (history)
107 			res = history->getReleasesReverse(clusterIds, range, moreResults);
108 
109 		return res;
110 	}
111 
112 	std::vector<Wt::Dbo::ptr<Database::Track>>
getRecentTracks(Database::Session & session,Wt::Dbo::ptr<Database::User> user,const std::set<Database::IdType> & clusterIds,std::optional<Database::Range> range,bool & moreResults)113 	Scrobbling::getRecentTracks(Database::Session& session,
114 										Wt::Dbo::ptr<Database::User> user,
115 										const std::set<Database::IdType>& clusterIds,
116 										std::optional<Database::Range> range,
117 										bool& moreResults)
118 	{
119 		const Wt::Dbo::ptr<Database::TrackList> history {getListensTrackList(session, user)};
120 
121 		std::vector<Wt::Dbo::ptr<Database::Track>> res;
122 		if (history)
123 			res = history->getTracksReverse(clusterIds, range, moreResults);
124 
125 		return res;
126 	}
127 
128 
129 	// Top
130 	std::vector<Wt::Dbo::ptr<Database::Artist>>
getTopArtists(Database::Session & session,Wt::Dbo::ptr<Database::User> user,const std::set<Database::IdType> & clusterIds,std::optional<Database::TrackArtistLinkType> linkType,std::optional<Database::Range> range,bool & moreResults)131 	Scrobbling::getTopArtists(Database::Session& session,
132 										Wt::Dbo::ptr<Database::User> user,
133 										const std::set<Database::IdType>& clusterIds,
134 										std::optional<Database::TrackArtistLinkType> linkType,
135 										std::optional<Database::Range> range,
136 										bool& moreResults)
137 	{
138 		const Wt::Dbo::ptr<Database::TrackList> history {getListensTrackList(session, user)};
139 
140 		std::vector<Wt::Dbo::ptr<Database::Artist>> res;
141 		if (history)
142 			res = history->getTopArtists(clusterIds, linkType, range, moreResults);
143 
144 		return res;
145 	}
146 
147 	std::vector<Wt::Dbo::ptr<Database::Release>>
getTopReleases(Database::Session & session,Wt::Dbo::ptr<Database::User> user,const std::set<Database::IdType> & clusterIds,std::optional<Database::Range> range,bool & moreResults)148 	Scrobbling::getTopReleases(Database::Session& session,
149 										Wt::Dbo::ptr<Database::User> user,
150 										const std::set<Database::IdType>& clusterIds,
151 										std::optional<Database::Range> range,
152 										bool& moreResults)
153 	{
154 		const Wt::Dbo::ptr<Database::TrackList> history {getListensTrackList(session, user)};
155 
156 		std::vector<Wt::Dbo::ptr<Database::Release>> res;
157 		if (history)
158 			res = history->getTopReleases(clusterIds, range, moreResults);
159 
160 		return res;
161 	}
162 
163 	std::vector<Wt::Dbo::ptr<Database::Track>>
getTopTracks(Database::Session & session,Wt::Dbo::ptr<Database::User> user,const std::set<Database::IdType> & clusterIds,std::optional<Database::Range> range,bool & moreResults)164 	Scrobbling::getTopTracks(Database::Session& session,
165 										Wt::Dbo::ptr<Database::User> user,
166 										const std::set<Database::IdType>& clusterIds,
167 										std::optional<Database::Range> range,
168 										bool& moreResults)
169 	{
170 		const Wt::Dbo::ptr<Database::TrackList> history {getListensTrackList(session, user)};
171 
172 		std::vector<Wt::Dbo::ptr<Database::Track>> res;
173 		if (history)
174 			res = history->getTopTracks(clusterIds, range, moreResults);
175 
176 		return res;
177 	}
178 
179 	Wt::Dbo::ptr<Database::TrackList>
getListensTrackList(Database::Session & session,Wt::Dbo::ptr<Database::User> user)180 	Scrobbling::getListensTrackList(Database::Session& session, Wt::Dbo::ptr<Database::User> user)
181 	{
182 		return _scrobblers[user->getScrobbler()]->getListensTrackList(session, user);
183 	}
184 
185 } // ns Scrobbling
186 
187