1 /*
2  *      Copyright (C) 2015-2019 Jean-Luc Barriere
3  *
4  *  This file is part of Noson-App
5  *
6  *  Noson 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  *  Noson 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 Noson.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 #ifndef NOSONAPPSONOS_H
22 #define NOSONAPPSONOS_H
23 
24 #include <noson/sonossystem.h>
25 
26 #include "tools.h"
27 #include "locked.h"
28 #include "future.h"
29 #include "zonesmodel.h"
30 #include "roomsmodel.h"
31 #include "albumsmodel.h"
32 #include "artistsmodel.h"
33 #include "composersmodel.h"
34 #include "genresmodel.h"
35 #include "tracksmodel.h"
36 #include "queuemodel.h"
37 #include "playlistsmodel.h"
38 #include "favoritesmodel.h"
39 #include "servicesmodel.h"
40 #include "mediamodel.h"
41 #include "allservicesmodel.h"
42 
43 #include <QObject>
44 #include <QString>
45 #include <QQmlEngine>
46 #include <QThread>
47 #include <QThreadPool>
48 
49 namespace nosonapp
50 {
51 
52 class Sonos : public QObject, public ContentProvider<Sonos>
53 {
54   Q_OBJECT
55   Q_PROPERTY(int jobCount READ jobCount NOTIFY jobCountChanged)
56   Q_PROPERTY(QString systemLocalURI READ systemLocalURI CONSTANT)
57 
58 public:
59   explicit Sonos(QObject *parent = 0);
60   ~Sonos();
61 
62   Q_INVOKABLE void debug(int debug);
63 
64   ///////////////////////////////////////////////////////////////////////////////
65   ///
66   /// About futures
67 
68   Q_INVOKABLE Future* tryInit(int debug = 0);
69   Q_INVOKABLE Future* tryRenewSubscriptions();
70   Q_INVOKABLE Future* tryJoinZones(const QVariantList& zonePayloads, const QVariant& toZonePayload);
71   Q_INVOKABLE Future* tryUnjoinZone(const QVariant& zonePayload);
72   Q_INVOKABLE Future* tryUnjoinRooms(const QVariantList& roomPayloads);
73   Q_INVOKABLE Future* tryCreateAlarm(const QVariant& alarmPayload);
74   Q_INVOKABLE Future* tryUpdateAlarm(const QVariant& alarmPayload);
75   Q_INVOKABLE Future* tryDestroyAlarm(const QString& id);
76   Q_INVOKABLE Future* tryRefreshShareIndex();
77   Q_INVOKABLE Future* tryDestroySavedQueue(const QString& SQid);
78   Q_INVOKABLE Future* tryAddItemToFavorites(const QVariant& payload, const QString& description, const QString& artURI);
79   Q_INVOKABLE Future* tryDestroyFavorite(const QString& FVid);
80 
81   ///////////////////////////////////////////////////////////////////////////////
82   ///
83   /// Synchonous
84 
85   Q_INVOKABLE bool init(int debug = 0);
86   Q_INVOKABLE bool init(int debug, const QString& url);
87 
88   Q_INVOKABLE void setLocale(const QString& locale);
89 
90   Q_INVOKABLE QString getLocale();
91 
92   Q_INVOKABLE QString getLibVersion();
93 
94   Q_INVOKABLE void addServiceOAuth(const QString& type, const QString& sn, const QString& key, const QString& token, const QString& username);
95   Q_INVOKABLE void deleteServiceOAuth(const QString& type, const QString& sn);
96 
97   Q_INVOKABLE void renewSubscriptions();
98 
99   Q_INVOKABLE QVariantList getZones();
100 
101   Q_INVOKABLE bool isConnected();
102 
103   Q_INVOKABLE QVariantList getZoneRooms(const QString& zoneId);
104 
105   Q_INVOKABLE bool joinRoom(const QVariant& roomPayload, const QVariant& toZonePayload);
106 
107   Q_INVOKABLE bool joinZone(const QVariant& zonePayload, const QVariant& toZonePayload);
108   Q_INVOKABLE bool joinZones(const QVariantList& zonePayloads, const QVariant& toZonePayload);
109 
110   Q_INVOKABLE bool unjoinRoom(const QVariant& roomPayload);
111   Q_INVOKABLE bool unjoinRooms(const QVariantList& roomPayloads);
112 
113   Q_INVOKABLE bool unjoinZone(const QVariant& zonePayload);
114 
115   Q_INVOKABLE bool createAlarm(const QVariant& alarmPayload);
116   Q_INVOKABLE bool updateAlarm(const QVariant& alarmPayload);
117   Q_INVOKABLE bool destroyAlarm(const QString& id);
118 
119   Q_INVOKABLE bool refreshShareIndex();
120   Q_INVOKABLE bool destroySavedQueue(const QString& SQid);
121   Q_INVOKABLE bool addItemToFavorites(const QVariant& payload, const QString& description, const QString& artURI);
122   Q_INVOKABLE bool destroyFavorite(const QString& FVid);
123 
124   Q_INVOKABLE QString getObjectIDFromUriMetadata(const QVariant& itemPayload);
125 
126   Q_INVOKABLE bool isItemFromService(const QVariant& itemPayload);
127 
128   SONOS::System& getSystem();
129   SONOS::ZonePtr findZone(const QString& zoneName);
130 
131   Q_INVOKABLE void runLoader();
132 
133   // Implements ContentProvider
134   void beforeLoad();
135   void afterLoad();
136   void runContentLoader(ListModel<Sonos>* model);
137   void loadContent(ListModel<Sonos>* model);
138   void loadAllContent();
139   void runContentLoaderForContext(ListModel<Sonos>* model, int id);
140   void loadContentForContext(ListModel<Sonos>* model, int id);
141   const char* getHost() const;
142   unsigned getPort() const;
143   QString getBaseUrl() const;
144   void registerContent(ListModel<Sonos>* model, const QString& root);
145   void unregisterContent(ListModel<Sonos>* model);
146 
147   // About jobs
148   bool startJob(QRunnable* worker);
jobCount()149   int jobCount() { return *(m_jobCount.Get()); }
150   void beginJob();
151   void endJob();
152 
systemLocalURI()153   const QString& systemLocalURI() { return m_systemLocalURI; }
154 
155   // Define singleton provider functions
sonos_provider(QQmlEngine * engine,QJSEngine * scriptEngine)156   static QObject* sonos_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
157   {
158     Q_UNUSED(engine)
159     Q_UNUSED(scriptEngine)
160     return new Sonos;
161   }
162 
allZonesModel_provider(QQmlEngine * engine,QJSEngine * scriptEngine)163   static QObject* allZonesModel_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
164   {
165     Q_UNUSED(engine)
166     Q_UNUSED(scriptEngine)
167     return new ZonesModel;
168   }
169 
allAlbumsModel_provider(QQmlEngine * engine,QJSEngine * scriptEngine)170   static QObject* allAlbumsModel_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
171   {
172     Q_UNUSED(engine)
173     Q_UNUSED(scriptEngine)
174     return new AlbumsModel;
175   }
176 
allArtistsModel_provider(QQmlEngine * engine,QJSEngine * scriptEngine)177   static QObject* allArtistsModel_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
178   {
179     Q_UNUSED(engine)
180     Q_UNUSED(scriptEngine)
181     return new ArtistsModel;
182   }
183 
allComposersModel_provider(QQmlEngine * engine,QJSEngine * scriptEngine)184   static QObject* allComposersModel_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
185   {
186     Q_UNUSED(engine)
187     Q_UNUSED(scriptEngine)
188     return new ComposersModel;
189   }
190 
allGenresModel_provider(QQmlEngine * engine,QJSEngine * scriptEngine)191   static QObject* allGenresModel_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
192   {
193     Q_UNUSED(engine)
194     Q_UNUSED(scriptEngine)
195     return new GenresModel;
196   }
197 
allPlaylistsModel_provider(QQmlEngine * engine,QJSEngine * scriptEngine)198   static QObject* allPlaylistsModel_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
199   {
200     Q_UNUSED(engine)
201     Q_UNUSED(scriptEngine)
202     return new PlaylistsModel;
203   }
204 
allFavoritesModel_provider(QQmlEngine * engine,QJSEngine * scriptEngine)205   static QObject* allFavoritesModel_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
206   {
207     Q_UNUSED(engine)
208     Q_UNUSED(scriptEngine)
209     return new FavoritesModel;
210   }
211 
allServicesModel_provider(QQmlEngine * engine,QJSEngine * scriptEngine)212   static QObject* allServicesModel_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
213   {
214     Q_UNUSED(engine)
215     Q_UNUSED(scriptEngine)
216     return new AllServicesModel;
217   }
218 
MyServicesModel_provider(QQmlEngine * engine,QJSEngine * scriptEngine)219   static QObject* MyServicesModel_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
220   {
221     Q_UNUSED(engine)
222     Q_UNUSED(scriptEngine)
223     return new ServicesModel;
224   }
225 
226   // Helpers
normalizedInputString(const QString & str)227   Q_INVOKABLE static QString normalizedInputString(const QString& str)
228   {
229     return nosonapp::normalizedString(str);
230   }
231 
havePulseAudio()232   Q_INVOKABLE static bool havePulseAudio()
233   {
234     return SONOS::System::HavePulseAudio();
235   }
236 
237 signals:
238   void initDone(bool succeeded);
239   void loadingStarted();
240   void loadingFinished();
241   void transportChanged();
242   void renderingControlChanged();
243   void topologyChanged();
244   void alarmClockChanged();
245   void shareIndexInProgress();
246   void shareIndexFinished();
247 
248   void jobCountChanged();
249 
250 private:
251   typedef QList<RegisteredContent<Sonos> > ManagedContents;
252   Locked<ManagedContents> m_library;
253   unsigned m_shareUpdateID; // Current updateID of SONOS shares
254   bool m_shareIndexInProgess;
255   unsigned m_savedQueuesUpdateID; // Current updatedID of SONOS Playlists
256 
257   SONOS::System m_system;
258   QThreadPool m_workerPool;
259   LockedNumber<int> m_jobCount;
260   QString m_systemLocalURI;
261 
262   Locked<QString> m_locale; // language_COUNTRY
263 
264   static void systemEventCB(void *handle);
265 
266   static void unregisterContent(ManagedContents& mc, ListModel<Sonos> *model);
267 
268   ///////////////////////////////////////////////////////////////////////////////
269   ///
270   /// About promises
271 
272   class PromiseInit : public Promise
273   {
274   public:
PromiseInit(Sonos & sonos,int debug)275     PromiseInit(Sonos& sonos, int debug)
276     : m_sonos(sonos), m_debug(debug) { }
277     void run() override;
278   private:
279     Sonos& m_sonos;
280     int m_debug;
281   };
282 
283   class PromiseRenewSubscriptions : public Promise
284   {
285   public:
PromiseRenewSubscriptions(Sonos & sonos)286     PromiseRenewSubscriptions(Sonos& sonos)
287     : m_sonos(sonos) { }
288     void run() override;
289   private:
290     Sonos& m_sonos;
291   };
292 
293   class PromiseJoinZones : public Promise
294   {
295   public:
PromiseJoinZones(Sonos & sonos,const QVariantList & zonePayloads,const QVariant & toZonePayload)296     PromiseJoinZones(Sonos& sonos, const QVariantList& zonePayloads, const QVariant& toZonePayload)
297     : m_sonos(sonos), m_zonePayloads(zonePayloads), m_toZonePayload(toZonePayload) { }
298     void run() override;
299   private:
300     Sonos& m_sonos;
301     const QVariantList m_zonePayloads;
302     const QVariant m_toZonePayload;
303   };
304 
305   class PromiseUnjoinZone : public Promise
306   {
307   public:
PromiseUnjoinZone(Sonos & sonos,const QVariant & zonePayload)308     PromiseUnjoinZone(Sonos& sonos, const QVariant& zonePayload)
309     : m_sonos(sonos), m_zonePayload(zonePayload) { }
310     void run() override;
311   private:
312     Sonos& m_sonos;
313     const QVariant m_zonePayload;
314   };
315 
316   class PromiseUnjoinRooms : public Promise
317   {
318   public:
PromiseUnjoinRooms(Sonos & sonos,const QVariantList & roomPayloads)319     PromiseUnjoinRooms(Sonos& sonos, const QVariantList& roomPayloads)
320     : m_sonos(sonos), m_roomPayloads(roomPayloads) { }
321     void run() override;
322   private:
323     Sonos& m_sonos;
324     const QVariantList m_roomPayloads;
325   };
326 
327   class PromiseCreateAlarm : public Promise
328   {
329   public:
PromiseCreateAlarm(Sonos & sonos,const QVariant & alarmPayload)330     PromiseCreateAlarm(Sonos& sonos, const QVariant& alarmPayload)
331     : m_sonos(sonos), m_alarmPayload(alarmPayload) { }
332     void run() override;
333   private:
334     Sonos& m_sonos;
335     const QVariant m_alarmPayload;
336   };
337 
338   class PromiseUpdateAlarm : public Promise
339   {
340   public:
PromiseUpdateAlarm(Sonos & sonos,const QVariant & alarmPayload)341     PromiseUpdateAlarm(Sonos& sonos, const QVariant& alarmPayload)
342     : m_sonos(sonos), m_alarmPayload(alarmPayload) { }
343     void run() override;
344   private:
345     Sonos& m_sonos;
346     const QVariant m_alarmPayload;
347   };
348 
349   class PromiseDestroyAlarm : public Promise
350   {
351   public:
PromiseDestroyAlarm(Sonos & sonos,const QString & id)352     PromiseDestroyAlarm(Sonos& sonos, const QString& id)
353     : m_sonos(sonos), m_id(id) { }
354     void run() override;
355   private:
356     Sonos& m_sonos;
357     const QString m_id;
358   };
359 
360   class PromiseRefreshShareIndex : public Promise
361   {
362   public:
PromiseRefreshShareIndex(Sonos & sonos)363     PromiseRefreshShareIndex(Sonos& sonos)
364     : m_sonos(sonos) { }
365     void run() override;
366   private:
367     Sonos& m_sonos;
368   };
369 
370   class PromiseDestroySavedQueue : public Promise
371   {
372   public:
PromiseDestroySavedQueue(Sonos & sonos,const QString & SQid)373     PromiseDestroySavedQueue(Sonos& sonos, const QString& SQid)
374     : m_sonos(sonos), m_SQid(SQid) { }
375     void run() override;
376   private:
377     Sonos& m_sonos;
378     const QString m_SQid;
379   };
380 
381   class PromiseAddItemToFavorites : public Promise
382   {
383   public:
PromiseAddItemToFavorites(Sonos & sonos,const QVariant & payload,const QString & description,const QString & artURI)384     PromiseAddItemToFavorites(Sonos& sonos, const QVariant& payload, const QString& description, const QString& artURI)
385     : m_sonos(sonos), m_payload(payload), m_description(description), m_artURI(artURI) { }
386     void run() override;
387   private:
388     Sonos& m_sonos;
389     QVariant m_payload;
390     const QString m_description;
391     const QString m_artURI;
392   };
393 
394   class PromiseDestroyFavorite : public Promise
395   {
396   public:
PromiseDestroyFavorite(Sonos & sonos,const QString & FVid)397     PromiseDestroyFavorite(Sonos& sonos, const QString& FVid)
398     : m_sonos(sonos), m_FVid(FVid) { }
399     void run() override;
400   private:
401     Sonos& m_sonos;
402     const QString m_FVid;
403   };
404 
405 };
406 
407 }
408 
409 #endif // NOSONAPPSONOS_H
410