#pragma once #include // for atomic #include // for uint8_t, uint64_t, uint32_t #include // for function #include // for shared_ptr #include // for mutex #include // for string #include // for unordered_map #include // for vector #include "BellTask.h" // for Task #include "Packet.h" // for Packet #include "Queue.h" // for Queue #include "Session.h" // for Session #include "protobuf/mercury.pb.h" // for Header namespace cspot { class TimeProvider; class MercurySession : public bell::Task, public cspot::Session { public: MercurySession(std::shared_ptr timeProvider); ~MercurySession(); typedef std::vector> DataParts; struct Response { Header mercuryHeader; uint8_t flags; DataParts parts; uint64_t sequenceId; bool fail; }; typedef std::function ResponseCallback; typedef std::function&)> AudioKeyCallback; typedef std::function ConnectionEstabilishedCallback; enum class RequestType : uint8_t { SUB = 0xb3, UNSUB = 0xb4, SUBRES = 0xb5, SEND = 0xb2, GET = 0xFF, // Shitty workaround, it's value is actually same as SEND PING = 0x04, PONG_ACK = 0x4a, AUDIO_CHUNK_REQUEST_COMMAND = 0x08, AUDIO_CHUNK_SUCCESS_RESPONSE = 0x09, AUDIO_CHUNK_FAILURE_RESPONSE = 0x0A, AUDIO_KEY_REQUEST_COMMAND = 0x0C, AUDIO_KEY_SUCCESS_RESPONSE = 0x0D, AUDIO_KEY_FAILURE_RESPONSE = 0x0E, COUNTRY_CODE_RESPONSE = 0x1B, }; std::unordered_map RequestTypeMap = { {RequestType::GET, "GET"}, {RequestType::SEND, "SEND"}, {RequestType::SUB, "SUB"}, {RequestType::UNSUB, "UNSUB"}, }; void handlePacket(); uint64_t executeSubscription(RequestType type, const std::string& uri, ResponseCallback callback, ResponseCallback subscription, DataParts& parts); uint64_t executeSubscription(RequestType type, const std::string& uri, ResponseCallback callback, ResponseCallback subscription) { DataParts parts = {}; return this->executeSubscription(type, uri, callback, subscription, parts); } uint64_t execute(RequestType type, const std::string& uri, ResponseCallback callback) { return this->executeSubscription(type, uri, callback, nullptr); } uint64_t execute(RequestType type, const std::string& uri, ResponseCallback callback, DataParts& parts) { return this->executeSubscription(type, uri, callback, nullptr, parts); } void unregister(uint64_t sequenceId); void unregisterAudioKey(uint32_t sequenceId); uint32_t requestAudioKey(const std::vector& trackId, const std::vector& fileId, AudioKeyCallback audioCallback); std::string getCountryCode(); void disconnect(); void setConnectedHandler(ConnectionEstabilishedCallback callback); bool triggerTimeout() override; private: const int PING_TIMEOUT_MS = 2 * 60 * 1000 + 5000; std::shared_ptr timeProvider; Header tempMercuryHeader = {}; ConnectionEstabilishedCallback connectionReadyCallback = nullptr; bell::Queue packetQueue; void runTask() override; void reconnect(); std::unordered_map callbacks; std::unordered_map subscriptions; std::unordered_map audioKeyCallbacks; uint64_t sequenceId = 1; uint32_t audioKeySequence = 1; unsigned long long timestampDiff; unsigned long long lastPingTimestamp = -1; std::string countryCode = ""; std::mutex isRunningMutex; std::atomic isRunning = false; std::atomic isReconnecting = false; std::atomic executeEstabilishedCallback = false; void failAllPending(); Response decodeResponse(const std::vector& data); }; } // namespace cspot