1 /* This file is part of Clementine.
2 Copyright 2012, Andreas Muttscheller <asfa194@gmail.com>
3
4 Clementine is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 Clementine is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with Clementine. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "incomingdataparser.h"
19
20 #include <algorithm>
21
22 #include "core/logging.h"
23 #include "core/timeconstants.h"
24 #include "engines/enginebase.h"
25 #include "internet/core/internetmodel.h"
26 #include "playlist/playlistmanager.h"
27 #include "playlist/playlistsequence.h"
28 #include "playlist/playlist.h"
29
30 #ifdef HAVE_LIBLASTFM
31 #include "internet/lastfm/lastfmservice.h"
32 #endif
33
IncomingDataParser(Application * app)34 IncomingDataParser::IncomingDataParser(Application* app) : app_(app) {
35 // load settings initially and sign up for updates
36 ReloadSettings();
37 connect(app_, SIGNAL(SettingsChanged()), SLOT(ReloadSettings()));
38
39 // Connect all the signals
40 // due the player is in a different thread, we cannot access these functions
41 // directly
42 connect(this, SIGNAL(Play()), app_->player(), SLOT(Play()));
43 connect(this, SIGNAL(PlayPause()), app_->player(), SLOT(PlayPause()));
44 connect(this, SIGNAL(Pause()), app_->player(), SLOT(Pause()));
45 connect(this, SIGNAL(Stop()), app_->player(), SLOT(Stop()));
46 connect(this, SIGNAL(StopAfterCurrent()), app_->player(),
47 SLOT(StopAfterCurrent()));
48 connect(this, SIGNAL(Next()), app_->player(), SLOT(Next()));
49 connect(this, SIGNAL(Previous()), app_->player(), SLOT(Previous()));
50 connect(this, SIGNAL(SetVolume(int)), app_->player(), SLOT(SetVolume(int)));
51 connect(this, SIGNAL(PlayAt(int, Engine::TrackChangeFlags, bool)),
52 app_->player(), SLOT(PlayAt(int, Engine::TrackChangeFlags, bool)));
53 connect(this, SIGNAL(SeekTo(int)), app_->player(), SLOT(SeekTo(int)));
54 connect(this, SIGNAL(Enque(int, int)), app_->playlist_manager(),
55 SLOT(Enque(int, int)));
56
57 connect(this, SIGNAL(SetActivePlaylist(int)), app_->playlist_manager(),
58 SLOT(SetActivePlaylist(int)));
59 connect(this, SIGNAL(ShuffleCurrent()), app_->playlist_manager(),
60 SLOT(ShuffleCurrent()));
61 connect(this, SIGNAL(SetRepeatMode(PlaylistSequence::RepeatMode)),
62 app_->playlist_manager()->sequence(),
63 SLOT(SetRepeatMode(PlaylistSequence::RepeatMode)));
64 connect(this, SIGNAL(SetShuffleMode(PlaylistSequence::ShuffleMode)),
65 app_->playlist_manager()->sequence(),
66 SLOT(SetShuffleMode(PlaylistSequence::ShuffleMode)));
67 connect(this, SIGNAL(InsertUrls(int, const QList<QUrl>&, int, bool, bool)),
68 app_->playlist_manager(),
69 SLOT(InsertUrls(int, const QList<QUrl>&, int, bool, bool)));
70 connect(this, SIGNAL(InsertSongs(int, const SongList&, int, bool, bool)),
71 app_->playlist_manager(),
72 SLOT(InsertSongs(int, const SongList&, int, bool, bool)));
73 connect(this, SIGNAL(RemoveSongs(int, const QList<int>&)),
74 app_->playlist_manager(),
75 SLOT(RemoveItemsWithoutUndo(int, const QList<int>&)));
76 connect(this, SIGNAL(Open(int)), app_->playlist_manager(), SLOT(Open(int)));
77 connect(this, SIGNAL(Close(int)), app_->playlist_manager(), SLOT(Close(int)));
78
79 connect(this, SIGNAL(RateCurrentSong(double)), app_->playlist_manager(),
80 SLOT(RateCurrentSong(double)));
81
82 #ifdef HAVE_LIBLASTFM
83 connect(this, SIGNAL(Love()), app_->scrobbler(), SLOT(Love()));
84 #endif
85 }
86
~IncomingDataParser()87 IncomingDataParser::~IncomingDataParser() {}
88
ReloadSettings()89 void IncomingDataParser::ReloadSettings() {
90 QSettings s;
91 s.beginGroup(MainWindow::kSettingsGroup);
92 doubleclick_playlist_addmode_ = MainWindow::PlaylistAddBehaviour(
93 s.value("doubleclick_playlist_addmode",
94 MainWindow::PlaylistAddBehaviour_Enqueue)
95 .toInt());
96 }
97
close_connection()98 bool IncomingDataParser::close_connection() { return close_connection_; }
99
Parse(const pb::remote::Message & msg)100 void IncomingDataParser::Parse(const pb::remote::Message& msg) {
101 close_connection_ = false;
102
103 RemoteClient* client = qobject_cast<RemoteClient*>(sender());
104
105 // Now check what's to do
106 switch (msg.type()) {
107 case pb::remote::CONNECT:
108 ClientConnect(msg, client);
109 break;
110 case pb::remote::DISCONNECT:
111 close_connection_ = true;
112 break;
113 case pb::remote::REQUEST_PLAYLISTS:
114 SendPlaylists(msg);
115 break;
116 case pb::remote::REQUEST_PLAYLIST_SONGS:
117 GetPlaylistSongs(msg);
118 break;
119 case pb::remote::SET_VOLUME:
120 emit SetVolume(msg.request_set_volume().volume());
121 break;
122 case pb::remote::PLAY:
123 emit Play();
124 break;
125 case pb::remote::PLAYPAUSE:
126 emit PlayPause();
127 break;
128 case pb::remote::PAUSE:
129 emit Pause();
130 break;
131 case pb::remote::STOP:
132 emit Stop();
133 break;
134 case pb::remote::STOP_AFTER:
135 emit StopAfterCurrent();
136 break;
137 case pb::remote::NEXT:
138 emit Next();
139 break;
140 case pb::remote::PREVIOUS:
141 emit Previous();
142 break;
143 case pb::remote::CHANGE_SONG:
144 ChangeSong(msg);
145 break;
146 case pb::remote::SHUFFLE_PLAYLIST:
147 emit ShuffleCurrent();
148 break;
149 case pb::remote::REPEAT:
150 SetRepeatMode(msg.repeat());
151 break;
152 case pb::remote::SHUFFLE:
153 SetShuffleMode(msg.shuffle());
154 break;
155 case pb::remote::SET_TRACK_POSITION:
156 emit SeekTo(msg.request_set_track_position().position());
157 break;
158 case pb::remote::INSERT_URLS:
159 InsertUrls(msg);
160 break;
161 case pb::remote::REMOVE_SONGS:
162 RemoveSongs(msg);
163 break;
164 case pb::remote::OPEN_PLAYLIST:
165 OpenPlaylist(msg);
166 break;
167 case pb::remote::CLOSE_PLAYLIST:
168 ClosePlaylist(msg);
169 break;
170 case pb::remote::LOVE:
171 emit Love();
172 break;
173 case pb::remote::BAN:
174 emit Ban();
175 break;
176 case pb::remote::GET_LYRICS:
177 emit GetLyrics();
178 break;
179 case pb::remote::DOWNLOAD_SONGS:
180 client->song_sender()->SendSongs(msg.request_download_songs());
181 break;
182 case pb::remote::SONG_OFFER_RESPONSE:
183 client->song_sender()->ResponseSongOffer(msg.response_song_offer().accepted());
184 break;
185 case pb::remote::GET_LIBRARY:
186 emit SendLibrary(client);
187 break;
188 case pb::remote::RATE_SONG:
189 RateSong(msg);
190 break;
191 case pb::remote::GLOBAL_SEARCH:
192 GlobalSearch(client, msg);
193 break;
194 default:
195 break;
196 }
197 }
198
GetPlaylistSongs(const pb::remote::Message & msg)199 void IncomingDataParser::GetPlaylistSongs(const pb::remote::Message& msg) {
200 emit SendPlaylistSongs(msg.request_playlist_songs().id());
201 }
202
ChangeSong(const pb::remote::Message & msg)203 void IncomingDataParser::ChangeSong(const pb::remote::Message& msg) {
204 // Get the first entry and check if there is a song
205 const pb::remote::RequestChangeSong& request = msg.request_change_song();
206
207 // Check if we need to change the playlist
208 if (request.playlist_id() != app_->playlist_manager()->active_id()) {
209 emit SetActivePlaylist(request.playlist_id());
210 }
211
212 switch (doubleclick_playlist_addmode_) {
213 // Play the selected song
214 case MainWindow::PlaylistAddBehaviour_Play:
215 emit PlayAt(request.song_index(), Engine::Manual, false);
216 break;
217
218 // Enque the selected song
219 case MainWindow::PlaylistAddBehaviour_Enqueue:
220 emit Enque(request.playlist_id(), request.song_index());
221 if (app_->player()->GetState() != Engine::Playing) {
222 emit PlayAt(request.song_index(), Engine::Manual, false);
223 }
224
225 break;
226 }
227 }
228
SetRepeatMode(const pb::remote::Repeat & repeat)229 void IncomingDataParser::SetRepeatMode(const pb::remote::Repeat& repeat) {
230 switch (repeat.repeat_mode()) {
231 case pb::remote::Repeat_Off:
232 emit SetRepeatMode(PlaylistSequence::Repeat_Off);
233 break;
234 case pb::remote::Repeat_Track:
235 emit SetRepeatMode(PlaylistSequence::Repeat_Track);
236 break;
237 case pb::remote::Repeat_Album:
238 emit SetRepeatMode(PlaylistSequence::Repeat_Album);
239 break;
240 case pb::remote::Repeat_Playlist:
241 emit SetRepeatMode(PlaylistSequence::Repeat_Playlist);
242 break;
243 default:
244 break;
245 }
246 }
247
SetShuffleMode(const pb::remote::Shuffle & shuffle)248 void IncomingDataParser::SetShuffleMode(const pb::remote::Shuffle& shuffle) {
249 switch (shuffle.shuffle_mode()) {
250 case pb::remote::Shuffle_Off:
251 emit SetShuffleMode(PlaylistSequence::Shuffle_Off);
252 break;
253 case pb::remote::Shuffle_All:
254 emit SetShuffleMode(PlaylistSequence::Shuffle_All);
255 break;
256 case pb::remote::Shuffle_InsideAlbum:
257 emit SetShuffleMode(PlaylistSequence::Shuffle_InsideAlbum);
258 break;
259 case pb::remote::Shuffle_Albums:
260 emit SetShuffleMode(PlaylistSequence::Shuffle_Albums);
261 break;
262 default:
263 break;
264 }
265 }
266
InsertUrls(const pb::remote::Message & msg)267 void IncomingDataParser::InsertUrls(const pb::remote::Message& msg) {
268 const pb::remote::RequestInsertUrls& request = msg.request_insert_urls();
269
270 // Insert plain urls without metadata
271 if (!request.urls().empty()) {
272 QList<QUrl> urls;
273 for (auto it = request.urls().begin(); it != request.urls().end(); ++it) {
274 std::string s = *it;
275 urls << QUrl(QStringFromStdString(s));
276 }
277
278 // Insert the urls
279 emit InsertUrls(request.playlist_id(), urls, request.position(),
280 request.play_now(), request.enqueue());
281 }
282
283 // Add songs with metadata if present
284 if (!request.songs().empty()) {
285 SongList songs;
286 for (int i = 0; i < request.songs().size(); i++) {
287 songs << CreateSongFromProtobuf(request.songs(i));
288 }
289 emit InsertSongs(request.playlist_id(), songs, request.position(),
290 request.play_now(), request.enqueue());
291 }
292 }
293
RemoveSongs(const pb::remote::Message & msg)294 void IncomingDataParser::RemoveSongs(const pb::remote::Message& msg) {
295 const pb::remote::RequestRemoveSongs& request = msg.request_remove_songs();
296
297 // Extract urls
298 QList<int> songs;
299 for (int i = 0; i < request.songs().size(); i++) {
300 songs.append(request.songs(i));
301 }
302
303 // Insert the urls
304 emit RemoveSongs(request.playlist_id(), songs);
305 }
306
ClientConnect(const pb::remote::Message & msg,RemoteClient * client)307 void IncomingDataParser::ClientConnect(const pb::remote::Message& msg, RemoteClient* client) {
308 // Always sned the Clementine infos
309 emit SendClementineInfo();
310
311 // Check if we should send the first data
312 if (!client->isDownloader()) {
313 if (!msg.request_connect().has_send_playlist_songs() // legacy
314 || msg.request_connect().send_playlist_songs()) {
315 emit SendFirstData(true);
316 } else {
317 emit SendFirstData(false);
318 }
319 }
320 }
321
SendPlaylists(const pb::remote::Message & msg)322 void IncomingDataParser::SendPlaylists(const pb::remote::Message& msg) {
323 if (!msg.has_request_playlists() ||
324 !msg.request_playlists().include_closed()) {
325 emit SendAllActivePlaylists();
326 } else {
327 emit SendAllPlaylists();
328 }
329 }
330
OpenPlaylist(const pb::remote::Message & msg)331 void IncomingDataParser::OpenPlaylist(const pb::remote::Message& msg) {
332 emit Open(msg.request_open_playlist().playlist_id());
333 }
334
ClosePlaylist(const pb::remote::Message & msg)335 void IncomingDataParser::ClosePlaylist(const pb::remote::Message& msg) {
336 emit Close(msg.request_close_playlist().playlist_id());
337 }
338
RateSong(const pb::remote::Message & msg)339 void IncomingDataParser::RateSong(const pb::remote::Message& msg) {
340 double rating = (double)msg.request_rate_song().rating();
341 emit RateCurrentSong(rating);
342 }
343
GlobalSearch(RemoteClient * client,const pb::remote::Message & msg)344 void IncomingDataParser::GlobalSearch(RemoteClient *client, const pb::remote::Message &msg) {
345 emit DoGlobalSearch(QStringFromStdString(msg.request_global_search().query()),
346 client);
347 }
348
CreateSongFromProtobuf(const pb::remote::SongMetadata & pb)349 Song IncomingDataParser::CreateSongFromProtobuf(const pb::remote::SongMetadata& pb){
350 Song song;
351 song.Init(QStringFromStdString(pb.title()),
352 QStringFromStdString(pb.artist()),
353 QStringFromStdString(pb.album()),
354 pb.length() * kNsecPerSec);
355
356 song.set_albumartist(QStringFromStdString(pb.albumartist()));
357 song.set_genre(QStringFromStdString(pb.genre()));
358 song.set_year(QStringFromStdString(pb.pretty_year()).toInt());
359 song.set_track(pb.track());
360 song.set_disc(pb.disc());
361 song.set_url(QUrl(QStringFromStdString(pb.url())));
362 song.set_filesize(pb.file_size());
363 song.set_rating(pb.rating());
364 song.set_basefilename(QStringFromStdString(pb.filename()));
365 song.set_art_automatic(QStringFromStdString(pb.art_automatic()));
366 song.set_art_manual(QStringFromStdString(pb.art_manual()));
367 song.set_filetype(static_cast<Song::FileType>(pb.type()));
368
369 return song;
370 }
371