mirror of
https://github.com/badaix/snapcast.git
synced 2025-04-28 09:47:09 +02:00
420 lines
10 KiB
C++
420 lines
10 KiB
C++
/***
|
|
This file is part of snapcast
|
|
Copyright (C) 2014-2024 Johannes Pohl
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
***/
|
|
|
|
#pragma once
|
|
|
|
|
|
// local headers
|
|
#include "common/json.hpp"
|
|
#include "common/utils.hpp"
|
|
#include "common/utils/string_utils.hpp"
|
|
|
|
// standard headers
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <sys/time.h>
|
|
#include <vector>
|
|
|
|
|
|
namespace strutils = utils::string;
|
|
using json = nlohmann::json;
|
|
|
|
struct ClientInfo;
|
|
struct Group;
|
|
|
|
using ClientInfoPtr = std::shared_ptr<ClientInfo>;
|
|
using GroupPtr = std::shared_ptr<Group>;
|
|
|
|
|
|
struct JsonConfigItem
|
|
{
|
|
/// Read config item from json object @p j
|
|
virtual void fromJson(const json& j) = 0;
|
|
/// @return config item serialized to json
|
|
virtual json toJson() = 0;
|
|
|
|
/// d'tor
|
|
virtual ~JsonConfigItem() = default;
|
|
|
|
protected:
|
|
/// @return value for key @p what or @p def, if not found. Result is casted to T.
|
|
template <typename T>
|
|
T jGet(const json& j, const std::string& what, const T& def)
|
|
{
|
|
try
|
|
{
|
|
if (!j.count(what))
|
|
return def;
|
|
return j[what].get<T>();
|
|
}
|
|
catch (...)
|
|
{
|
|
return def;
|
|
}
|
|
}
|
|
};
|
|
|
|
struct Volume : public JsonConfigItem
|
|
{
|
|
Volume(uint16_t _percent = 100, bool _muted = false) : percent(_percent), muted(_muted)
|
|
{
|
|
}
|
|
|
|
void fromJson(const json& j) override
|
|
{
|
|
percent = jGet<uint16_t>(j, "percent", percent);
|
|
muted = jGet<bool>(j, "muted", muted);
|
|
}
|
|
|
|
json toJson() override
|
|
{
|
|
json j;
|
|
j["percent"] = percent;
|
|
j["muted"] = muted;
|
|
return j;
|
|
}
|
|
|
|
uint16_t percent;
|
|
bool muted;
|
|
};
|
|
|
|
|
|
|
|
struct Host : public JsonConfigItem
|
|
{
|
|
Host() : name(""), mac(""), os(""), arch(""), ip("")
|
|
{
|
|
}
|
|
|
|
void update()
|
|
{
|
|
name = getHostName();
|
|
os = getOS();
|
|
arch = getArch();
|
|
}
|
|
|
|
void fromJson(const json& j) override
|
|
{
|
|
name = strutils::trim_copy(jGet<std::string>(j, "name", ""));
|
|
mac = strutils::trim_copy(jGet<std::string>(j, "mac", ""));
|
|
os = strutils::trim_copy(jGet<std::string>(j, "os", ""));
|
|
arch = strutils::trim_copy(jGet<std::string>(j, "arch", ""));
|
|
ip = strutils::trim_copy(jGet<std::string>(j, "ip", ""));
|
|
}
|
|
|
|
json toJson() override
|
|
{
|
|
json j;
|
|
j["name"] = name;
|
|
j["mac"] = mac;
|
|
j["os"] = os;
|
|
j["arch"] = arch;
|
|
j["ip"] = ip;
|
|
return j;
|
|
}
|
|
|
|
std::string name;
|
|
std::string mac;
|
|
std::string os;
|
|
std::string arch;
|
|
std::string ip;
|
|
};
|
|
|
|
|
|
struct ClientConfig : public JsonConfigItem
|
|
{
|
|
ClientConfig() : name(""), volume(100), latency(0), instance(1)
|
|
{
|
|
}
|
|
|
|
void fromJson(const json& j) override
|
|
{
|
|
name = strutils::trim_copy(jGet<std::string>(j, "name", ""));
|
|
volume.fromJson(j["volume"]);
|
|
latency = jGet<int32_t>(j, "latency", 0);
|
|
instance = jGet<size_t>(j, "instance", 1);
|
|
}
|
|
|
|
json toJson() override
|
|
{
|
|
json j;
|
|
j["name"] = strutils::trim_copy(name);
|
|
j["volume"] = volume.toJson();
|
|
j["latency"] = latency;
|
|
j["instance"] = instance;
|
|
return j;
|
|
}
|
|
|
|
std::string name;
|
|
Volume volume;
|
|
int32_t latency;
|
|
size_t instance;
|
|
};
|
|
|
|
|
|
|
|
struct Snapcast : public JsonConfigItem
|
|
{
|
|
Snapcast(const std::string& _name = "", const std::string& _version = "") : name(_name), version(_version), protocolVersion(1)
|
|
{
|
|
}
|
|
|
|
virtual ~Snapcast() = default;
|
|
|
|
void fromJson(const json& j) override
|
|
{
|
|
name = strutils::trim_copy(jGet<std::string>(j, "name", ""));
|
|
version = strutils::trim_copy(jGet<std::string>(j, "version", ""));
|
|
protocolVersion = jGet<int>(j, "protocolVersion", 1);
|
|
}
|
|
|
|
json toJson() override
|
|
{
|
|
json j;
|
|
j["name"] = strutils::trim_copy(name);
|
|
j["version"] = strutils::trim_copy(version);
|
|
j["protocolVersion"] = protocolVersion;
|
|
return j;
|
|
}
|
|
|
|
std::string name;
|
|
std::string version;
|
|
int protocolVersion;
|
|
};
|
|
|
|
|
|
struct Snapclient : public Snapcast
|
|
{
|
|
Snapclient(const std::string& _name = "", const std::string& _version = "") : Snapcast(_name, _version)
|
|
{
|
|
}
|
|
};
|
|
|
|
|
|
struct Snapserver : public Snapcast
|
|
{
|
|
Snapserver(const std::string& _name = "", const std::string& _version = "") : Snapcast(_name, _version), controlProtocolVersion(1)
|
|
{
|
|
}
|
|
|
|
void fromJson(const json& j) override
|
|
{
|
|
Snapcast::fromJson(j);
|
|
controlProtocolVersion = jGet<int>(j, "controlProtocolVersion", 1);
|
|
}
|
|
|
|
json toJson() override
|
|
{
|
|
json j = Snapcast::toJson();
|
|
j["controlProtocolVersion"] = controlProtocolVersion;
|
|
return j;
|
|
}
|
|
|
|
int controlProtocolVersion;
|
|
};
|
|
|
|
|
|
struct ClientInfo : public JsonConfigItem
|
|
{
|
|
ClientInfo(const std::string& _clientId = "") : id(_clientId), connected(false)
|
|
{
|
|
lastSeen.tv_sec = 0;
|
|
lastSeen.tv_usec = 0;
|
|
}
|
|
|
|
void fromJson(const json& j) override
|
|
{
|
|
host.fromJson(j["host"]);
|
|
id = jGet<std::string>(j, "id", host.mac);
|
|
snapclient.fromJson(j["snapclient"]);
|
|
config.fromJson(j["config"]);
|
|
lastSeen.tv_sec = jGet<int32_t>(j["lastSeen"], "sec", 0);
|
|
lastSeen.tv_usec = jGet<int32_t>(j["lastSeen"], "usec", 0);
|
|
connected = jGet<bool>(j, "connected", true);
|
|
}
|
|
|
|
json toJson() override
|
|
{
|
|
json j;
|
|
j["id"] = id;
|
|
j["host"] = host.toJson();
|
|
j["snapclient"] = snapclient.toJson();
|
|
j["config"] = config.toJson();
|
|
j["lastSeen"]["sec"] = lastSeen.tv_sec;
|
|
j["lastSeen"]["usec"] = lastSeen.tv_usec;
|
|
j["connected"] = connected;
|
|
return j;
|
|
}
|
|
|
|
std::string id;
|
|
Host host;
|
|
Snapclient snapclient;
|
|
ClientConfig config;
|
|
timeval lastSeen;
|
|
bool connected;
|
|
};
|
|
|
|
|
|
struct Group : public JsonConfigItem
|
|
{
|
|
Group(const ClientInfoPtr client = nullptr) : muted(false)
|
|
{
|
|
if (client)
|
|
id = client->id;
|
|
id = generateUUID();
|
|
}
|
|
|
|
void fromJson(const json& j) override
|
|
{
|
|
name = strutils::trim_copy(jGet<std::string>(j, "name", ""));
|
|
id = strutils::trim_copy(jGet<std::string>(j, "id", ""));
|
|
streamId = strutils::trim_copy(jGet<std::string>(j, "stream_id", ""));
|
|
muted = jGet<bool>(j, "muted", false);
|
|
clients.clear();
|
|
if (j.count("clients"))
|
|
{
|
|
for (const auto& jClient : j["clients"])
|
|
{
|
|
ClientInfoPtr client = std::make_shared<ClientInfo>();
|
|
client->fromJson(jClient);
|
|
client->connected = false;
|
|
addClient(client);
|
|
}
|
|
}
|
|
}
|
|
|
|
json toJson() override
|
|
{
|
|
json j;
|
|
j["name"] = strutils::trim_copy(name);
|
|
j["id"] = strutils::trim_copy(id);
|
|
j["stream_id"] = strutils::trim_copy(streamId);
|
|
j["muted"] = muted;
|
|
|
|
json jClients = json::array();
|
|
for (const auto& client : clients)
|
|
jClients.push_back(client->toJson());
|
|
j["clients"] = jClients;
|
|
return j;
|
|
}
|
|
|
|
ClientInfoPtr removeClient(const std::string& clientId)
|
|
{
|
|
for (auto iter = clients.begin(); iter != clients.end(); ++iter)
|
|
{
|
|
if ((*iter)->id == clientId)
|
|
{
|
|
clients.erase(iter);
|
|
return (*iter);
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
ClientInfoPtr removeClient(ClientInfoPtr client)
|
|
{
|
|
if (!client)
|
|
return nullptr;
|
|
|
|
return removeClient(client->id);
|
|
}
|
|
|
|
ClientInfoPtr getClient(const std::string& clientId)
|
|
{
|
|
for (const auto& client : clients)
|
|
{
|
|
if (client->id == clientId)
|
|
return client;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void addClient(ClientInfoPtr client)
|
|
{
|
|
if (!client)
|
|
return;
|
|
|
|
for (const auto& c : clients)
|
|
{
|
|
if (c->id == client->id)
|
|
return;
|
|
}
|
|
|
|
clients.push_back(client);
|
|
/* sort(clients.begin(), clients.end(),
|
|
[](const ClientInfoPtr a, const ClientInfoPtr b) -> bool
|
|
{
|
|
return a.name > b.name;
|
|
});
|
|
*/
|
|
}
|
|
|
|
bool empty() const
|
|
{
|
|
return clients.empty();
|
|
}
|
|
|
|
std::string name;
|
|
std::string id;
|
|
std::string streamId;
|
|
bool muted;
|
|
std::vector<ClientInfoPtr> clients;
|
|
};
|
|
|
|
|
|
class Config
|
|
{
|
|
public:
|
|
static Config& instance()
|
|
{
|
|
static Config instance_;
|
|
return instance_;
|
|
}
|
|
|
|
ClientInfoPtr getClientInfo(const std::string& clientId) const;
|
|
GroupPtr addClientInfo(const std::string& clientId);
|
|
GroupPtr addClientInfo(ClientInfoPtr client);
|
|
void remove(ClientInfoPtr client);
|
|
void remove(GroupPtr group, bool force = false);
|
|
|
|
// GroupPtr removeFromGroup(const std::string& groupId, const std::string& clientId);
|
|
// GroupPtr setGroupForClient(const std::string& groupId, const std::string& clientId);
|
|
|
|
GroupPtr getGroupFromClient(const std::string& clientId);
|
|
GroupPtr getGroupFromClient(ClientInfoPtr client);
|
|
GroupPtr getGroup(const std::string& groupId) const;
|
|
|
|
json getGroups() const;
|
|
json getServerStatus(const json& streams) const;
|
|
|
|
void save();
|
|
|
|
void init(const std::string& root_directory = "", const std::string& user = "", const std::string& group = "");
|
|
|
|
std::vector<GroupPtr> groups;
|
|
|
|
std::mutex& getMutex();
|
|
|
|
private:
|
|
Config() = default;
|
|
~Config();
|
|
mutable std::recursive_mutex mutex_;
|
|
std::mutex client_mutex_;
|
|
std::string filename_;
|
|
};
|