mirror of
https://github.com/badaix/snapcast.git
synced 2025-04-28 17:57:05 +02:00
Make metadata part of the properties
This commit is contained in:
parent
5cebc64e15
commit
004ea21e3f
13 changed files with 134 additions and 153 deletions
|
@ -26,6 +26,8 @@
|
|||
|
||||
#include "common/aixlog.hpp"
|
||||
#include "common/json.hpp"
|
||||
#include "common/metatags.hpp"
|
||||
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
|
@ -152,6 +154,8 @@ public:
|
|||
fromJson(j);
|
||||
}
|
||||
|
||||
/// Meta data
|
||||
std::optional<Metatags> metatags;
|
||||
/// https://www.musicpd.org/doc/html/protocol.html#tags
|
||||
/// The current playback status
|
||||
std::optional<PlaybackStatus> playback_status;
|
||||
|
@ -202,6 +206,8 @@ public:
|
|||
addTag(j, "canPause", can_pause);
|
||||
addTag(j, "canSeek", can_seek);
|
||||
addTag(j, "canControl", can_control);
|
||||
if (metatags.has_value())
|
||||
addTag(j, "metadata", metatags->toJson());
|
||||
return j;
|
||||
}
|
||||
|
||||
|
@ -209,7 +215,7 @@ public:
|
|||
{
|
||||
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"};
|
||||
"canGoNext", "canGoPrevious", "canPlay", "canPause", "canSeek", "canControl", "metadata"};
|
||||
for (const auto& element : j.items())
|
||||
{
|
||||
bool is_rw = (rw_props.find(element.key()) != rw_props.end());
|
||||
|
@ -243,12 +249,38 @@ public:
|
|||
readTag(j, "canPause", can_pause, false);
|
||||
readTag(j, "canSeek", can_seek, false);
|
||||
readTag(j, "canControl", can_control, false);
|
||||
|
||||
if (j.contains("metadata"))
|
||||
{
|
||||
Metatags m;
|
||||
m.fromJson(j["metadata"]);
|
||||
metatags = m;
|
||||
}
|
||||
else
|
||||
metatags = std::nullopt;
|
||||
}
|
||||
|
||||
bool operator==(const Properties& other) const
|
||||
{
|
||||
// expensive, but not called ofetn and less typing
|
||||
return (toJson() == other.toJson());
|
||||
|
||||
// clang-format off
|
||||
// return (playback_status == other.playback_status &&
|
||||
// loop_status == other.loop_status &&
|
||||
// rate == other.rate &&
|
||||
// shuffle == other.shuffle &&
|
||||
// volume == other.volume &&
|
||||
// position == other.position &&
|
||||
// minimum_rate == other.minimum_rate &&
|
||||
// maximum_rate == other.maximum_rate &&
|
||||
// can_go_next == other.can_go_next &&
|
||||
// can_go_previous == other.can_go_previous &&
|
||||
// can_play == other.can_play &&
|
||||
// can_pause == other.can_pause &&
|
||||
// can_seek == other.can_seek &&
|
||||
// can_control == other.can_control);
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -376,7 +376,8 @@ class MPDWrapper(object):
|
|||
success = True
|
||||
command = request['params']['command']
|
||||
params = request['params'].get('params', {})
|
||||
logger.debug(f'Control command: {command}, params: {params}')
|
||||
logger.debug(
|
||||
f'Control command: {command}, params: {params}')
|
||||
if command == 'Next':
|
||||
self.next()
|
||||
elif command == 'Previous':
|
||||
|
@ -495,8 +496,20 @@ class MPDWrapper(object):
|
|||
def __track_key(self, snapmeta):
|
||||
return hash(snapmeta.get('artist', [''])[0] + snapmeta.get('album', snapmeta.get('title', '')))
|
||||
|
||||
def update_albumart(self, snapmeta):
|
||||
def get_albumart(self, snapmeta, cached):
|
||||
album_key = 'musicbrainzAlbumId'
|
||||
track_key = self.__track_key(snapmeta)
|
||||
album_art = self._album_art_map.get(track_key)
|
||||
if album_art is not None:
|
||||
if album_art == '':
|
||||
return None
|
||||
else:
|
||||
return album_art
|
||||
|
||||
if cached:
|
||||
return None
|
||||
|
||||
self._album_art_map[track_key] = ''
|
||||
try:
|
||||
if not album_key in snapmeta:
|
||||
mbartist = None
|
||||
|
@ -521,22 +534,21 @@ class MPDWrapper(object):
|
|||
data = musicbrainzngs.get_image_list(snapmeta[album_key])
|
||||
for image in data["images"]:
|
||||
if "Front" in image["types"] and image["approved"]:
|
||||
snapmeta['artUrl'] = image["thumbnails"]["small"]
|
||||
album_art = image["thumbnails"]["small"]
|
||||
logger.debug(
|
||||
f'{snapmeta["artUrl"]} is an approved front image')
|
||||
logger.info(f'Snapmeta: {snapmeta}')
|
||||
self._album_art_map[self.__track_key(
|
||||
snapmeta)] = snapmeta['artUrl']
|
||||
send(
|
||||
{"jsonrpc": "2.0", "method": "Plugin.Stream.Player.Metadata", "params": snapmeta})
|
||||
f'{album_art} is an approved front image')
|
||||
self._album_art_map[track_key] = album_art
|
||||
break
|
||||
|
||||
except musicbrainzngs.musicbrainz.ResponseError as e:
|
||||
logger.error(
|
||||
f'Error while getting cover for {snapmeta[album_key]}: {e}')
|
||||
self._album_art_map[self.__track_key(snapmeta)] = ''
|
||||
album_art = self._album_art_map[track_key]
|
||||
if album_art == '':
|
||||
return None
|
||||
return album_art
|
||||
|
||||
def update_metadata(self):
|
||||
def get_metadata(self):
|
||||
"""
|
||||
Translate metadata returned by MPD to the MPRIS v2 syntax.
|
||||
http://www.freedesktop.org/wiki/Specifications/mpris-spec/metadata
|
||||
|
@ -586,19 +598,12 @@ class MPDWrapper(object):
|
|||
snapmeta['artist'] = [fields[0]]
|
||||
snapmeta['title'] = fields[1]
|
||||
|
||||
track_key = self.__track_key(snapmeta)
|
||||
if track_key in self._album_art_map:
|
||||
art_url = self._album_art_map[track_key]
|
||||
art_url = self.get_albumart(snapmeta, True)
|
||||
if art_url is not None:
|
||||
logger.info(f'album art cache hit: "{art_url}"')
|
||||
if art_url != '':
|
||||
snapmeta['artUrl'] = art_url
|
||||
snapmeta['artUrl'] = art_url
|
||||
|
||||
send({"jsonrpc": "2.0",
|
||||
"method": "Plugin.Stream.Player.Metadata", "params": snapmeta})
|
||||
if not track_key in self._album_art_map:
|
||||
# t = threading.Thread(target=self.update_albumart, args=[snapmeta])
|
||||
# t.start()
|
||||
self.update_albumart(snapmeta)
|
||||
return snapmeta
|
||||
|
||||
def __diff_map(self, old_map, new_map):
|
||||
diff = {}
|
||||
|
@ -662,14 +667,11 @@ class MPDWrapper(object):
|
|||
logger.debug('nothing to do')
|
||||
return
|
||||
|
||||
logger.info(f'new status: {new_status}')
|
||||
logger.info(f'changed_status: {changed_status}')
|
||||
logger.info(f'changed_song: {changed_song}')
|
||||
logger.info(
|
||||
f'new status: {new_status}, changed_status: {changed_status}, changed_song: {changed_song}')
|
||||
self._time = new_time = int(time.time())
|
||||
|
||||
snapstatus = self._get_properties(new_status)
|
||||
send({"jsonrpc": "2.0", "method": "Plugin.Stream.Player.Properties",
|
||||
"params": snapstatus})
|
||||
|
||||
if 'elapsed' in new_status:
|
||||
new_position = float(new_status['elapsed'])
|
||||
|
@ -682,9 +684,9 @@ class MPDWrapper(object):
|
|||
|
||||
# "player" subsystem
|
||||
|
||||
force = len(changed_song) > 0
|
||||
new_song = len(changed_song) > 0
|
||||
|
||||
if not force:
|
||||
if not new_song:
|
||||
if new_status['state'] == 'play':
|
||||
expected_position = old_position + (new_time - old_time)
|
||||
else:
|
||||
|
@ -698,7 +700,18 @@ class MPDWrapper(object):
|
|||
|
||||
else:
|
||||
# Update current song metadata
|
||||
self.update_metadata()
|
||||
snapstatus["metadata"] = self.get_metadata()
|
||||
|
||||
send({"jsonrpc": "2.0", "method": "Plugin.Stream.Player.Properties",
|
||||
"params": snapstatus})
|
||||
|
||||
if new_song:
|
||||
if 'artUrl' not in snapstatus['metadata']:
|
||||
album_art = self.get_albumart(snapstatus['metadata'], False)
|
||||
if album_art is not None:
|
||||
snapstatus['metadata']['artUrl'] = album_art
|
||||
send(
|
||||
{"jsonrpc": "2.0", "method": "Plugin.Stream.Player.Properties", "params": snapstatus})
|
||||
|
||||
# Compatibility functions
|
||||
|
||||
|
|
|
@ -331,12 +331,12 @@ class SnapcastWrapper(object):
|
|||
if not 'mpris:artUrl' in self._metadata:
|
||||
self._metadata['mpris:artUrl'] = f'http://{self._params["host"]}:{self._params["port"]}/snapcast-512.png'
|
||||
|
||||
logger.info(f'mpris meta: {self._metadata}')
|
||||
logger.debug(f'mpris meta: {self._metadata}')
|
||||
|
||||
self.notify_about_track(self._metadata)
|
||||
new_meta = self._dbus_service.update_property('org.mpris.MediaPlayer2.Player',
|
||||
'Metadata')
|
||||
logger.info(f'new meta {new_meta}')
|
||||
logger.debug(f'new meta {new_meta}')
|
||||
except Exception as e:
|
||||
logger.error(f'Error in update_metadata: {str(e)}')
|
||||
|
||||
|
@ -344,11 +344,10 @@ class SnapcastWrapper(object):
|
|||
try:
|
||||
if props is None:
|
||||
props = {}
|
||||
logger.info(f'Properties: "{props}"')
|
||||
logger.debug(f'Properties: "{props}"')
|
||||
# store the last receive time stamp for better position estimation
|
||||
if 'position' in props:
|
||||
props['_received'] = time.time()
|
||||
|
||||
# ignore "internal" properties, starting with "_"
|
||||
changed_properties = {}
|
||||
for key, value in props.items():
|
||||
|
@ -369,6 +368,8 @@ class SnapcastWrapper(object):
|
|||
if key in property_mapping:
|
||||
self._dbus_service.update_property(
|
||||
'org.mpris.MediaPlayer2.Player', property_mapping[key])
|
||||
if 'metadata' in changed_properties:
|
||||
self.__update_metadata(props.get('metadata', None))
|
||||
except Exception as e:
|
||||
logger.error(f'Error in update_properties: {str(e)}')
|
||||
|
||||
|
@ -388,8 +389,6 @@ class SnapcastWrapper(object):
|
|||
logger.info(f'Stream id: {self._stream_id}')
|
||||
for stream in jmsg['result']['server']['streams']:
|
||||
if stream['id'] == self._stream_id:
|
||||
if 'metadata' in stream:
|
||||
self.__update_metadata(stream['metadata'])
|
||||
if 'properties' in stream:
|
||||
self.__update_properties(stream['properties'])
|
||||
break
|
||||
|
@ -399,14 +398,6 @@ class SnapcastWrapper(object):
|
|||
logger.info(f'Stream id: {self._stream_id}')
|
||||
elif jmsg['method'] == "Group.OnStreamChanged":
|
||||
self.send_request("Server.GetStatus")
|
||||
elif jmsg["method"] == "Stream.OnMetadata":
|
||||
stream_id = jmsg["params"]["id"]
|
||||
logger.info(f'Stream meta changed for "{stream_id}"')
|
||||
if self._stream_id != stream_id:
|
||||
return
|
||||
meta = jmsg["params"]["metadata"]
|
||||
self.__update_metadata(meta)
|
||||
|
||||
elif jmsg["method"] == "Stream.OnProperties":
|
||||
stream_id = jmsg["params"]["id"]
|
||||
logger.info(
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
A stream plugin is (this might change in future) an executable binary or script that is started by the server for a specific stream and offers playback control capabilities and provides information about the stream's state, as well as metadata for the currently playing track.
|
||||
The Snapcast server communicates via stdin/stdout with the plugin and sends newline delimited JSON-RPC commands and receives responses and notifications from the plugin, as described below. In upcoming releases shared library plugins might be supported as well.
|
||||
|
||||
## Requests:
|
||||
## Requests
|
||||
|
||||
A Stream plugin must support and handle the following requests, sent by the Snapcast server
|
||||
|
||||
### Plugin.Stream.Player.Control:
|
||||
### Plugin.Stream.Player.Control
|
||||
|
||||
Used to control the player
|
||||
|
||||
|
@ -19,33 +19,33 @@ Used to control the player
|
|||
Supported `command`s:
|
||||
|
||||
- `Play`: Start playback
|
||||
- `params`: none
|
||||
- `params`: none
|
||||
- `Pause`: Stop playback
|
||||
- `params`: none
|
||||
- `params`: none
|
||||
- `PlayPause`: Toggle play/pause
|
||||
- `params`: none
|
||||
- `params`: none
|
||||
- `Stop`: Stop playback
|
||||
- `params`: none
|
||||
- `params`: none
|
||||
- `Next`: Skip to next track
|
||||
- `params`: none
|
||||
- `params`: none
|
||||
- `Previous`: Skip to previous track
|
||||
- `params`: none
|
||||
- `params`: none
|
||||
- `Seek`: Seek forward or backward in the current track
|
||||
- `params`:
|
||||
- `Offset`: [float] seek offset in seconds
|
||||
- `params`:
|
||||
- `Offset`: [float] seek offset in seconds
|
||||
- `SetPosition`: Set the current track position in seconds
|
||||
- `params`:
|
||||
- `Position`: [float] the new track position
|
||||
- `TrackId`: [string] the optional currently playing track's identifier
|
||||
- `params`:
|
||||
- `Position`: [float] the new track position
|
||||
- `TrackId`: [string] the optional currently playing track's identifier
|
||||
|
||||
#### Example:
|
||||
#### Example
|
||||
|
||||
```json
|
||||
{"id": 1, "jsonrpc": "2.0", "method": "Plugin.Stream.Player.Control", "params": {"command": "SetPosition", "params": { "Position": 170966827, "TrackId": "/org/mpris/MediaPlayer2/Track/2"}}}
|
||||
```
|
||||
|
||||
|
||||
#### Expected response:
|
||||
#### Expected response
|
||||
|
||||
Success:
|
||||
|
||||
|
@ -59,23 +59,23 @@ Error:
|
|||
todo
|
||||
```
|
||||
|
||||
### Plugin.Stream.Player.SetProperty:
|
||||
### Plugin.Stream.Player.SetProperty
|
||||
|
||||
```json
|
||||
{"id": 1, "jsonrpc": "2.0", "method": "Plugin.Stream.Player.SetProperty", "params": {"<property>", <value>}}
|
||||
```
|
||||
|
||||
#### Supported `property`s:
|
||||
#### Supported `property`s
|
||||
|
||||
- `loopStatus`: [string] the current repeat status, one of:
|
||||
- `none`: the playback will stop when there are no more tracks to play
|
||||
- `track`: the current track will start again from the begining once it has finished playing
|
||||
- `playlist`: the playback loops through a list of tracks
|
||||
- `none`: the playback will stop when there are no more tracks to play
|
||||
- `track`: the current track will start again from the begining once it has finished playing
|
||||
- `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..)
|
||||
|
||||
#### Expected response:
|
||||
#### Expected response
|
||||
|
||||
Success:
|
||||
|
||||
|
@ -95,7 +95,7 @@ todo
|
|||
{"id": 1, "jsonrpc": "2.0", "method": "Plugin.Stream.Player.GetProperties"}
|
||||
```
|
||||
|
||||
#### Expected response:
|
||||
#### Expected response
|
||||
|
||||
Success:
|
||||
|
||||
|
@ -106,13 +106,13 @@ Success:
|
|||
#### Supported `property`s
|
||||
|
||||
- `playbackStatus`: [string] The current playback status, one of:
|
||||
- `playing`
|
||||
- `paused`
|
||||
- `stopped`
|
||||
- `playing`
|
||||
- `paused`
|
||||
- `stopped`
|
||||
- `loopStatus`: [string] The current repeat status, one of:
|
||||
- `none`: The playback will stop when there are no more tracks to play
|
||||
- `track`: The current track will start again from the begining once it has finished playing
|
||||
- `playlist`: The playback loops through a list of tracks
|
||||
- `none`: The playback will stop when there are no more tracks to play
|
||||
- `track`: The current track will start again from the begining once it has finished playing
|
||||
- `playlist`: The playback loops through a list of tracks
|
||||
- `rate`: [float] The current playback rate, valid range (0..)
|
||||
- `shuffle`: [bool] Traverse through the playlist in random order
|
||||
- `volume`: [int] Voume in percent, valid range [0..100]
|
||||
|
@ -132,11 +132,13 @@ todo
|
|||
|
||||
### Plugin.Stream.Player.GetMetadata
|
||||
|
||||
TODO: Metadata are part of the properties
|
||||
|
||||
```json
|
||||
{"id": 1, "jsonrpc": "2.0", "method": "Plugin.Stream.Player.GetMetadata"}
|
||||
```
|
||||
|
||||
#### Expected response:
|
||||
#### Expected response
|
||||
|
||||
Success:
|
||||
|
||||
|
@ -144,10 +146,12 @@ Success:
|
|||
{"id": 1, "jsonrpc": "2.0", "result": {"artist":["Travis Scott & HVME"],"file":"http://wdr-1live-live.icecast.wdr.de/wdr/1live/live/mp3/128/stream.mp3","name":"1Live, Westdeutscher Rundfunk Koeln","title":"Goosebumps (Remix)","trackId":"3"}}
|
||||
```
|
||||
|
||||
## Notifications:
|
||||
## Notifications
|
||||
|
||||
### Plugin.Stream.Player.Metadata
|
||||
|
||||
TODO: Metadata are part of the properties
|
||||
|
||||
```json
|
||||
{"jsonrpc": "2.0", "method": "Plugin.Stream.Player.Metadata", "params": {"artist":["Travis Scott & HVME"],"file":"http://wdr-1live-live.icecast.wdr.de/wdr/1live/live/mp3/128/stream.mp3","name":"1Live, Westdeutscher Rundfunk Koeln","title":"Goosebumps (Remix)","trackId":"3"}}
|
||||
```
|
||||
|
@ -199,7 +203,6 @@ Success:
|
|||
- `spotifyArtistId`: [string] The [Spotify Artist ID](https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids)
|
||||
- `spotifyTrackId`: [string] The [Spotify Track ID](https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids)
|
||||
|
||||
|
||||
### Plugin.Stream.Player.Properties
|
||||
|
||||
```json
|
||||
|
@ -234,12 +237,11 @@ The plugin shall send this notification when it's up and ready to receive comman
|
|||
{"jsonrpc": "2.0", "method": "Plugin.Stream.Ready"}
|
||||
```
|
||||
|
||||
|
||||
# Server:
|
||||
# Server
|
||||
|
||||
TODO: this belongs to doc/json_rpc_api/v2_0_0.md
|
||||
|
||||
## Requests:
|
||||
## Requests
|
||||
|
||||
To control the stream state, the following commands can be sent to the Snapcast server and will be forwarded to the respective stream:
|
||||
|
||||
|
@ -251,7 +253,7 @@ To control the stream state, the following commands can be sent to the Snapcast
|
|||
{"id": 1, "jsonrpc": "2.0", "method": "Stream.SetProperty", "params": {"id": "Pipe", "property": property, "value": value}}
|
||||
```
|
||||
|
||||
## Notifications:
|
||||
## Notifications
|
||||
|
||||
```json
|
||||
{"jsonrpc": "2.0", "method": "Stream.OnMetadata", "params": {"id": "Pipe", "metadata": {}}}
|
||||
|
|
|
@ -49,23 +49,6 @@ void Server::onNewSession(std::shared_ptr<StreamSession> session)
|
|||
}
|
||||
|
||||
|
||||
void Server::onMetadataChanged(const PcmStream* pcmStream, const Metatags& metadata)
|
||||
{
|
||||
// clang-format off
|
||||
// Notification: {"jsonrpc":"2.0","method":"Stream.OnMetadata","params":{"id":"stream 1", "metadata": {"album": "some album", "artist": "some artist", "track": "some track"...}}}
|
||||
// clang-format on
|
||||
|
||||
LOG(DEBUG, LOG_TAG) << "Metadata changed, stream: " << pcmStream->getName() << ", meta: " << metadata.toJson().dump(3) << "\n";
|
||||
|
||||
// streamServer_->onMetadataChanged(pcmStream, meta);
|
||||
|
||||
// Send meta to all connected clients
|
||||
json notification = jsonrpcpp::Notification("Stream.OnMetadata", jsonrpcpp::Parameter("id", pcmStream->getId(), "metadata", metadata.toJson())).to_json();
|
||||
controlServer_->send(notification.dump(), nullptr);
|
||||
// cout << "Notification: " << notification.dump() << "\n";
|
||||
}
|
||||
|
||||
|
||||
void Server::onPropertiesChanged(const PcmStream* pcmStream, const Properties& properties)
|
||||
{
|
||||
LOG(DEBUG, LOG_TAG) << "Properties changed, stream: " << pcmStream->getName() << ", properties: " << properties.toJson().dump(3) << "\n";
|
||||
|
|
|
@ -78,7 +78,6 @@ private:
|
|||
void onNewSession(std::shared_ptr<StreamSession> session) override;
|
||||
|
||||
/// Implementation of PcmListener
|
||||
void onMetadataChanged(const PcmStream* pcmStream, const Metatags& metadata) override;
|
||||
void onPropertiesChanged(const PcmStream* pcmStream, const Properties& properties) override;
|
||||
void onStateChanged(const PcmStream* pcmStream, ReaderState state) override;
|
||||
void onChunkRead(const PcmStream* pcmStream, const msg::PcmChunk& chunk) override;
|
||||
|
|
|
@ -183,7 +183,7 @@ void AirplayStream::push()
|
|||
// mden = metadata end, pcen == picture end
|
||||
if (metadata_dirty_ && entry_->type == "ssnc" && (entry_->code == "mden" || entry_->code == "pcen"))
|
||||
{
|
||||
setMetadata(metadata_);
|
||||
// setMetadata(metadata_);
|
||||
metadata_dirty_ = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,7 +161,7 @@ void LibrespotStream::onStderrMsg(const std::string& line)
|
|||
Metatags meta;
|
||||
meta.artist = std::vector<std::string>{j["ARTIST"].get<std::string>()};
|
||||
meta.title = j["TITLE"].get<std::string>();
|
||||
setMetadata(meta);
|
||||
// TODO setMetadata(meta);
|
||||
}
|
||||
else if (regex_search(line, m, re_track_loaded))
|
||||
{
|
||||
|
@ -169,7 +169,7 @@ void LibrespotStream::onStderrMsg(const std::string& line)
|
|||
Metatags meta;
|
||||
meta.title = string(m[1]);
|
||||
meta.duration = cpt::stod(m[2]) / 1000.;
|
||||
setMetadata(meta);
|
||||
// TODO setMetadata(meta);
|
||||
Properties properties;
|
||||
// properties.can_seek = true;
|
||||
// properties.can_control = true;
|
||||
|
|
|
@ -84,16 +84,6 @@ void MetaStream::stop()
|
|||
}
|
||||
|
||||
|
||||
void MetaStream::onMetadataChanged(const PcmStream* pcmStream, const Metatags& metadata)
|
||||
{
|
||||
LOG(DEBUG, LOG_TAG) << "onMetadataChanged: " << pcmStream->getName() << "\n";
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
if (pcmStream != active_stream_.get())
|
||||
return;
|
||||
setMetadata(metadata);
|
||||
}
|
||||
|
||||
|
||||
void MetaStream::onPropertiesChanged(const PcmStream* pcmStream, const Properties& properties)
|
||||
{
|
||||
LOG(DEBUG, LOG_TAG) << "onPropertiesChanged: " << pcmStream->getName() << "\n";
|
||||
|
@ -118,7 +108,6 @@ void MetaStream::onStateChanged(const PcmStream* pcmStream, ReaderState state)
|
|||
LOG(INFO, LOG_TAG) << "Stream: " << name_ << ", switching active stream: " << (active_stream_ ? active_stream_->getName() : "<null>") << " => "
|
||||
<< new_stream->getName() << "\n";
|
||||
active_stream_ = new_stream;
|
||||
setMetadata(active_stream_->getMetadata());
|
||||
setProperties(active_stream_->getProperties());
|
||||
resampler_ = make_unique<Resampler>(active_stream_->getSampleFormat(), sampleFormat_);
|
||||
};
|
||||
|
|
|
@ -62,7 +62,6 @@ public:
|
|||
|
||||
protected:
|
||||
/// Implementation of PcmListener
|
||||
void onMetadataChanged(const PcmStream* pcmStream, const Metatags& metadata) override;
|
||||
void onPropertiesChanged(const PcmStream* pcmStream, const Properties& properties) override;
|
||||
void onStateChanged(const PcmStream* pcmStream, ReaderState state) override;
|
||||
void onChunkRead(const PcmStream* pcmStream, const msg::PcmChunk& chunk) override;
|
||||
|
|
|
@ -137,12 +137,7 @@ void PcmStream::onControlNotification(const jsonrpcpp::Notification& notificatio
|
|||
try
|
||||
{
|
||||
LOG(INFO, LOG_TAG) << "Notification method: " << notification.method() << ", params: " << notification.params().to_json() << "\n";
|
||||
if (notification.method() == "Plugin.Stream.Player.Metadata")
|
||||
{
|
||||
LOG(DEBUG, LOG_TAG) << "Received metadata notification\n";
|
||||
setMetadata(notification.params().to_json());
|
||||
}
|
||||
else if (notification.method() == "Plugin.Stream.Player.Properties")
|
||||
if (notification.method() == "Plugin.Stream.Player.Properties")
|
||||
{
|
||||
LOG(DEBUG, LOG_TAG) << "Received properties notification\n";
|
||||
setProperties(notification.params().to_json());
|
||||
|
@ -155,15 +150,10 @@ void PcmStream::onControlNotification(const jsonrpcpp::Notification& notificatio
|
|||
if (response.error().code() == 0)
|
||||
setProperties(response.result());
|
||||
});
|
||||
stream_ctrl_->command({++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)
|
||||
setMetadata(response.result());
|
||||
});
|
||||
|
||||
// TODO: Add capabilities or settings?
|
||||
// {"jsonrpc": "2.0", "method": "Plugin.Stream.Ready", "params": {"pollProperties": 10, "responseTimeout": 5}}
|
||||
pollProperties();
|
||||
// pollProperties();
|
||||
}
|
||||
else if (notification.method() == "Plugin.Stream.Log")
|
||||
{
|
||||
|
@ -452,44 +442,31 @@ void PcmStream::play(ResultHandler handler)
|
|||
}
|
||||
|
||||
|
||||
void PcmStream::setMetadata(const Metatags& metadata)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
if (metadata == metadata_)
|
||||
{
|
||||
LOG(DEBUG, LOG_TAG) << "setMetadata: Metadata did not change\n";
|
||||
return;
|
||||
}
|
||||
|
||||
metadata_ = metadata;
|
||||
LOG(INFO, LOG_TAG) << "setMetadata, stream: " << getId() << ", metadata: " << metadata_.toJson() << "\n";
|
||||
|
||||
// Trigger a stream update
|
||||
for (auto* listener : pcmListeners_)
|
||||
{
|
||||
if (listener != nullptr)
|
||||
listener->onMetadataChanged(this, metadata_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PcmStream::setProperties(const Properties& properties)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
if (properties == properties_)
|
||||
|
||||
Properties props = properties;
|
||||
// Missing metadata means, they didn't change, so
|
||||
// enrich the new properites with old metadata
|
||||
if (!props.metatags.has_value() && properties_.metatags.has_value())
|
||||
props.metatags = properties_.metatags;
|
||||
|
||||
if (props == properties_)
|
||||
{
|
||||
LOG(DEBUG, LOG_TAG) << "setProperties: Properties did not change\n";
|
||||
return;
|
||||
}
|
||||
|
||||
properties_ = properties;
|
||||
properties_ = std::move(props);
|
||||
|
||||
LOG(INFO, LOG_TAG) << "setProperties, stream: " << getId() << ", properties: " << properties_.toJson() << "\n";
|
||||
|
||||
// Trigger a stream update
|
||||
for (auto* listener : pcmListeners_)
|
||||
{
|
||||
if (listener != nullptr)
|
||||
listener->onPropertiesChanged(this, properties);
|
||||
listener->onPropertiesChanged(this, properties_);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "common/error_code.hpp"
|
||||
#include "common/json.hpp"
|
||||
#include "common/metatags.hpp"
|
||||
#include "common/properties.hpp"
|
||||
#include "common/sample_format.hpp"
|
||||
#include "encoder/encoder.hpp"
|
||||
|
@ -98,7 +97,6 @@ static constexpr auto kControlScript = "controlscript";
|
|||
class PcmListener
|
||||
{
|
||||
public:
|
||||
virtual void onMetadataChanged(const PcmStream* pcmStream, const Metatags& metadata) = 0;
|
||||
virtual void onPropertiesChanged(const PcmStream* pcmStream, const Properties& properties) = 0;
|
||||
virtual void onStateChanged(const PcmStream* pcmStream, ReaderState state) = 0;
|
||||
virtual void onChunkRead(const PcmStream* pcmStream, const msg::PcmChunk& chunk) = 0;
|
||||
|
@ -166,7 +164,6 @@ protected:
|
|||
void resync(const std::chrono::nanoseconds& duration);
|
||||
void chunkEncoded(const encoder::Encoder& encoder, std::shared_ptr<msg::PcmChunk> chunk, double duration);
|
||||
|
||||
void setMetadata(const Metatags& metadata);
|
||||
void setProperties(const Properties& properties);
|
||||
|
||||
void pollProperties();
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include <regex>
|
||||
|
||||
#include "common/aixlog.hpp"
|
||||
#include "common/metatags.hpp"
|
||||
#include "common/properties.hpp"
|
||||
#include "common/utils/string_utils.hpp"
|
||||
#include "server/streamreader/control_error.hpp"
|
||||
|
|
Loading…
Add table
Reference in a new issue