mirror of
https://github.com/badaix/snapcast.git
synced 2025-06-28 07:27:05 +02:00
Rename jsron RPC commands, add "Ready" and "Log"
This commit is contained in:
parent
0eaee48f10
commit
149c2ae9a2
3 changed files with 92 additions and 40 deletions
|
@ -36,6 +36,7 @@ import time
|
||||||
import json
|
import json
|
||||||
import musicbrainzngs
|
import musicbrainzngs
|
||||||
import fcntl
|
import fcntl
|
||||||
|
import threading
|
||||||
|
|
||||||
__version__ = "@version@"
|
__version__ = "@version@"
|
||||||
__git_version__ = "@gitversion@"
|
__git_version__ = "@gitversion@"
|
||||||
|
@ -287,6 +288,8 @@ class MPDWrapper(object):
|
||||||
|
|
||||||
self.timer_callback()
|
self.timer_callback()
|
||||||
self.idle_enter()
|
self.idle_enter()
|
||||||
|
send({"jsonrpc": "2.0", "method": "Plugin.Stream.Ready"})
|
||||||
|
|
||||||
# Return False to stop trying to connect
|
# Return False to stop trying to connect
|
||||||
return False
|
return False
|
||||||
except socket.error as e:
|
except socket.error as e:
|
||||||
|
@ -364,8 +367,8 @@ class MPDWrapper(object):
|
||||||
try:
|
try:
|
||||||
request = json.loads(cmd)
|
request = json.loads(cmd)
|
||||||
id = request['id']
|
id = request['id']
|
||||||
[interface, cmd] = request['method'].split('.', 1)
|
[interface, cmd] = request['method'].rsplit('.', 1)
|
||||||
if interface == 'Player':
|
if interface == 'Plugin.Stream.Player':
|
||||||
if cmd == 'Control':
|
if cmd == 'Control':
|
||||||
success = True
|
success = True
|
||||||
command = request['params']['command']
|
command = request['params']['command']
|
||||||
|
@ -417,19 +420,26 @@ class MPDWrapper(object):
|
||||||
self.repeat(0)
|
self.repeat(0)
|
||||||
if self._can_single:
|
if self._can_single:
|
||||||
self.single(0)
|
self.single(0)
|
||||||
|
elif cmd == 'GetProperties':
|
||||||
|
snapstatus = self._get_properties(self.status())
|
||||||
|
logger.info(f'Snapstatus: {snapstatus}')
|
||||||
|
return send({"jsonrpc": "2.0", "id": id, "result": snapstatus})
|
||||||
|
# return send({"jsonrpc": "2.0", "error": {"code": -32601,
|
||||||
|
# "message": "TODO: GetProperties not yet implemented"}, "id": id})
|
||||||
|
elif cmd == 'GetMetadata':
|
||||||
|
send({"jsonrpc": "2.0", "method": "Plugin.Stream.Log", "params": {"severity":"Info","message":"Logmessage"}})
|
||||||
|
return send({"jsonrpc": "2.0", "error": {"code": -32601,
|
||||||
|
"message": "TODO: GetMetadata not yet implemented"}, "id": id})
|
||||||
else:
|
else:
|
||||||
send({"jsonrpc": "2.0", "error": {"code": -32601,
|
return send({"jsonrpc": "2.0", "error": {"code": -32601,
|
||||||
"message": "Method not found"}, "id": id})
|
"message": "Method not found"}, "id": id})
|
||||||
success = False
|
|
||||||
else:
|
else:
|
||||||
send({"jsonrpc": "2.0", "error": {"code": -32601,
|
return send({"jsonrpc": "2.0", "error": {"code": -32601,
|
||||||
"message": "Method not found"}, "id": id})
|
"message": "Method not found"}, "id": id})
|
||||||
success = False
|
|
||||||
if success:
|
|
||||||
send({"jsonrpc": "2.0", "result": "ok", "id": id})
|
send({"jsonrpc": "2.0", "result": "ok", "id": id})
|
||||||
except:
|
except Exception as e:
|
||||||
send({"jsonrpc": "2.0", "error": {
|
send({"jsonrpc": "2.0", "error": {
|
||||||
"code": -32700, "message": "Parse error"}, "id": id})
|
"code": -32700, "message": "Parse error", "data": str(e)}, "id": id})
|
||||||
|
|
||||||
def io_callback(self, fd, event):
|
def io_callback(self, fd, event):
|
||||||
logger.debug("IO event %r on fd %r" % (event, fd))
|
logger.debug("IO event %r on fd %r" % (event, fd))
|
||||||
|
@ -510,7 +520,7 @@ class MPDWrapper(object):
|
||||||
self._album_art_map[self.__track_key(
|
self._album_art_map[self.__track_key(
|
||||||
snapmeta)] = snapmeta['artUrl']
|
snapmeta)] = snapmeta['artUrl']
|
||||||
send(
|
send(
|
||||||
{"jsonrpc": "2.0", "method": "Player.Metadata", "params": snapmeta})
|
{"jsonrpc": "2.0", "method": "Plugin.Stream.Player.Metadata", "params": snapmeta})
|
||||||
break
|
break
|
||||||
|
|
||||||
except musicbrainzngs.musicbrainz.ResponseError as e:
|
except musicbrainzngs.musicbrainz.ResponseError as e:
|
||||||
|
@ -573,8 +583,11 @@ class MPDWrapper(object):
|
||||||
if art_url != '':
|
if art_url != '':
|
||||||
snapmeta['artUrl'] = art_url
|
snapmeta['artUrl'] = art_url
|
||||||
|
|
||||||
send({"jsonrpc": "2.0", "method": "Player.Metadata", "params": snapmeta})
|
send({"jsonrpc": "2.0",
|
||||||
|
"method": "Plugin.Stream.Player.Metadata", "params": snapmeta})
|
||||||
if not track_key in self._album_art_map:
|
if not track_key in self._album_art_map:
|
||||||
|
# t = threading.Thread(target=self.update_albumart, args=[snapmeta])
|
||||||
|
# t.start()
|
||||||
self.update_albumart(snapmeta)
|
self.update_albumart(snapmeta)
|
||||||
|
|
||||||
def __diff_map(self, old_map, new_map):
|
def __diff_map(self, old_map, new_map):
|
||||||
|
@ -589,6 +602,29 @@ class MPDWrapper(object):
|
||||||
diff[key] = [value, None]
|
diff[key] = [value, None]
|
||||||
return diff
|
return diff
|
||||||
|
|
||||||
|
def _get_properties(self, mpd_status):
|
||||||
|
snapstatus = {}
|
||||||
|
for key, value in mpd_status.items():
|
||||||
|
try:
|
||||||
|
mapped_key = status_mapping[key][0]
|
||||||
|
mapped_val = status_mapping[key][1](value)
|
||||||
|
snapstatus[mapped_key] = mapped_val
|
||||||
|
logger.debug(
|
||||||
|
f'key: {key}, value: {value}, mapped key: {mapped_key}, mapped value: {mapped_val}')
|
||||||
|
except KeyError:
|
||||||
|
logger.debug(f'property "{key}" not supported')
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
logger.warning(
|
||||||
|
f"Can't cast value {value} to {status_mapping[key][1]}")
|
||||||
|
|
||||||
|
snapstatus['canGoNext'] = True
|
||||||
|
snapstatus['canGoPrevious'] = True
|
||||||
|
snapstatus['canPlay'] = True
|
||||||
|
snapstatus['canPause'] = True
|
||||||
|
snapstatus['canSeek'] = 'duration' in snapstatus
|
||||||
|
snapstatus['canControl'] = True
|
||||||
|
return snapstatus
|
||||||
|
|
||||||
def _update_properties(self, force=False):
|
def _update_properties(self, force=False):
|
||||||
logger.debug(f'update_properties force: {force}')
|
logger.debug(f'update_properties force: {force}')
|
||||||
old_position = self._position
|
old_position = self._position
|
||||||
|
@ -621,27 +657,9 @@ class MPDWrapper(object):
|
||||||
logger.info(f'changed_song: {changed_song}')
|
logger.info(f'changed_song: {changed_song}')
|
||||||
self._time = new_time = int(time.time())
|
self._time = new_time = int(time.time())
|
||||||
|
|
||||||
snapstatus = {}
|
snapstatus = self._get_properties(new_status)
|
||||||
for key, value in new_status.items():
|
send({"jsonrpc": "2.0", "method": "Plugin.Stream.Player.Properties",
|
||||||
try:
|
"params": snapstatus})
|
||||||
mapped_key = status_mapping[key][0]
|
|
||||||
mapped_val = status_mapping[key][1](value)
|
|
||||||
snapstatus[mapped_key] = mapped_val
|
|
||||||
logger.debug(
|
|
||||||
f'key: {key}, value: {value}, mapped key: {mapped_key}, mapped value: {mapped_val}')
|
|
||||||
except KeyError:
|
|
||||||
logger.debug(f'property "{key}" not supported')
|
|
||||||
except (ValueError, TypeError):
|
|
||||||
logger.warning(
|
|
||||||
f"Can't cast value {value} to {status_mapping[key][1]}")
|
|
||||||
|
|
||||||
snapstatus['canGoNext'] = True
|
|
||||||
snapstatus['canGoPrevious'] = True
|
|
||||||
snapstatus['canPlay'] = True
|
|
||||||
snapstatus['canPause'] = True
|
|
||||||
snapstatus['canSeek'] = 'duration' in snapstatus
|
|
||||||
snapstatus['canControl'] = True
|
|
||||||
send({"jsonrpc": "2.0", "method": "Player.Properties", "params": snapstatus})
|
|
||||||
|
|
||||||
if 'elapsed' in new_status:
|
if 'elapsed' in new_status:
|
||||||
new_position = float(new_status['elapsed'])
|
new_position = float(new_status['elapsed'])
|
||||||
|
|
|
@ -264,16 +264,36 @@ void PcmStream::onControlRequest(const jsonrpcpp::Request& request)
|
||||||
void PcmStream::onControlNotification(const jsonrpcpp::Notification& notification)
|
void PcmStream::onControlNotification(const jsonrpcpp::Notification& notification)
|
||||||
{
|
{
|
||||||
LOG(INFO, LOG_TAG) << "Notification method: " << notification.method() << ", params: " << notification.params().to_json() << "\n";
|
LOG(INFO, LOG_TAG) << "Notification method: " << notification.method() << ", params: " << notification.params().to_json() << "\n";
|
||||||
if (notification.method() == "Player.Metadata")
|
if (notification.method() == "Plugin.Stream.Player.Metadata")
|
||||||
{
|
{
|
||||||
LOG(DEBUG, LOG_TAG) << "Received metadata notification\n";
|
LOG(DEBUG, LOG_TAG) << "Received metadata notification\n";
|
||||||
setMeta(notification.params().to_json());
|
setMeta(notification.params().to_json());
|
||||||
}
|
}
|
||||||
else if (notification.method() == "Player.Properties")
|
else if (notification.method() == "Plugin.Stream.Player.Properties")
|
||||||
{
|
{
|
||||||
LOG(DEBUG, LOG_TAG) << "Received properties notification\n";
|
LOG(DEBUG, LOG_TAG) << "Received properties notification\n";
|
||||||
setProperties(notification.params().to_json());
|
setProperties(notification.params().to_json());
|
||||||
}
|
}
|
||||||
|
else if (notification.method() == "Plugin.Stream.Ready")
|
||||||
|
{
|
||||||
|
LOG(DEBUG, LOG_TAG) << "Plugin is ready\n";
|
||||||
|
ctrl_script_->send({++req_id_, "Plugin.Stream.Player.GetProperties"}, [this](const jsonrpcpp::Response& response) {
|
||||||
|
LOG(INFO, LOG_TAG) << "Response for Plugin.Stream.Player.GetProperties: " << response.to_json() << "\n";
|
||||||
|
if (response.error().code() == 0)
|
||||||
|
setMeta(response.result());
|
||||||
|
});
|
||||||
|
ctrl_script_->send({++req_id_, "Plugin.Stream.Player.GetMetadata"}, [this](const jsonrpcpp::Response& response) {
|
||||||
|
LOG(INFO, LOG_TAG) << "Response for Plugin.Stream.Player.GetMetadata: " << response.to_json() << "\n";
|
||||||
|
if (response.error().code() == 0)
|
||||||
|
setProperties(response.result());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (notification.method() == "Plugin.Stream.Log")
|
||||||
|
{
|
||||||
|
std::string severity = notification.params().get("severity");
|
||||||
|
std::string message = notification.params().get("message");
|
||||||
|
LOG(INFO, LOG_TAG) << "Plugin log - severity: " << severity << ", message: " << message << "\n";
|
||||||
|
}
|
||||||
else
|
else
|
||||||
LOG(WARNING, LOG_TAG) << "Received unknown notification method: '" << notification.method() << "'\n";
|
LOG(WARNING, LOG_TAG) << "Received unknown notification method: '" << notification.method() << "'\n";
|
||||||
}
|
}
|
||||||
|
@ -312,9 +332,11 @@ void PcmStream::start()
|
||||||
active_ = true;
|
active_ = true;
|
||||||
|
|
||||||
if (ctrl_script_)
|
if (ctrl_script_)
|
||||||
|
{
|
||||||
ctrl_script_->start(
|
ctrl_script_->start(
|
||||||
getId(), server_settings_, [this](const jsonrpcpp::Notification& notification) { onControlNotification(notification); },
|
getId(), server_settings_, [this](const jsonrpcpp::Notification& notification) { onControlNotification(notification); },
|
||||||
[this](const jsonrpcpp::Request& request) { onControlRequest(request); }, [this](std::string message) { onControlLog(std::move(message)); });
|
[this](const jsonrpcpp::Request& request) { onControlRequest(request); }, [this](std::string message) { onControlLog(std::move(message)); });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -462,7 +484,7 @@ void PcmStream::setProperty(const jsonrpcpp::Request& request, const CtrlScript:
|
||||||
|
|
||||||
if (ctrl_script_)
|
if (ctrl_script_)
|
||||||
{
|
{
|
||||||
jsonrpcpp::Request req(++req_id_, "Player.SetProperty", {name, value});
|
jsonrpcpp::Request req(++req_id_, "Plugin.Stream.Player.SetProperty", {name, value});
|
||||||
ctrl_script_->send(req, response_handler);
|
ctrl_script_->send(req, response_handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -489,7 +511,7 @@ void PcmStream::control(const jsonrpcpp::Request& request, const CtrlScript::OnR
|
||||||
jsonrpcpp::Parameter params{"command", command};
|
jsonrpcpp::Parameter params{"command", command};
|
||||||
if (request.params().has("params"))
|
if (request.params().has("params"))
|
||||||
params.add("params", request.params().get("params"));
|
params.add("params", request.params().get("params"));
|
||||||
jsonrpcpp::Request req(++req_id_, "Player.Control", params);
|
jsonrpcpp::Request req(++req_id_, "Plugin.Stream.Player.Control", params);
|
||||||
ctrl_script_->send(req, response_handler);
|
ctrl_script_->send(req, response_handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -497,6 +519,12 @@ void PcmStream::control(const jsonrpcpp::Request& request, const CtrlScript::OnR
|
||||||
|
|
||||||
void PcmStream::setMeta(const Metatags& meta)
|
void PcmStream::setMeta(const Metatags& meta)
|
||||||
{
|
{
|
||||||
|
if ((meta_ != nullptr) && (meta == *meta_))
|
||||||
|
{
|
||||||
|
LOG(DEBUG, LOG_TAG) << "setMeta: Meta data did not change\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
meta_ = std::make_shared<Metatags>(meta);
|
meta_ = std::make_shared<Metatags>(meta);
|
||||||
LOG(INFO, LOG_TAG) << "setMeta, stream: " << getId() << ", metadata: " << meta_->toJson() << "\n";
|
LOG(INFO, LOG_TAG) << "setMeta, stream: " << getId() << ", metadata: " << meta_->toJson() << "\n";
|
||||||
|
|
||||||
|
@ -511,6 +539,12 @@ void PcmStream::setMeta(const Metatags& meta)
|
||||||
|
|
||||||
void PcmStream::setProperties(const Properties& props)
|
void PcmStream::setProperties(const Properties& props)
|
||||||
{
|
{
|
||||||
|
if ((properties_ != nullptr) && (props == *properties_))
|
||||||
|
{
|
||||||
|
LOG(DEBUG, LOG_TAG) << "setProperties: Properties did not change\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
properties_ = std::make_shared<Properties>(props);
|
properties_ = std::make_shared<Properties>(props);
|
||||||
LOG(INFO, LOG_TAG) << "setProperties, stream: " << getId() << ", properties: " << props.toJson() << "\n";
|
LOG(INFO, LOG_TAG) << "setProperties, stream: " << getId() << ", properties: " << props.toJson() << "\n";
|
||||||
|
|
||||||
|
|
|
@ -216,7 +216,7 @@ protected:
|
||||||
boost::asio::io_context& ioc_;
|
boost::asio::io_context& ioc_;
|
||||||
ServerSettings server_settings_;
|
ServerSettings server_settings_;
|
||||||
std::unique_ptr<CtrlScript> ctrl_script_;
|
std::unique_ptr<CtrlScript> ctrl_script_;
|
||||||
size_t req_id_;
|
int req_id_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace streamreader
|
} // namespace streamreader
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue