1 /*
2  * Strawberry Music Player
3  * Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
4  *
5  * Strawberry is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * Strawberry is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Strawberry.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #ifndef TIDALSERVICE_H
21 #define TIDALSERVICE_H
22 
23 #include "config.h"
24 
25 #include <memory>
26 
27 #include <QtGlobal>
28 #include <QObject>
29 #include <QPair>
30 #include <QSet>
31 #include <QList>
32 #include <QMap>
33 #include <QVariant>
34 #include <QByteArray>
35 #include <QString>
36 #include <QStringList>
37 #include <QUrl>
38 #include <QDateTime>
39 #include <QSslError>
40 
41 #include "core/song.h"
42 #include "internet/internetservice.h"
43 #include "internet/internetsearchview.h"
44 #include "settings/tidalsettingspage.h"
45 
46 class QSortFilterProxyModel;
47 class QNetworkReply;
48 class QTimer;
49 
50 class Application;
51 class NetworkAccessManager;
52 class TidalUrlHandler;
53 class TidalRequest;
54 class TidalFavoriteRequest;
55 class TidalStreamURLRequest;
56 class CollectionBackend;
57 class CollectionModel;
58 
59 class TidalService : public InternetService {
60   Q_OBJECT
61 
62  public:
63   explicit TidalService(Application *app, QObject *parent);
64   ~TidalService() override;
65 
66   static const Song::Source kSource;
67   static const char *kApiUrl;
68   static const char *kResourcesUrl;
69 
70   void Exit() override;
71   void ReloadSettings() override;
72 
73   void Logout();
74   int Search(const QString &text, InternetSearchView::SearchType type) override;
75   void CancelSearch() override;
76 
max_login_attempts()77   int max_login_attempts() { return kLoginAttempts; }
78 
app()79   Application *app() { return app_; }
80 
oauth()81   bool oauth() override { return oauth_; }
client_id()82   QString client_id() { return client_id_; }
api_token()83   QString api_token() { return api_token_; }
user_id()84   quint64 user_id() { return user_id_; }
country_code()85   QString country_code() { return country_code_; }
username()86   QString username() { return username_; }
password()87   QString password() { return password_; }
quality()88   QString quality() { return quality_; }
artistssearchlimit()89   int artistssearchlimit() { return artistssearchlimit_; }
albumssearchlimit()90   int albumssearchlimit() { return albumssearchlimit_; }
songssearchlimit()91   int songssearchlimit() { return songssearchlimit_; }
fetchalbums()92   bool fetchalbums() { return fetchalbums_; }
coversize()93   QString coversize() { return coversize_; }
download_album_covers()94   bool download_album_covers() { return download_album_covers_; }
stream_url_method()95   TidalSettingsPage::StreamUrlMethod stream_url_method() { return stream_url_method_; }
album_explicit()96   bool album_explicit() { return album_explicit_; }
97 
access_token()98   QString access_token() { return access_token_; }
session_id()99   QString session_id() { return session_id_; }
100 
authenticated()101   bool authenticated() override { return (!access_token_.isEmpty() || !session_id_.isEmpty()); }
login_sent()102   bool login_sent() { return login_sent_; }
login_attempts()103   bool login_attempts() { return login_attempts_; }
104 
105   void GetStreamURL(const QUrl &url);
106 
artists_collection_backend()107   CollectionBackend *artists_collection_backend() override { return artists_collection_backend_; }
albums_collection_backend()108   CollectionBackend *albums_collection_backend() override { return albums_collection_backend_; }
songs_collection_backend()109   CollectionBackend *songs_collection_backend() override { return songs_collection_backend_; }
110 
artists_collection_model()111   CollectionModel *artists_collection_model() override { return artists_collection_model_; }
albums_collection_model()112   CollectionModel *albums_collection_model() override { return albums_collection_model_; }
songs_collection_model()113   CollectionModel *songs_collection_model() override { return songs_collection_model_; }
114 
artists_collection_sort_model()115   QSortFilterProxyModel *artists_collection_sort_model() override { return artists_collection_sort_model_; }
albums_collection_sort_model()116   QSortFilterProxyModel *albums_collection_sort_model() override { return albums_collection_sort_model_; }
songs_collection_sort_model()117   QSortFilterProxyModel *songs_collection_sort_model() override { return songs_collection_sort_model_; }
118 
119  public slots:
120   void ShowConfig() override;
121   void StartAuthorization(const QString &client_id);
122   void TryLogin();
123   void SendLogin();
124   void SendLoginWithCredentials(const QString &api_token, const QString &username, const QString &password);
125   void GetArtists() override;
126   void GetAlbums() override;
127   void GetSongs() override;
128   void ResetArtistsRequest() override;
129   void ResetAlbumsRequest() override;
130   void ResetSongsRequest() override;
131   void AuthorizationUrlReceived(const QUrl &url);
132 
133  private slots:
134   void ExitReceived();
RequestNewAccessToken()135   void RequestNewAccessToken() { RequestAccessToken(); }
136   void HandleLoginSSLErrors(const QList<QSslError> &ssl_errors);
137   void AccessTokenRequestFinished(QNetworkReply *reply);
138   void HandleAuthReply(QNetworkReply *reply);
139   void ResetLoginAttempts();
140   void StartSearch();
141   void ArtistsResultsReceived(const int id, const SongMap &songs, const QString &error);
142   void AlbumsResultsReceived(const int id, const SongMap &songs, const QString &error);
143   void SongsResultsReceived(const int id, const SongMap &songs, const QString &error);
144   void SearchResultsReceived(const int id, const SongMap &songs, const QString &error);
145   void ArtistsUpdateStatusReceived(const int id, const QString &text);
146   void AlbumsUpdateStatusReceived(const int id, const QString &text);
147   void SongsUpdateStatusReceived(const int id, const QString &text);
148   void ArtistsProgressSetMaximumReceived(const int id, const int max);
149   void AlbumsProgressSetMaximumReceived(const int id, const int max);
150   void SongsProgressSetMaximumReceived(const int id, const int max);
151   void ArtistsUpdateProgressReceived(const int id, const int progress);
152   void AlbumsUpdateProgressReceived(const int id, const int progress);
153   void SongsUpdateProgressReceived(const int id, const int progress);
154   void HandleStreamURLFinished(const int id, const QUrl &original_url, const QUrl &stream_url, const Song::FileType filetype, const int samplerate, const int bit_depth, const qint64 duration, const QString &error = QString());
155 
156  private:
157   typedef QPair<QString, QString> Param;
158   typedef QList<Param> ParamList;
159 
160   void LoadSession();
161   void RequestAccessToken(const QString &code = QString());
162   void SendSearch();
163   void LoginError(const QString &error = QString(), const QVariant &debug = QVariant());
164 
165   static const char *kOAuthUrl;
166   static const char *kOAuthAccessTokenUrl;
167   static const char *kOAuthRedirectUrl;
168   static const char *kAuthUrl;
169 
170   static const int kLoginAttempts;
171   static const int kTimeResetLoginAttempts;
172 
173   static const char *kArtistsSongsTable;
174   static const char *kAlbumsSongsTable;
175   static const char *kSongsTable;
176 
177   static const char *kArtistsSongsFtsTable;
178   static const char *kAlbumsSongsFtsTable;
179   static const char *kSongsFtsTable;
180 
181   Application *app_;
182   NetworkAccessManager *network_;
183   TidalUrlHandler *url_handler_;
184 
185   CollectionBackend *artists_collection_backend_;
186   CollectionBackend *albums_collection_backend_;
187   CollectionBackend *songs_collection_backend_;
188 
189   CollectionModel *artists_collection_model_;
190   CollectionModel *albums_collection_model_;
191   CollectionModel *songs_collection_model_;
192 
193   QSortFilterProxyModel *artists_collection_sort_model_;
194   QSortFilterProxyModel *albums_collection_sort_model_;
195   QSortFilterProxyModel *songs_collection_sort_model_;
196 
197   QTimer *timer_search_delay_;
198   QTimer *timer_login_attempt_;
199   QTimer *timer_refresh_login_;
200 
201   std::shared_ptr<TidalRequest> artists_request_;
202   std::shared_ptr<TidalRequest> albums_request_;
203   std::shared_ptr<TidalRequest> songs_request_;
204   std::shared_ptr<TidalRequest> search_request_;
205   TidalFavoriteRequest *favorite_request_;
206 
207   bool enabled_;
208   bool oauth_;
209   QString client_id_;
210   QString api_token_;
211   quint64 user_id_;
212   QString country_code_;
213   QString username_;
214   QString password_;
215   QString quality_;
216   int artistssearchlimit_;
217   int albumssearchlimit_;
218   int songssearchlimit_;
219   bool fetchalbums_;
220   QString coversize_;
221   bool download_album_covers_;
222   TidalSettingsPage::StreamUrlMethod stream_url_method_;
223   bool album_explicit_;
224 
225   QString access_token_;
226   QString refresh_token_;
227   QString session_id_;
228   quint64 expires_in_;
229   quint64 login_time_;
230 
231   int pending_search_id_;
232   int next_pending_search_id_;
233   QString pending_search_text_;
234   InternetSearchView::SearchType pending_search_type_;
235 
236   int search_id_;
237   QString search_text_;
238   bool login_sent_;
239   int login_attempts_;
240 
241   QString code_verifier_;
242   QString code_challenge_;
243 
244   int next_stream_url_request_id_;
245   QMap<int, std::shared_ptr<TidalStreamURLRequest>> stream_url_requests_;
246 
247   QStringList login_errors_;
248 
249   QList<QObject*> wait_for_exit_;
250   QList<QNetworkReply*> replies_;
251 
252 };
253 
254 #endif  // TIDALSERVICE_H
255