1 /* This file is part of Clementine.
2    Copyright 2011, Tyler Rhodes <tyler.s.rhodes@gmail.com>
3    Copyright 2011-2012, 2014, Arnaud Bienner <arnaud.bienner@gmail.com>
4    Copyright 2011-2012, 2014, John Maguire <john.maguire@gmail.com>
5    Copyright 2011-2012, 2014, David Sansome <me@davidsansome.com>
6    Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
7 
8    Clementine is free software: you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation, either version 3 of the License, or
11    (at your option) any later version.
12 
13    Clementine is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with Clementine.  If not, see <http://www.gnu.org/licenses/>.
20 */
21 
22 #ifndef INTERNET_SPOTIFY_SPOTIFYSERVICE_H_
23 #define INTERNET_SPOTIFY_SPOTIFYSERVICE_H_
24 
25 #include "internet/core/internetmodel.h"
26 #include "internet/core/internetservice.h"
27 #include "spotifymessages.pb.h"
28 
29 #include <QProcess>
30 #include <QTimer>
31 
32 class Playlist;
33 class SearchBoxWidget;
34 class SpotifyServer;
35 
36 class QMenu;
37 
38 class SpotifyService : public InternetService {
39   Q_OBJECT
40 
41  public:
42   SpotifyService(Application* app, InternetModel* parent);
43   ~SpotifyService();
44 
45   enum Type {
46     Type_SearchResults = InternetModel::TypeCount,
47     Type_StarredPlaylist,
48     Type_InboxPlaylist,
49     Type_Toplist,
50   };
51 
52   enum Role {
53     Role_UserPlaylistIndex = InternetModel::RoleCount,
54   };
55 
56   // Values are persisted - don't change.
57   enum LoginState {
58     LoginState_LoggedIn = 1,
59     LoginState_Banned = 2,
60     LoginState_BadCredentials = 3,
61     LoginState_NoPremium = 4,
62     LoginState_OtherError = 5,
63     LoginState_ReloginFailed = 6
64   };
65 
66   static const char* kServiceName;
67   static const char* kSettingsGroup;
68   static const char* kBlobDownloadUrl;
69   static const int kSearchDelayMsec;
70 
71   void ReloadSettings() override;
72 
73   QStandardItem* CreateRootItem() override;
74   void LazyPopulate(QStandardItem* parent) override;
75   void ShowContextMenu(const QPoint& global_pos) override;
76   void ItemDoubleClicked(QStandardItem* item) override;
77   void DropMimeData(const QMimeData* data, const QModelIndex& index) override;
78   QList<QAction*> playlistitem_actions(const Song& song) override;
79   PlaylistItem::Options playlistitem_options() const override;
80   QWidget* HeaderWidget() const override;
81 
82   void Logout();
83   void Login(const QString& username, const QString& password);
84   Q_INVOKABLE void LoadImage(const QString& id);
85   Q_INVOKABLE void SetPaused(bool paused);
86 
87   SpotifyServer* server() const;
88 
89   bool IsBlobInstalled() const;
90   void InstallBlob();
91 
92   // Persisted in the settings and updated on each Login().
login_state()93   LoginState login_state() const { return login_state_; }
IsLoggedIn()94   bool IsLoggedIn() const { return login_state_ == LoginState_LoggedIn; }
95 
96   static void SongFromProtobuf(const pb::spotify::Track& track, Song* song);
97 
98  signals:
99   void BlobStateChanged();
100   void LoginFinished(bool success);
101   void ImageLoaded(const QString& id, const QImage& image);
102 
103  public slots:
104   void Search(const QString& text, bool now = false);
105   void ShowConfig() override;
106   void RemoveCurrentFromPlaylist();
107 
108  private:
109   void StartBlobProcess();
110   void FillPlaylist(
111       QStandardItem* item,
112       const google::protobuf::RepeatedPtrField<pb::spotify::Track>& tracks);
113   void FillPlaylist(QStandardItem* item,
114                     const pb::spotify::LoadPlaylistResponse& response);
115   void AddSongsToUserPlaylist(int playlist_index,
116                               const QList<QUrl>& songs_urls);
117   void AddSongsToStarred(const QList<QUrl>& songs_urls);
118   void EnsureMenuCreated();
119   // Create a new "show config" action. The caller is responsible for deleting
120   // the pointer (or adding it to menu or anything else that will take ownership
121   // of it)
122   QAction* GetNewShowConfigAction();
123   void InitSearch();
124   void ClearSearchResults();
125   QStandardItem* PlaylistBySpotifyIndex(int index) const;
126   bool DoPlaylistsDiffer(const pb::spotify::Playlists& response) const;
127 
128  private slots:
129   void EnsureServerCreated(const QString& username = QString(),
130                            const QString& password = QString());
131   void BlobProcessError(QProcess::ProcessError error);
132   void LoginCompleted(bool success, const QString& error,
133                       pb::spotify::LoginResponse_Error error_code);
134   void AddCurrentSongToUserPlaylist(QAction* action);
135   void AddCurrentSongToStarredPlaylist();
136   void RemoveSongsFromUserPlaylist(int playlist_index,
137                                    const QList<int>& songs_indices_to_remove);
138   void RemoveSongsFromStarred(const QList<int>& songs_indices_to_remove);
139   void PlaylistsUpdated(const pb::spotify::Playlists& response);
140   void InboxLoaded(const pb::spotify::LoadPlaylistResponse& response);
141   void StarredLoaded(const pb::spotify::LoadPlaylistResponse& response);
142   void UserPlaylistLoaded(const pb::spotify::LoadPlaylistResponse& response);
143   void SearchResults(const pb::spotify::SearchResponse& response);
144   void SyncPlaylistProgress(const pb::spotify::SyncPlaylistProgress& progress);
145   void ToplistLoaded(const pb::spotify::BrowseToplistResponse& response);
146   void GetCurrentSongUrlToShare() const;
147   void GetCurrentPlaylistUrlToShare() const;
148 
149   void DoSearch();
150 
151   void SyncPlaylist();
152   void BlobDownloadFinished();
153 
154  private:
155   SpotifyServer* server_;
156 
157   QString system_blob_path_;
158   QString local_blob_version_;
159   QString local_blob_path_;
160   QProcess* blob_process_;
161 
162   QStandardItem* root_;
163   QStandardItem* search_;
164   QStandardItem* starred_;
165   QStandardItem* inbox_;
166   QStandardItem* toplist_;
167   QList<QStandardItem*> playlists_;
168 
169   int login_task_id_;
170   QString pending_search_;
171 
172   QMenu* context_menu_;
173   QMenu* playlist_context_menu_;
174   QMenu* song_context_menu_;
175   QAction* playlist_sync_action_;
176   QAction* get_url_to_share_playlist_;
177   QList<QAction*> playlistitem_actions_;
178   QAction* remove_from_playlist_;
179   QUrl current_song_url_;
180   QUrl current_playlist_url_;
181 
182   SearchBoxWidget* search_box_;
183 
184   QTimer* search_delay_;
185 
186   int inbox_sync_id_;
187   int starred_sync_id_;
188   QMap<int, int> playlist_sync_ids_;
189 
190   LoginState login_state_;
191   pb::spotify::Bitrate bitrate_;
192   bool volume_normalisation_;
193 };
194 
195 #endif  // INTERNET_SPOTIFY_SPOTIFYSERVICE_H_
196