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 };