mirror of
https://github.com/badaix/snapcast.git
synced 2025-05-30 01:16:16 +02:00
Add "mute" to stream API
This commit is contained in:
parent
60dc1aa48a
commit
9e3c0fdf55
12 changed files with 47 additions and 10 deletions
|
@ -519,6 +519,7 @@ See [Plugin.Stream.Player.SetProperty](stream_plugin.md#pluginstreamplayersetpro
|
|||
{"id": 1, "jsonrpc": "2.0", "error": {"code": -32602, "message": "Value for loopStatus must be one of 'none', 'track', 'playlist'"}}
|
||||
{"id": 1, "jsonrpc": "2.0", "error": {"code": -32602, "message": "Value for shuffle must be bool"}}
|
||||
{"id": 1, "jsonrpc": "2.0", "error": {"code": -32602, "message": "Value for volume must be an int"}}
|
||||
{"id": 1, "jsonrpc": "2.0", "error": {"code": -32602, "message": "Value for mute must be bool"}}
|
||||
{"id": 1, "jsonrpc": "2.0", "error": {"code": -32602, "message": "Value for rate must be float"}}
|
||||
```
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ Any [json-rpc 2.0 conformant error](https://www.jsonrpc.org/specification#error_
|
|||
Snapserver will send the `SetProperty` command to the plugin, if `canControl` is `true`.
|
||||
|
||||
```json
|
||||
{"id": 1, "jsonrpc": "2.0", "method": "Plugin.Stream.Player.SetProperty", "params": {"<property>", <value>}}
|
||||
{"id": 1, "jsonrpc": "2.0", "method": "Plugin.Stream.Player.SetProperty", "params": {"<property>": <value>}}
|
||||
```
|
||||
|
||||
#### Supported `property`s
|
||||
|
@ -93,7 +93,8 @@ Snapserver will send the `SetProperty` command to the plugin, if `canControl` is
|
|||
* `playlist`: the playback loops through a list of tracks
|
||||
* `shuffle`: [bool] play playlist in random order
|
||||
* `volume`: [int] voume in percent, valid range [0..100]
|
||||
* `rate`: [float] The current playback rate, valid range (0..)
|
||||
* `mute`: [bool] the current mute state
|
||||
* `rate`: [float] the current playback rate, valid range (0..)
|
||||
|
||||
#### Expected response
|
||||
|
||||
|
@ -125,6 +126,7 @@ Any [json-rpc 2.0 conformant error](https://www.jsonrpc.org/specification#error_
|
|||
* `playlist`: The playback loops through a list of tracks
|
||||
* `shuffle`: [bool] Traverse through the playlist in random order
|
||||
* `volume`: [int] Voume in percent, valid range [0..100]
|
||||
* `mute`: [bool] Current mute state
|
||||
* `rate`: [float] The current playback rate, valid range (0..)
|
||||
* `position`: [float] Current playback position in seconds
|
||||
* `canGoNext`: [bool] Whether the client can call the `next` method on this interface and expect the current track to change
|
||||
|
@ -187,9 +189,9 @@ Any [json-rpc 2.0 conformant error](https://www.jsonrpc.org/specification#error_
|
|||
##### Success
|
||||
|
||||
```json
|
||||
{"id": 1, "jsonrpc": "2.0", "result": {"canControl":true,"canGoNext":true,"canGoPrevious":true,"canPause":true,"canPlay":true,"canSeek":false,"loopStatus":"none","playbackStatus":"playing","position":93.394,"shuffle":false,"volume":86}}
|
||||
{"id": 1, "jsonrpc": "2.0", "result": {"canControl":true,"canGoNext":true,"canGoPrevious":true,"canPause":true,"canPlay":true,"canSeek":false,"loopStatus":"none","playbackStatus":"playing","position":93.394,"shuffle":false,"volume":86,"mute":false}}
|
||||
|
||||
{"id": 1, "jsonrpc": "2.0", "result": {"canControl":true,"canGoNext":true,"canGoPrevious":true,"canPause":true,"canPlay":true,"canSeek":true,"loopStatus":"none","metadata":{"album":"Doldinger","albumArtist":["Klaus Doldinger's Passport"],"artUrl":"http://coverartarchive.org/release/0d4ff56b-2a2b-43b5-bf99-063cac1599e5/16940576164-250.jpg","artist":["Klaus Doldinger's Passport feat. Nils Landgren"],"contentCreated":"2016","duration":305.2929992675781,"genre":["Jazz"],"musicbrainzAlbumId":"0d4ff56b-2a2b-43b5-bf99-063cac1599e5","title":"Soul Town","trackId":"7","trackNumber":6,"url":"Klaus Doldinger's Passport - Doldinger (2016)/06 - Soul Town.mp3"},"playbackStatus":"playing","position":72.79499816894531,"shuffle":false,"volume":97}}
|
||||
{"id": 1, "jsonrpc": "2.0", "result": {"canControl":true,"canGoNext":true,"canGoPrevious":true,"canPause":true,"canPlay":true,"canSeek":true,"loopStatus":"none","metadata":{"album":"Doldinger","albumArtist":["Klaus Doldinger's Passport"],"artUrl":"http://coverartarchive.org/release/0d4ff56b-2a2b-43b5-bf99-063cac1599e5/16940576164-250.jpg","artist":["Klaus Doldinger's Passport feat. Nils Landgren"],"contentCreated":"2016","duration":305.2929992675781,"genre":["Jazz"],"musicbrainzAlbumId":"0d4ff56b-2a2b-43b5-bf99-063cac1599e5","title":"Soul Town","trackId":"7","trackNumber":6,"url":"Klaus Doldinger's Passport - Doldinger (2016)/06 - Soul Town.mp3"},"playbackStatus":"playing","position":72.79499816894531,"shuffle":false,"volume":97,"mute":false}}
|
||||
```
|
||||
|
||||
##### Error
|
||||
|
@ -201,7 +203,7 @@ Any [json-rpc 2.0 conformant error](https://www.jsonrpc.org/specification#error_
|
|||
### Plugin.Stream.Player.Properties
|
||||
|
||||
```json
|
||||
{"jsonrpc": "2.0", "method": "Plugin.Stream.Player.Properties", "params": {"canControl":true,"canGoNext":true,"canGoPrevious":true,"canPause":true,"canPlay":true,"canSeek":false,"loopStatus":"none","playbackStatus":"playing","position":593.394,"shuffle":false,"volume":86}}
|
||||
{"jsonrpc": "2.0", "method": "Plugin.Stream.Player.Properties", "params": {"canControl":true,"canGoNext":true,"canGoPrevious":true,"canPause":true,"canPlay":true,"canSeek":false,"loopStatus":"none","playbackStatus":"playing","position":593.394,"shuffle":false,"volume":86,"mute":false}}
|
||||
```
|
||||
|
||||
Same format as in [GetProperties](#pluginstreamplayergetproperties). If `metadata` is missing, the last known metadata will be used, so the plugin must not send the complete metadata if one of the properties is updated.
|
||||
|
|
|
@ -185,6 +185,8 @@ class MopidyControl(object):
|
|||
properties['shuffle'] = result
|
||||
elif request == 'core.mixer.get_volume':
|
||||
properties['volume'] = result
|
||||
elif request == 'core.mixer.get_mute':
|
||||
properties['mute'] = result
|
||||
elif request == 'core.playback.get_time_position':
|
||||
properties['position'] = float(result) / 1000
|
||||
elif request == 'core.playback.get_current_track':
|
||||
|
@ -283,7 +285,7 @@ class MopidyControl(object):
|
|||
jmsg['tl_track']['track'])
|
||||
logger.debug(f'Meta: {self._metadata}')
|
||||
self.send_batch_request([("core.playback.get_state", None), ("core.tracklist.get_repeat", None), ("core.tracklist.get_single", None),
|
||||
("core.tracklist.get_random", None), ("core.mixer.get_volume", None), ("core.playback.get_time_position", None), ('core.library.get_images', {
|
||||
("core.tracklist.get_random", None), ("core.mixer.get_volume", None), ("core.mixer.get_mute", None), ("core.playback.get_time_position", None), ('core.library.get_images', {
|
||||
'uris': [self._metadata['url']]})], self.onPropertiesResponse)
|
||||
elif event in ['tracklist_changed', 'track_playback_ended']:
|
||||
logger.debug("Nothing to do")
|
||||
|
@ -293,7 +295,7 @@ class MopidyControl(object):
|
|||
logger.debug("Nothing to do")
|
||||
else:
|
||||
self.send_batch_request([("core.playback.get_state", None), ("core.tracklist.get_repeat", None), ("core.tracklist.get_single", None),
|
||||
("core.tracklist.get_random", None), ("core.mixer.get_volume", None), ("core.playback.get_time_position", None)], self.onPropertiesResponse)
|
||||
("core.tracklist.get_random", None), ("core.mixer.get_volume", None), ("core.mixer.get_mute", None), ("core.playback.get_time_position", None)], self.onPropertiesResponse)
|
||||
|
||||
def on_ws_error(self, ws, error):
|
||||
logger.error("Snapcast RPC websocket error")
|
||||
|
@ -402,6 +404,9 @@ class MopidyControl(object):
|
|||
if 'volume' in property:
|
||||
self.send_request("core.mixer.set_volume", {
|
||||
"volume": int(property['volume'])})
|
||||
if 'mute' in property:
|
||||
self.send_request("core.mixer.set_mute", {
|
||||
"mute": property['mute']})
|
||||
elif cmd == 'GetProperties':
|
||||
self.send_batch_request([("core.playback.get_state", None), ("core.tracklist.get_repeat", None), ("core.tracklist.get_single", None),
|
||||
("core.tracklist.get_random", None), ("core.mixer.get_volume", None), ("core.playback.get_current_track", None), ("core.playback.get_time_position", None)], lambda req_res: self.onSnapcastPropertiesResponse(id, req_res))
|
||||
|
|
|
@ -800,6 +800,7 @@ Usage: %(progname)s [OPTION]...
|
|||
--snapcast-port=PORT Set the snapcast server port
|
||||
--stream=ID Set the stream id
|
||||
|
||||
-h, --help Show this help message
|
||||
-d, --debug Run in debug mode
|
||||
-v, --version meta_mpd version
|
||||
|
||||
|
|
|
@ -564,6 +564,12 @@ void Server::processRequest(const jsonrpcpp::request_ptr request, const OnRespon
|
|||
throw jsonrpcpp::InvalidParamsException("Value for volume must be an int", request->id());
|
||||
stream->setVolume(value.get<int16_t>(), [handle_response](const snapcast::ErrorCode& ec) { handle_response(ec); });
|
||||
}
|
||||
else if (name == "mute")
|
||||
{
|
||||
if (!value.is_boolean())
|
||||
throw jsonrpcpp::InvalidParamsException("Value for mute must be bool", request->id());
|
||||
stream->setMute(value.get<bool>(), [handle_response](const snapcast::ErrorCode& ec) { handle_response(ec); });
|
||||
}
|
||||
else if (name == "rate")
|
||||
{
|
||||
if (!value.is_number_float())
|
||||
|
|
|
@ -226,6 +226,12 @@ void MetaStream::setVolume(uint16_t volume, ResultHandler handler)
|
|||
active_stream_->setVolume(volume, std::move(handler));
|
||||
}
|
||||
|
||||
void MetaStream::setMute(bool mute, ResultHandler handler)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
active_stream_->setMute(mute, std::move(handler));
|
||||
}
|
||||
|
||||
void MetaStream::setRate(float rate, ResultHandler handler)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
|
|
|
@ -51,6 +51,7 @@ public:
|
|||
void setShuffle(bool shuffle, ResultHandler handler) override;
|
||||
void setLoopStatus(LoopStatus status, ResultHandler handler) override;
|
||||
void setVolume(uint16_t volume, ResultHandler handler) override;
|
||||
void setMute(bool mute, ResultHandler handler) override;
|
||||
void setRate(float rate, ResultHandler handler) override;
|
||||
|
||||
// Control commands
|
||||
|
|
|
@ -347,6 +347,15 @@ void PcmStream::setVolume(uint16_t volume, ResultHandler handler)
|
|||
}
|
||||
|
||||
|
||||
void PcmStream::setMute(bool mute, ResultHandler handler)
|
||||
{
|
||||
LOG(DEBUG, LOG_TAG) << "setMute: " << mute << "\n";
|
||||
if (!properties_.can_control)
|
||||
return handler({ControlErrc::can_control_is_false});
|
||||
sendRequest("Plugin.Stream.Player.SetProperty", {"mute", mute}, std::move(handler));
|
||||
}
|
||||
|
||||
|
||||
void PcmStream::setRate(float rate, ResultHandler handler)
|
||||
{
|
||||
LOG(DEBUG, LOG_TAG) << "setRate: " << rate << "\n";
|
||||
|
|
|
@ -141,6 +141,7 @@ public:
|
|||
virtual void setShuffle(bool shuffle, ResultHandler handler);
|
||||
virtual void setLoopStatus(LoopStatus status, ResultHandler handler);
|
||||
virtual void setVolume(uint16_t volume, ResultHandler handler);
|
||||
virtual void setMute(bool mute, ResultHandler handler);
|
||||
virtual void setRate(float rate, ResultHandler handler);
|
||||
|
||||
// Control commands
|
||||
|
|
|
@ -92,6 +92,7 @@ json Properties::toJson() const
|
|||
addTag(j, "rate", rate);
|
||||
addTag(j, "shuffle", shuffle);
|
||||
addTag(j, "volume", volume);
|
||||
addTag(j, "mute", mute);
|
||||
addTag(j, "position", position);
|
||||
addTag(j, "minimumRate", minimum_rate);
|
||||
addTag(j, "maximumRate", maximum_rate);
|
||||
|
@ -108,8 +109,8 @@ json Properties::toJson() const
|
|||
|
||||
void Properties::fromJson(const json& j)
|
||||
{
|
||||
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",
|
||||
static std::set<std::string> rw_props = {"loopStatus", "shuffle", "volume", "mute", "rate"};
|
||||
static std::set<std::string> ro_props = {"playbackStatus", "loopStatus", "shuffle", "volume", "mute", "position", "minimumRate", "maximumRate",
|
||||
"canGoNext", "canGoPrevious", "canPlay", "canPause", "canSeek", "canControl", "metadata"};
|
||||
for (const auto& element : j.items())
|
||||
{
|
||||
|
@ -135,6 +136,7 @@ void Properties::fromJson(const json& j)
|
|||
readTag(j, "rate", rate);
|
||||
readTag(j, "shuffle", shuffle);
|
||||
readTag(j, "volume", volume);
|
||||
readTag(j, "mute", mute);
|
||||
readTag(j, "position", position);
|
||||
readTag(j, "minimumRate", minimum_rate);
|
||||
readTag(j, "maximumRate", maximum_rate);
|
||||
|
|
|
@ -166,6 +166,8 @@ public:
|
|||
std::optional<bool> shuffle;
|
||||
/// The volume level between 0-100
|
||||
std::optional<int> volume;
|
||||
/// The current mute state
|
||||
std::optional<bool> mute;
|
||||
/// The current track position in seconds
|
||||
std::optional<float> position;
|
||||
/// The minimum value which the Rate property can take. Clients should not attempt to set the Rate property below this value
|
||||
|
|
|
@ -190,6 +190,7 @@ TEST_CASE("Properties")
|
|||
"loopStatus": "track",
|
||||
"shuffle": false,
|
||||
"volume": 42,
|
||||
"mute": false,
|
||||
"position": 23.0
|
||||
}
|
||||
)");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue