1 //////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2004-2021 musikcube team
4 //
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions are met:
9 //
10 //    * Redistributions of source code must retain the above copyright notice,
11 //      this list of conditions and the following disclaimer.
12 //
13 //    * Redistributions in binary form must reproduce the above copyright
14 //      notice, this list of conditions and the following disclaimer in the
15 //      documentation and/or other materials provided with the distribution.
16 //
17 //    * Neither the name of the author nor the names of other contributors may
18 //      be used to endorse or promote products derived from this software
19 //      without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
32 //
33 //////////////////////////////////////////////////////////////////////////////
34 
35 #include "Context.h"
36 #include "Snapshots.h"
37 
38 #include <musikcore/sdk/constants.h>
39 #include <musikcore/sdk/ITrack.h>
40 
41 #pragma warning(push, 0)
42 #include <websocketpp/config/asio_no_tls.hpp>
43 #include <websocketpp/extensions/permessage_deflate/enabled.hpp>
44 #include <websocketpp/server.hpp>
45 #include <nlohmann/json.hpp>
46 #pragma warning(pop, 0)
47 
48 #include <mutex>
49 #include <condition_variable>
50 
51 class WebSocketServer {
52     public:
53         WebSocketServer(Context& context);
54         ~WebSocketServer();
55 
56         bool Start();
57         bool Stop();
58         void Wait();
59 
60         void OnTrackChanged(musik::core::sdk::ITrack* track);
61         void OnPlaybackStateChanged(musik::core::sdk::PlaybackState state);
62         void OnPlaybackTimeChanged(double time);
63         void OnVolumeChanged(double volume);
64         void OnModeChanged(musik::core::sdk::RepeatMode repeatMode, bool shuffled);
65         void OnPlayQueueChanged();
66 
67     private:
68         /* our special server config that supports gzip */
69         struct asio_with_deflate : public websocketpp::config::asio {
70             typedef asio_with_deflate type;
71             typedef asio base;
72 
73             typedef base::concurrency_type concurrency_type;
74 
75             typedef base::request_type request_type;
76             typedef base::response_type response_type;
77 
78             typedef base::message_type message_type;
79             typedef base::con_msg_manager_type con_msg_manager_type;
80             typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
81 
82             typedef base::alog_type alog_type;
83             typedef base::elog_type elog_type;
84 
85             typedef base::rng_type rng_type;
86 
87             struct transport_config : public base::transport_config {
88                 typedef type::concurrency_type concurrency_type;
89                 typedef type::alog_type alog_type;
90                 typedef type::elog_type elog_type;
91                 typedef type::request_type request_type;
92                 typedef type::response_type response_type;
93                 typedef websocketpp::transport::asio::basic_socket::endpoint
94                     socket_type;
95             };
96 
97             typedef websocketpp::transport::asio::endpoint<transport_config>
98                 transport_type;
99 
100             struct permessage_deflate_config {};
101 
102             typedef websocketpp::extensions::permessage_deflate::enabled
103                 <permessage_deflate_config> permessage_deflate_type;
104         };
105 
106         /* typedefs */
107         using server = websocketpp::server<asio_with_deflate>;
108         using connection_hdl = websocketpp::connection_hdl;
109         using message_ptr = server::message_ptr;
110         using ConnectionList = std::map<connection_hdl, bool, std::owner_less<connection_hdl>>;
111         using json = nlohmann::json;
112         using ITrackList = musik::core::sdk::ITrackList;
113         using ITrack = musik::core::sdk::ITrack;
114 
115         /* vars */
116         Context& context;
117         ConnectionList connections;
118         ReadWriteLock connectionLock;
119         std::shared_ptr<server> wss;
120         std::shared_ptr<std::thread> thread;
121         std::mutex exitMutex;
122         std::condition_variable exitCondition;
123         Snapshots snapshots;
124         volatile bool running;
125 
126         /* gross extra state */
127         std::string lastPlaybackOverview;
128 
129         void ThreadProc();
130         void HandleAuthentication(connection_hdl connection, json& request);
131         void HandleRequest(connection_hdl connection, json& request);
132 
133         void Broadcast(const std::string& name, json& options);
134         void RespondWithOptions(connection_hdl connection, json& request, json& options);
135         void RespondWithOptions(connection_hdl connection, json& request, json&& options = json({}));
136         void RespondWithInvalidRequest(connection_hdl connection, const std::string& name, const std::string& id);
137         void RespondWithSuccess(connection_hdl connection, json& request);
138         void RespondWithFailure(connection_hdl connection, json& request);
139         void RespondWithSuccess(connection_hdl connection, const std::string& name, const std::string& id);
140 
141         void RespondWithSendRawQuery(connection_hdl connection, json& request);
142         void RespondWithSetVolume(connection_hdl connection, json& request);
143         void RespondWithPlaybackOverview(connection_hdl connection, json& reuest);
144         bool RespondWithTracks(connection_hdl connection, json& request, ITrackList* tracks, int limit, int offset);
145         void RespondWithQueryTracks(connection_hdl connection, json& request);
146         void RespondWithQueryTracksByExternalIds(connection_hdl connection, json& request);
147         void RespondWithPlayQueueTracks(connection_hdl connection, json& request);
148         void RespondWithQueryAlbums(connection_hdl connection, json& request);
149         void RespondWithPlayTracks(connection_hdl connection, json& request);
150         void RespondWithQueryTracksByCategory(connection_hdl connection, json& request);
151         void RespondWithListCategories(connection_hdl connection, json& request);
152         void RespondWithQueryCategory(connection_hdl connection, json& request);
153         void RespondWithPlayAllTracks(connection_hdl connection, json& request);
154         void RespondWithPlaySnapshotTracks(connection_hdl connection, json& request);
155         void RespondWithPlayTracksByCategory(connection_hdl connection, json& request);
156         void RespondWithEnvironment(connection_hdl connection, json& request);
157         void RespondWithCurrentTime(connection_hdl connection, json& request);
158         void RespondWithSavePlaylist(connection_hdl connection, json& request);
159         void RespondWithRenamePlaylist(connection_hdl connection, json& request);
160         void RespondWithDeletePlaylist(connection_hdl connection, json& request);
161         void RespondWithAppendToPlaylist(connection_hdl connection, json& request);
162         void RespondWithRemoveTracksFromPlaylist(connection_hdl connection, json& request);
163         void RespondWithRunIndexer(connection_hdl connection, json& request);
164         void RespondWithListOutputDrivers(connection_hdl connection, json& request);
165         void RespondWithSetDefaultOutputDriver(connection_hdl connection, json& request);
166         void RespondWithGetGainSettings(connection_hdl connection, json& request);
167         void RespondWithSetGainSettings(connection_hdl connection, json& request);
168         void RespondWithGetEqualizerSettings(connection_hdl connection, json& request);
169         void RespondWithSetEqualizerSettings(connection_hdl connection, json& request);
170         void RespondWithGetTransportType(connection_hdl connection, json& request);
171         void RespondWithSetTransportType(connection_hdl connection, json& request);
172         void RespondWithSnapshotPlayQueue(connection_hdl connection, json& request);
173         void RespondWithInvalidatePlayQueueSnapshot(connection_hdl connection, json& request);
174 
175         void BroadcastPlaybackOverview();
176         void BroadcastPlayQueueChanged();
177 
178         void GetLimitAndOffset(json& options, int& limit, int& offset);
179         ITrackList* QueryTracksByCategory(json& request, int& limit, int& offset);
180         ITrackList* QueryTracks(json& request, int& limit, int& offset);
181         json ReadTrackMetadata(ITrack* track);
182         void BuildPlaybackOverview(json& options);
183 
184         void OnOpen(connection_hdl connection);
185         void OnClose(connection_hdl connection);
186         void OnMessage(server* s, connection_hdl hdl, message_ptr msg);
187 };