mirror of
https://github.com/badaix/snapcast.git
synced 2025-04-29 02:07:55 +02:00
Change Stream.SetProperties to Stream.SetProperty
This commit is contained in:
parent
5b9321b8c6
commit
bba3968f50
6 changed files with 102 additions and 65 deletions
|
@ -19,6 +19,7 @@
|
|||
#ifndef PROPERTIES_HPP
|
||||
#define PROPERTIES_HPP
|
||||
|
||||
#include <boost/any.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
@ -95,8 +96,6 @@ static std::ostream& operator<<(std::ostream& os, LoopStatus loop_status)
|
|||
|
||||
class Properties
|
||||
{
|
||||
static constexpr auto LOG_TAG = "Properties";
|
||||
|
||||
public:
|
||||
Properties() = default;
|
||||
Properties(const json& j)
|
||||
|
@ -112,12 +111,16 @@ public:
|
|||
/// A value of false indicates that playback is progressing linearly through a playlist, while true means playback is progressing through a playlist in some
|
||||
/// other order.
|
||||
boost::optional<bool> shuffle;
|
||||
/// The current playback rate
|
||||
boost::optional<int> rate;
|
||||
/// The volume level between 0-100
|
||||
boost::optional<int> volume;
|
||||
/// The current track position in seconds
|
||||
boost::optional<float> position;
|
||||
// /// The current track duration in seconds
|
||||
// boost::optional<float> duration;
|
||||
/// The minimum value which the Rate property can take. Clients should not attempt to set the Rate property below this value
|
||||
boost::optional<int> minimum_rate;
|
||||
/// The maximum value which the Rate property can take. Clients should not attempt to set the Rate property above this value
|
||||
boost::optional<int> maximum_rate;
|
||||
/// Whether the client can call the Next method on this interface and expect the current track to change
|
||||
boost::optional<bool> can_go_next;
|
||||
/// Whether the client can call the Previous method on this interface and expect the current track to change
|
||||
|
@ -134,14 +137,17 @@ public:
|
|||
json toJson() const
|
||||
{
|
||||
json j;
|
||||
if (playback_status.has_value())
|
||||
addTag(j, "playbackStatus", boost::optional<std::string>(to_string(playback_status.value())));
|
||||
if (loop_status.has_value())
|
||||
addTag(j, "loopStatus", boost::optional<std::string>(to_string(loop_status.value())));
|
||||
addTag(j, "shuffle", shuffle);
|
||||
addTag(j, "volume", volume);
|
||||
addTag(j, "rate", rate);
|
||||
|
||||
if (playback_status.has_value())
|
||||
addTag(j, "playbackStatus", boost::optional<std::string>(to_string(playback_status.value())));
|
||||
addTag(j, "position", position);
|
||||
// addTag(j, "duration", duration);
|
||||
addTag(j, "minimumRate", minimum_rate);
|
||||
addTag(j, "maximumRate", maximum_rate);
|
||||
addTag(j, "canGoNext", can_go_next);
|
||||
addTag(j, "canGoPrevious", can_go_previous);
|
||||
addTag(j, "canPlay", can_play);
|
||||
|
@ -153,29 +159,19 @@ public:
|
|||
|
||||
void fromJson(const json& j)
|
||||
{
|
||||
static std::set<std::string> supported_props = {"playbackStatus", "loopStatus", "shuffle", "volume", "position", "canGoNext",
|
||||
"canGoPrevious", "canPlay", "canPause", "canSeek", "canControl"};
|
||||
static std::set<std::string> rw_props = {"loopStatus", "shuffle", "volume", "rate"};
|
||||
static std::set<std::string> ro_props = {"playbackStatus", "loopStatus", "shuffle", "volume", "position", "minimumRate", "maximumRate",
|
||||
"canGoNext", "canGoPrevious", "canPlay", "canPause", "canSeek", "canControl"};
|
||||
for (const auto& element : j.items())
|
||||
{
|
||||
if (supported_props.find(element.key()) == supported_props.end())
|
||||
bool is_rw = (rw_props.find(element.key()) != rw_props.end());
|
||||
bool is_ro = (ro_props.find(element.key()) != ro_props.end());
|
||||
if (!is_rw && !is_ro)
|
||||
LOG(WARNING, LOG_TAG) << "Property not supoorted: " << element.key() << "\n";
|
||||
}
|
||||
|
||||
boost::optional<std::string> opt;
|
||||
readTag(j, "playbackStatus", opt);
|
||||
if (opt.has_value())
|
||||
{
|
||||
if (*opt == "playing")
|
||||
playback_status = PlaybackStatus::kPlaying;
|
||||
else if (*opt == "paused")
|
||||
playback_status = PlaybackStatus::kPaused;
|
||||
else if (*opt == "stopped")
|
||||
playback_status = PlaybackStatus::kStopped;
|
||||
else
|
||||
playback_status = PlaybackStatus::kUnknown;
|
||||
}
|
||||
else
|
||||
playback_status = boost::none;
|
||||
|
||||
readTag(j, "loopStatus", opt);
|
||||
if (opt.has_value())
|
||||
{
|
||||
|
@ -192,8 +188,25 @@ public:
|
|||
loop_status = boost::none;
|
||||
readTag(j, "shuffle", shuffle);
|
||||
readTag(j, "volume", volume);
|
||||
readTag(j, "rate", rate);
|
||||
|
||||
readTag(j, "playbackStatus", opt);
|
||||
if (opt.has_value())
|
||||
{
|
||||
if (*opt == "playing")
|
||||
playback_status = PlaybackStatus::kPlaying;
|
||||
else if (*opt == "paused")
|
||||
playback_status = PlaybackStatus::kPaused;
|
||||
else if (*opt == "stopped")
|
||||
playback_status = PlaybackStatus::kStopped;
|
||||
else
|
||||
playback_status = PlaybackStatus::kUnknown;
|
||||
}
|
||||
else
|
||||
playback_status = boost::none;
|
||||
readTag(j, "position", position);
|
||||
// readTag(j, "duration", duration);
|
||||
readTag(j, "minimumRate", minimum_rate);
|
||||
readTag(j, "maximumRate", maximum_rate);
|
||||
readTag(j, "canGoNext", can_go_next);
|
||||
readTag(j, "canGoPrevious", can_go_previous);
|
||||
readTag(j, "canPlay", can_play);
|
||||
|
@ -237,6 +250,9 @@ private:
|
|||
LOG(ERROR, LOG_TAG) << "failed to add tag: '" << tag << "': " << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr auto LOG_TAG = "Properties";
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -395,13 +395,13 @@ class MPDWrapper(object):
|
|||
if offset >= 0:
|
||||
strOffset = "+" + strOffset
|
||||
self.seekcur(strOffset)
|
||||
elif cmd == 'SetProperties':
|
||||
properties = request['params']
|
||||
logger.info(f'SetProperties: {properties}')
|
||||
if 'shuffle' in properties:
|
||||
self.random(int(properties['shuffle']))
|
||||
if 'loopStatus' in properties:
|
||||
value = properties['loopStatus']
|
||||
elif cmd == 'SetProperty':
|
||||
property = request['params']
|
||||
logger.info(f'SetProperty: {property}')
|
||||
if 'shuffle' in property:
|
||||
self.random(int(property['shuffle']))
|
||||
if 'loopStatus' in property:
|
||||
value = property['loopStatus']
|
||||
if value == "playlist":
|
||||
self.repeat(1)
|
||||
if self._can_single:
|
||||
|
@ -438,7 +438,6 @@ class MPDWrapper(object):
|
|||
for char in chunk:
|
||||
if char == '\n':
|
||||
logger.info(f'Received: {self._buffer}')
|
||||
|
||||
self.control(self._buffer)
|
||||
self._buffer = ''
|
||||
else:
|
||||
|
@ -628,7 +627,7 @@ class MPDWrapper(object):
|
|||
logger.debug(
|
||||
f'key: {key}, value: {value}, mapped key: {mapped_key}, mapped value: {mapped_val}')
|
||||
except KeyError:
|
||||
logger.debug(f'tag "{key}" not supported')
|
||||
logger.debug(f'property "{key}" not supported')
|
||||
except (ValueError, TypeError):
|
||||
logger.warning(
|
||||
f"Can't cast value {value} to {status_mapping[key][1]}")
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
|
||||
from time import sleep
|
||||
from systemd.journal import _valid_field_name
|
||||
import websocket
|
||||
import logging
|
||||
import threading
|
||||
|
@ -467,11 +468,9 @@ class SnapcastWrapper(object):
|
|||
"id": self._stream_id, "command": command, "params": params})
|
||||
|
||||
def set_property(self, property, value):
|
||||
properties = {}
|
||||
properties[property] = value
|
||||
logger.info(f'set_properties {properties}')
|
||||
self.send_request("Stream.SetProperties", {
|
||||
"id": self._stream_id, "properties": properties})
|
||||
logger.info(f'set_property {property} = {value}')
|
||||
self.send_request("Stream.SetProperty", {
|
||||
"id": self._stream_id, "property": property, "value": value})
|
||||
|
||||
@property
|
||||
def metadata(self):
|
||||
|
|
|
@ -463,13 +463,13 @@ void Server::processRequest(const jsonrpcpp::request_ptr request, jsonrpcpp::ent
|
|||
// Setup response
|
||||
result["id"] = streamId;
|
||||
}
|
||||
else if (request->method().find("Stream.SetProperties") == 0)
|
||||
else if (request->method().find("Stream.SetProperty") == 0)
|
||||
{
|
||||
// clang-format off
|
||||
// clang-format on
|
||||
|
||||
LOG(INFO, LOG_TAG) << "Stream.SetProperties id: " << request->params().get<std::string>("id")
|
||||
<< ", properties: " << request->params().get("properties") << "\n";
|
||||
LOG(INFO, LOG_TAG) << "Stream.SetProperty id: " << request->params().get<std::string>("id")
|
||||
<< ", property: " << request->params().get("property") << ", value: " << request->params().get("value") << "\n";
|
||||
|
||||
// Find stream
|
||||
string streamId = request->params().get<std::string>("id");
|
||||
|
@ -477,12 +477,10 @@ void Server::processRequest(const jsonrpcpp::request_ptr request, jsonrpcpp::ent
|
|||
if (stream == nullptr)
|
||||
throw jsonrpcpp::InternalErrorException("Stream not found", request->id());
|
||||
|
||||
Properties props(request->params().get("properties"));
|
||||
|
||||
// Set metadata from request
|
||||
stream->setProperties(props);
|
||||
stream->setProperty(request->params().get("property"), request->params().get("value"));
|
||||
|
||||
// Setup response
|
||||
// TODO: error handling
|
||||
result["id"] = streamId;
|
||||
}
|
||||
else if (request->method() == "Stream.AddStream")
|
||||
|
|
|
@ -262,13 +262,7 @@ void PcmStream::onControlMsg(const std::string& msg)
|
|||
else if (notification->method() == "Player.Properties")
|
||||
{
|
||||
LOG(DEBUG, LOG_TAG) << "Received properties notification\n";
|
||||
properties_ = std::make_shared<Properties>(notification->params().to_json());
|
||||
// Trigger a stream update
|
||||
for (auto* listener : pcmListeners_)
|
||||
{
|
||||
if (listener != nullptr)
|
||||
listener->onPropertiesChanged(this);
|
||||
}
|
||||
setProperties(notification->params().to_json());
|
||||
}
|
||||
else
|
||||
LOG(WARNING, LOG_TAG) << "Received unknown notification method: '" << notification->method() << "'\n";
|
||||
|
@ -405,24 +399,38 @@ std::shared_ptr<Properties> PcmStream::getProperties() const
|
|||
}
|
||||
|
||||
|
||||
void PcmStream::setProperties(const Properties& props)
|
||||
void PcmStream::setProperty(const std::string& name, const json& value)
|
||||
{
|
||||
LOG(INFO, LOG_TAG) << "Stream '" << getId() << "' set properties: " << props.toJson() << "\n";
|
||||
LOG(INFO, LOG_TAG) << "Stream '" << getId() << "' set property: " << name << " = " << value << "\n";
|
||||
// TODO: check validity
|
||||
if (name == "loopStatus")
|
||||
;
|
||||
else if (name == "shuffle")
|
||||
;
|
||||
else if (name == "volume")
|
||||
;
|
||||
else if (name == "rate")
|
||||
;
|
||||
else
|
||||
{
|
||||
LOG(ERROR, LOG_TAG) << "Property not supported: " << name << "\n";
|
||||
}
|
||||
|
||||
// TODO: queue commands, send next on timeout or after reception of the last command's response
|
||||
if (ctrl_script_)
|
||||
{
|
||||
jsonrpcpp::Request request(++req_id_, "Player.SetProperties", props.toJson());
|
||||
jsonrpcpp::Request request(++req_id_, "Player.SetProperty", {name, value});
|
||||
ctrl_script_->send(request.to_json().dump() + "\n"); //, params);
|
||||
}
|
||||
else // TODO: Will the ctr_script always loop back the new properties?
|
||||
{
|
||||
properties_ = std::make_shared<Properties>(props);
|
||||
// Trigger a stream update
|
||||
for (auto* listener : pcmListeners_)
|
||||
{
|
||||
if (listener != nullptr)
|
||||
listener->onPropertiesChanged(this);
|
||||
}
|
||||
// properties_ = std::make_shared<Properties>(props);
|
||||
// // Trigger a stream update
|
||||
// for (auto* listener : pcmListeners_)
|
||||
// {
|
||||
// if (listener != nullptr)
|
||||
// listener->onPropertiesChanged(this);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -446,7 +454,7 @@ void PcmStream::control(const std::string& command, const json& params)
|
|||
void PcmStream::setMeta(const Metatags& meta)
|
||||
{
|
||||
meta_ = std::make_shared<Metatags>(meta);
|
||||
LOG(INFO, LOG_TAG) << "Stream: " << name_ << ", metadata=" << meta_->toJson().dump(4) << "\n";
|
||||
LOG(INFO, LOG_TAG) << "setMeta, stream: " << getId() << ", metadata: " << meta_->toJson() << "\n";
|
||||
|
||||
// Trigger a stream update
|
||||
for (auto* listener : pcmListeners_)
|
||||
|
@ -456,4 +464,20 @@ void PcmStream::setMeta(const Metatags& meta)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void PcmStream::setProperties(const Properties& props)
|
||||
{
|
||||
properties_ = std::make_shared<Properties>(props);
|
||||
LOG(INFO, LOG_TAG) << "setProperties, stream: " << getId() << ", properties: " << props.toJson() << "\n";
|
||||
|
||||
// Trigger a stream update
|
||||
for (auto* listener : pcmListeners_)
|
||||
{
|
||||
if (listener != nullptr)
|
||||
listener->onPropertiesChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace streamreader
|
||||
|
|
|
@ -172,7 +172,7 @@ public:
|
|||
std::shared_ptr<Metatags> getMeta() const;
|
||||
|
||||
std::shared_ptr<Properties> getProperties() const;
|
||||
void setProperties(const Properties& props);
|
||||
void setProperty(const std::string& name, const json& value);
|
||||
|
||||
virtual void control(const std::string& command, const json& params);
|
||||
|
||||
|
@ -191,6 +191,7 @@ protected:
|
|||
void chunkEncoded(const encoder::Encoder& encoder, std::shared_ptr<msg::PcmChunk> chunk, double duration);
|
||||
|
||||
void setMeta(const Metatags& meta);
|
||||
void setProperties(const Properties& props);
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> tvEncodedChunk_;
|
||||
std::vector<PcmListener*> pcmListeners_;
|
||||
|
|
Loading…
Add table
Reference in a new issue