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 TIDALREQUEST_H
21 #define TIDALREQUEST_H
22 
23 #include "config.h"
24 
25 #include <QtGlobal>
26 #include <QObject>
27 #include <QPair>
28 #include <QSet>
29 #include <QList>
30 #include <QHash>
31 #include <QMap>
32 #include <QMultiMap>
33 #include <QQueue>
34 #include <QVariant>
35 #include <QString>
36 #include <QStringList>
37 #include <QUrl>
38 #include <QJsonObject>
39 
40 #include "core/song.h"
41 #include "tidalbaserequest.h"
42 
43 class QNetworkReply;
44 class Application;
45 class NetworkAccessManager;
46 class TidalService;
47 class TidalUrlHandler;
48 
49 class TidalRequest : public TidalBaseRequest {
50   Q_OBJECT
51 
52  public:
53   explicit TidalRequest(TidalService *service, TidalUrlHandler *url_handler, Application *app, NetworkAccessManager *network, QueryType type, QObject *parent);
54   ~TidalRequest() override;
55 
56   void ReloadSettings();
57 
58   void Process();
set_need_login()59   void set_need_login() override { need_login_ = true; }
60   void Search(const int query_id, const QString &search_text);
61 
62  private:
63   struct Request {
RequestRequest64     Request() : offset(0), limit(0), album_explicit(false) {}
65     QString artist_id;
66     QString album_id;
67     QString song_id;
68     int offset;
69     int limit;
70     QString album_artist;
71     QString album;
72     bool album_explicit;
73   };
74   struct AlbumCoverRequest {
75     QString artist_id;
76     QString album_id;
77     QUrl url;
78     QString filename;
79   };
80 
81  signals:
82   void LoginSuccess();
83   void LoginFailure(QString failure_reason);
84   void Results(int id, SongMap songs, QString error);
85   void UpdateStatus(int id, QString text);
86   void ProgressSetMaximum(int id, int max);
87   void UpdateProgress(int id, int max);
88   void StreamURLFinished(QUrl original_url, QUrl url, Song::FileType, QString error = QString());
89 
90  private slots:
91   void ArtistsReplyReceived(QNetworkReply *reply, const int limit_requested, const int offset_requested);
92 
93   void AlbumsReplyReceived(QNetworkReply *reply, const int limit_requested, const int offset_requested);
94   void AlbumsReceived(QNetworkReply *reply, const QString &artist_id_requested, const int limit_requested, const int offset_requested, const bool auto_login);
95 
96   void SongsReplyReceived(QNetworkReply *reply, const int limit_requested, const int offset_requested);
97   void SongsReceived(QNetworkReply *reply, const QString &artist_id, const QString &album_id, const int limit_requested, const int offset_requested, const bool auto_login = false, const QString &album_artist = QString(), const QString &album = QString(), const bool album_explicit = false);
98 
99   void ArtistAlbumsReplyReceived(QNetworkReply *reply, const QString &artist_id, const int offset_requested);
100   void AlbumSongsReplyReceived(QNetworkReply *reply, const QString &artist_id, const QString &album_id, const int offset_requested, const QString &album_artist, const QString &album, const bool album_explicit);
101   void AlbumCoverReceived(QNetworkReply *reply, const QString &album_id, const QUrl &url, const QString &filename);
102 
103  public slots:
104   void LoginComplete(const bool success, const QString &error = QString());
105 
106  private:
IsQuery()107   bool IsQuery() { return (type_ == QueryType_Artists || type_ == QueryType_Albums || type_ == QueryType_Songs); }
IsSearch()108   bool IsSearch() { return (type_ == QueryType_SearchArtists || type_ == QueryType_SearchAlbums || type_ == QueryType_SearchSongs); }
109 
110   void GetArtists();
111   void GetAlbums();
112   void GetSongs();
113 
114   void ArtistsSearch();
115   void AlbumsSearch();
116   void SongsSearch();
117 
118   void AddArtistsRequest(const int offset = 0, const int limit = 0);
119   void AddArtistsSearchRequest(const int offset = 0);
120   void FlushArtistsRequests();
121   void AddAlbumsRequest(const int offset = 0, const int limit = 0);
122   void AddAlbumsSearchRequest(const int offset = 0);
123   void FlushAlbumsRequests();
124   void AddSongsRequest(const int offset = 0, const int limit = 0);
125   void AddSongsSearchRequest(const int offset = 0);
126   void FlushSongsRequests();
127 
128   void ArtistsFinishCheck(const int limit = 0, const int offset = 0, const int artists_received = 0);
129   void AlbumsFinishCheck(const QString &artist_id, const int limit = 0, const int offset = 0, const int albums_total = 0, const int albums_received = 0);
130   void SongsFinishCheck(const QString &artist_id, const QString &album_id, const int limit, const int offset, const int songs_total, const int songs_received, const QString &album_artist, const QString &album, const bool album_explicit);
131 
132   void AddArtistAlbumsRequest(const QString &artist_id, const int offset = 0);
133   void FlushArtistAlbumsRequests();
134 
135   void AddAlbumSongsRequest(const QString &artist_id, const QString &album_id, const QString &album_artist, const QString &album, const bool album_explicit, const int offset = 0);
136   void FlushAlbumSongsRequests();
137 
138   QString ParseSong(Song &song, const QJsonObject &json_obj, const QString &artist_id_requested = QString(), const QString &album_id_requested = QString(), const QString &album_artist = QString(), const QString &album_album = QString(), const bool album_explicit = false);
139 
140   void GetAlbumCovers();
141   void AddAlbumCoverRequest(const Song &song);
142   void FlushAlbumCoverRequests();
143   void AlbumCoverFinishCheck();
144 
145   void FinishCheck();
146   static void Warn(const QString &error, const QVariant &debug = QVariant());
147   void Error(const QString &error, const QVariant &debug = QVariant()) override;
148 
149   static const char *kResourcesUrl;
150   static const int kMaxConcurrentArtistsRequests;
151   static const int kMaxConcurrentAlbumsRequests;
152   static const int kMaxConcurrentSongsRequests;
153   static const int kMaxConcurrentArtistAlbumsRequests;
154   static const int kMaxConcurrentAlbumSongsRequests;
155   static const int kMaxConcurrentAlbumCoverRequests;
156 
157   TidalService *service_;
158   TidalUrlHandler *url_handler_;
159   Application *app_;
160   NetworkAccessManager *network_;
161 
162   QueryType type_;
163   bool fetchalbums_;
164   QString coversize_;
165 
166   int query_id_;
167   QString search_text_;
168 
169   bool finished_;
170 
171   QQueue<Request> artists_requests_queue_;
172   QQueue<Request> albums_requests_queue_;
173   QQueue<Request> songs_requests_queue_;
174 
175   QQueue<Request> artist_albums_requests_queue_;
176   QQueue<Request> album_songs_requests_queue_;
177   QQueue<AlbumCoverRequest> album_cover_requests_queue_;
178 
179   QList<QString> artist_albums_requests_pending_;
180   QHash<QString, Request> album_songs_requests_pending_;
181   QMultiMap<QString, QString> album_covers_requests_sent_;
182 
183   int artists_requests_active_;
184   int artists_total_;
185   int artists_received_;
186 
187   int albums_requests_active_;
188   int songs_requests_active_;
189 
190   int artist_albums_requests_active_;
191   int artist_albums_requested_;
192   int artist_albums_received_;
193 
194   int album_songs_requests_active_;
195   int album_songs_requested_;
196   int album_songs_received_;
197 
198   int album_covers_requests_active_;
199   int album_covers_requested_;
200   int album_covers_received_;
201 
202   SongMap songs_;
203   QStringList errors_;
204   bool need_login_;
205   bool no_results_;
206   QList<QNetworkReply*> replies_;
207   QList<QNetworkReply*> album_cover_replies_;
208 
209 };
210 
211 #endif  // TIDALREQUEST_H
212