diff --git a/control/meta_mpd.py b/control/meta_mpd.py index 32259afe..9f5cdb28 100755 --- a/control/meta_mpd.py +++ b/control/meta_mpd.py @@ -40,7 +40,6 @@ import time import json import musicbrainzngs import fcntl -import threading __version__ = "@version@" __git_version__ = "@gitversion@" @@ -378,31 +377,27 @@ class MPDWrapper(object): params = request['params'].get('params', {}) logger.debug( f'Control command: {command}, params: {params}') - if command == 'Next': + if command == 'next': self.next() - elif command == 'Previous': + elif command == 'previous': self.previous() - elif command == 'Play': + elif command == 'play': self.play() - elif command == 'Pause': + elif command == 'pause': self.pause(1) - elif command == 'PlayPause': + elif command == 'playPause': if self.status()['state'] == 'play': self.pause(1) else: self.play() - elif command == 'Stop': + elif command == 'stop': self.stop() - elif command == 'SetPosition': - position = float(params['Position']) - logger.info(f'SetPosition {position}') - if 'TrackId' in params: - trackid = params['TrackId'].rsplit('/', 1)[1] - self.seekid(int(trackid), position) - else: - self.seekcur(position) - elif command == 'Seek': - offset = float(params['Offset']) + elif command == 'setPosition': + position = float(params['position']) + logger.info(f'setPosition {position}') + self.seekcur(position) + elif command == 'seek': + offset = float(params['offset']) strOffset = str(offset) if offset >= 0: strOffset = "+" + strOffset diff --git a/control/snapcast_mpris.py b/control/snapcast_mpris.py index 798324da..bfdbcc1f 100755 --- a/control/snapcast_mpris.py +++ b/control/snapcast_mpris.py @@ -888,38 +888,38 @@ class MPRISInterface(dbus.service.Object): # Player methods @ dbus.service.method(__player_interface, in_signature='', out_signature='') def Next(self): - snapcast_wrapper.control("Next") + snapcast_wrapper.control("next") return @ dbus.service.method(__player_interface, in_signature='', out_signature='') def Previous(self): - snapcast_wrapper.control("Previous") + snapcast_wrapper.control("previous") return @ dbus.service.method(__player_interface, in_signature='', out_signature='') def Pause(self): - snapcast_wrapper.control("Pause") + snapcast_wrapper.control("pause") return @ dbus.service.method(__player_interface, in_signature='', out_signature='') def PlayPause(self): - snapcast_wrapper.control("PlayPause") + snapcast_wrapper.control("playPause") return @ dbus.service.method(__player_interface, in_signature='', out_signature='') def Stop(self): - snapcast_wrapper.control("Stop") + snapcast_wrapper.control("stop") return @ dbus.service.method(__player_interface, in_signature='', out_signature='') def Play(self): - snapcast_wrapper.control("Play") + snapcast_wrapper.control("play") return @ dbus.service.method(__player_interface, in_signature='x', out_signature='') def Seek(self, offset): logger.debug(f'Seek {offset}') - snapcast_wrapper.control("Seek", {"Offset": float(offset) / 1000000}) + snapcast_wrapper.control("seek", {"offset": float(offset) / 1000000}) # status = mpd_wrapper.status() # current, end = status['time'].split(':') # current = int(current) @@ -935,9 +935,9 @@ class MPRISInterface(dbus.service.Object): @ dbus.service.method(__player_interface, in_signature='ox', out_signature='') def SetPosition(self, trackid, position): - logger.debug(f'SetPosition TrackId: {trackid}, Position: {position}') + logger.debug(f'setPosition trackId: {trackid}, position: {position}') snapcast_wrapper.control( - "SetPosition", {"TrackId": trackid, "Position": float(position) / 1000000}) + "setPosition", {"position": float(position) / 1000000}) self.Seeked(position) # song = mpd_wrapper.last_currentsong() diff --git a/doc/json_rpc_api/control.md b/doc/json_rpc_api/control.md index 27c3e792..dfe55dbd 100644 --- a/doc/json_rpc_api/control.md +++ b/doc/json_rpc_api/control.md @@ -484,8 +484,8 @@ See [Plugin.Stream.Player.Control](stream_plugin.md#pluginstreamplayercontrol). {"id": 1, "jsonrpc": "2.0", "error": {"code": -32602, "message": "Command '' not supported"}} {"id": 1, "jsonrpc": "2.0", "error": {"code": -32602, "message": "Parameter 'commmand' is missing"}} -{"id": 1, "jsonrpc": "2.0", "error": {"code": -32602, "message": "SetPosition requires parameters 'Position'"}} -{"id": 1, "jsonrpc": "2.0", "error": {"code": -32602, "message": "Seek requires parameter 'Offset'"}} +{"id": 1, "jsonrpc": "2.0", "error": {"code": -32602, "message": "setPosition requires parameter 'position'"}} +{"id": 1, "jsonrpc": "2.0", "error": {"code": -32602, "message": "seek requires parameter 'offset'"}} ``` ### Stream.SetProperty diff --git a/doc/json_rpc_api/stream_plugin.md b/doc/json_rpc_api/stream_plugin.md index cecf46f5..0321b0cb 100644 --- a/doc/json_rpc_api/stream_plugin.md +++ b/doc/json_rpc_api/stream_plugin.md @@ -31,30 +31,29 @@ Used to control the player. The property `canControl` must be `true`. #### Supported `command`s -* `Play`: Start playback (if `canPlay` is `true`) +* `play`: Start playback (if `canPlay` is `true`) * `params`: none -* `Pause`: Stop playback (if `canPause` is `true`) +* `pause`: Stop playback (if `canPause` is `true`) * `params`: none -* `PlayPause`: Toggle play/pause (if `canPause` is `true`) +* `playPause`: Toggle play/pause (if `canPause` is `true`) * `params`: none -* `Stop`: Stop playback (if `canControl` is `true`) +* `stop`: Stop playback (if `canControl` is `true`) * `params`: none -* `Next`: Skip to next track (if `canGoNext` is `true`) +* `next`: Skip to next track (if `canGoNext` is `true`) * `params`: none -* `Previous`: Skip to previous track (if `canGoPrevious` is `true`) +* `previous`: Skip to previous track (if `canGoPrevious` is `true`) * `params`: none -* `Seek`: Seek forward or backward in the current track (if `canSeek` is `true`) +* `seek`: Seek forward or backward in the current track (if `canSeek` is `true`) * `params`: - * `Offset`: [float] seek offset in seconds -* `SetPosition`: Set the current track position in seconds (if `canSeek` is `true`) + * `offset`: [float] seek offset in seconds +* `setPosition`: Set the current track position in seconds (if `canSeek` is `true`) * `params`: - * `Position`: [float] the new track position - * `TrackId`: [string] the optional currently playing track's identifier + * `position`: [float] the new track position #### Example ```json -{"id": 1, "jsonrpc": "2.0", "method": "Plugin.Stream.Player.Control", "params": {"command": "SetPosition", "params": { "Position": 17.827, "TrackId": "/org/mpris/MediaPlayer2/Track/2"}}} +{"id": 1, "jsonrpc": "2.0", "method": "Plugin.Stream.Player.Control", "params": {"command": "setPosition", "params": { "position": 17.827 }}} ``` #### Expected response @@ -119,11 +118,11 @@ Any [json-rpc 2.0 conformant error](https://www.jsonrpc.org/specification#error_ * `volume`: [int] Voume in percent, valid range [0..100] * `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 -* `canGoPrevious`: [bool] Whether the client can call the `Previous` method on this interface and expect the current track to change -* `canPlay`: [bool] Whether playback can be started using `Play` or `PlayPause` -* `canPause`: [bool] Whether playback can be paused using `Pause` or `PlayPause` -* `canSeek`: [bool] Whether the client can control the playback position using `Seek` and `SetPosition` +* `canGoNext`: [bool] Whether the client can call the `next` method on this interface and expect the current track to change +* `canGoPrevious`: [bool] Whether the client can call the `previous` method on this interface and expect the current track to change +* `canPlay`: [bool] Whether playback can be started using `play` or `playPause` +* `canPause`: [bool] Whether playback can be paused using `pause` or `playPause` +* `canSeek`: [bool] Whether the client can control the playback position using `seek` and `setPosition` * `canControl`: [bool] Whether the media player may be controlled over this interface * `metadata`: [json] message with the following (optional) fields: * `trackId`: [string] A unique identity for this track within the context of an MPRIS object (eg: tracklist). diff --git a/server/server.cpp b/server/server.cpp index 73daa5f4..e1450f84 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -458,7 +458,7 @@ void Server::processRequest(const jsonrpcpp::request_ptr request, const OnRespon if (ec) log_level = AixLog::Severity::error; LOG(log_level, LOG_TAG) << "Response to '" << command << "': " << ec << ", message: " << ec.detailed_message() << ", msg: " << ec.message() - << ", category: " << ec.category().name() << "\n"; + << ", category: " << ec.category().name() << "\n"; std::shared_ptr response; if (ec) response = make_shared(request->id(), jsonrpcpp::Error(ec.detailed_message(), ec.value())); @@ -468,43 +468,43 @@ void Server::processRequest(const jsonrpcpp::request_ptr request, const OnRespon on_response(response, nullptr); }; - if (command == "SetPosition") + if (command == "setPosition") { - if (!request->params().has("params") || !request->params().get("params").contains("Position")) - throw jsonrpcpp::InvalidParamsException("SetPosition requires parameters 'Position'"); - auto seconds = request->params().get("params")["Position"].get(); + if (!request->params().has("params") || !request->params().get("params").contains("position")) + throw jsonrpcpp::InvalidParamsException("setPosition requires parameter 'position'"); + auto seconds = request->params().get("params")["position"].get(); stream->setPosition(std::chrono::milliseconds(static_cast(seconds * 1000)), [handle_response](const snapcast::ErrorCode& ec) { handle_response(ec); }); } - else if (command == "Seek") + else if (command == "seek") { - if (!request->params().has("params") || !request->params().get("params").contains("Offset")) - throw jsonrpcpp::InvalidParamsException("Seek requires parameter 'Offset'"); - auto offset = request->params().get("params")["Offset"].get(); + if (!request->params().has("params") || !request->params().get("params").contains("offset")) + throw jsonrpcpp::InvalidParamsException("Seek requires parameter 'offset'"); + auto offset = request->params().get("params")["offset"].get(); stream->seek(std::chrono::milliseconds(static_cast(offset * 1000)), [handle_response](const snapcast::ErrorCode& ec) { handle_response(ec); }); } - else if (command == "Next") + else if (command == "next") { stream->next([handle_response](const snapcast::ErrorCode& ec) { handle_response(ec); }); } - else if (command == "Previous") + else if (command == "previous") { stream->previous([handle_response](const snapcast::ErrorCode& ec) { handle_response(ec); }); } - else if (command == "Pause") + else if (command == "pause") { stream->pause([handle_response](const snapcast::ErrorCode& ec) { handle_response(ec); }); } - else if (command == "PlayPause") + else if (command == "playPause") { stream->playPause([handle_response](const snapcast::ErrorCode& ec) { handle_response(ec); }); } - else if (command == "Stop") + else if (command == "stop") { stream->stop([handle_response](const snapcast::ErrorCode& ec) { handle_response(ec); }); } - else if (command == "Play") + else if (command == "play") { stream->play([handle_response](const snapcast::ErrorCode& ec) { handle_response(ec); }); } diff --git a/server/streamreader/pcm_stream.cpp b/server/streamreader/pcm_stream.cpp index 18c1fd1d..918f97c7 100644 --- a/server/streamreader/pcm_stream.cpp +++ b/server/streamreader/pcm_stream.cpp @@ -359,9 +359,9 @@ void PcmStream::setPosition(std::chrono::milliseconds position, ResultHandler ha if (!properties_.can_seek) return handler({ControlErrc::can_seek_is_false}); json params; - params["command"] = "SetPosition"; + params["command"] = "setPosition"; json j; - j["Position"] = position.count() / 1000.f; + j["position"] = position.count() / 1000.f; params["params"] = j; sendRequest("Plugin.Stream.Player.Control", params, std::move(handler)); } @@ -373,9 +373,9 @@ void PcmStream::seek(std::chrono::milliseconds offset, ResultHandler handler) if (!properties_.can_seek) return handler({ControlErrc::can_seek_is_false}); json params; - params["command"] = "Seek"; + params["command"] = "seek"; json j; - j["Offset"] = offset.count() / 1000.f; + j["offset"] = offset.count() / 1000.f; params["params"] = j; sendRequest("Plugin.Stream.Player.Control", params, std::move(handler)); } @@ -386,7 +386,7 @@ void PcmStream::next(ResultHandler handler) LOG(DEBUG, LOG_TAG) << "next\n"; if (!properties_.can_go_next) return handler({ControlErrc::can_go_next_is_false}); - sendRequest("Plugin.Stream.Player.Control", {"command", "Next"}, std::move(handler)); + sendRequest("Plugin.Stream.Player.Control", {"command", "next"}, std::move(handler)); } @@ -395,7 +395,7 @@ void PcmStream::previous(ResultHandler handler) LOG(DEBUG, LOG_TAG) << "previous\n"; if (!properties_.can_go_previous) return handler({ControlErrc::can_go_previous_is_false}); - sendRequest("Plugin.Stream.Player.Control", {"command", "Previous"}, std::move(handler)); + sendRequest("Plugin.Stream.Player.Control", {"command", "previous"}, std::move(handler)); } @@ -404,9 +404,37 @@ void PcmStream::pause(ResultHandler handler) LOG(DEBUG, LOG_TAG) << "pause\n"; if (!properties_.can_pause) return handler({ControlErrc::can_pause_is_false}); - sendRequest("Plugin.Stream.Player.Control", {"command", "Pause"}, std::move(handler)); + sendRequest("Plugin.Stream.Player.Control", {"command", "pause"}, std::move(handler)); } + +void PcmStream::playPause(ResultHandler handler) +{ + LOG(DEBUG, LOG_TAG) << "playPause\n"; + if (!properties_.can_pause) + return handler({ControlErrc::can_play_is_false}); + sendRequest("Plugin.Stream.Player.Control", {"command", "playPause"}, std::move(handler)); +} + + +void PcmStream::stop(ResultHandler handler) +{ + LOG(DEBUG, LOG_TAG) << "stop\n"; + if (!properties_.can_control) + return handler({ControlErrc::can_control_is_false}); + sendRequest("Plugin.Stream.Player.Control", {"command", "stop"}, std::move(handler)); +} + + +void PcmStream::play(ResultHandler handler) +{ + LOG(DEBUG, LOG_TAG) << "play\n"; + if (!properties_.can_play) + return handler({ControlErrc::can_play_is_false}); + sendRequest("Plugin.Stream.Player.Control", {"command", "play"}, std::move(handler)); +} + + void PcmStream::sendRequest(const std::string& method, const jsonrpcpp::Parameter& params, ResultHandler handler) { if (!stream_ctrl_) @@ -422,33 +450,6 @@ void PcmStream::sendRequest(const std::string& method, const jsonrpcpp::Paramete } -void PcmStream::playPause(ResultHandler handler) -{ - LOG(DEBUG, LOG_TAG) << "playPause\n"; - if (!properties_.can_pause) - return handler({ControlErrc::can_play_is_false}); - sendRequest("Plugin.Stream.Player.Control", {"command", "PlayPause"}, std::move(handler)); -} - - -void PcmStream::stop(ResultHandler handler) -{ - LOG(DEBUG, LOG_TAG) << "stop\n"; - if (!properties_.can_control) - return handler({ControlErrc::can_control_is_false}); - sendRequest("Plugin.Stream.Player.Control", {"command", "Stop"}, std::move(handler)); -} - - -void PcmStream::play(ResultHandler handler) -{ - LOG(DEBUG, LOG_TAG) << "play\n"; - if (!properties_.can_play) - return handler({ControlErrc::can_play_is_false}); - sendRequest("Plugin.Stream.Player.Control", {"command", "Play"}, std::move(handler)); -} - - void PcmStream::setProperties(const Properties& properties) { std::lock_guard lock(mutex_); diff --git a/server/streamreader/properties.hpp b/server/streamreader/properties.hpp index bba37e48..cc81b397 100644 --- a/server/streamreader/properties.hpp +++ b/server/streamreader/properties.hpp @@ -19,10 +19,12 @@ #ifndef PROPERTIES_HPP #define PROPERTIES_HPP +// local headers #include "common/aixlog.hpp" #include "common/json.hpp" #include "metadata.hpp" +// standard headers #include #include #include @@ -170,9 +172,9 @@ public: std::optional minimum_rate; /// The maximum value which the Rate property can take. Clients should not attempt to set the Rate property above this value std::optional maximum_rate; - /// Whether the client can call the Next method on this interface and expect the current track to change + /// Whether the client can call the next method on this interface and expect the current track to change bool can_go_next = false; - /// Whether the client can call the Previous method on this interface and expect the current track to change + /// Whether the client can call the previous method on this interface and expect the current track to change bool can_go_previous = false; /// Whether playback can be started using "play" or "playPause" bool can_play = false;