mirror of
https://github.com/badaix/snapcast.git
synced 2025-05-12 08:36:43 +02:00
Modified metadata interface to be tag independent, added JSON api.
This commit is contained in:
parent
67083975b0
commit
ce17b0010a
7 changed files with 116 additions and 33 deletions
|
@ -55,7 +55,7 @@ public:
|
|||
|
||||
std::string serialize()
|
||||
{
|
||||
return METADATA + ":" + _msg->dump() + "\n";
|
||||
return METADATA + ":" + _msg->dump();
|
||||
}
|
||||
|
||||
void tag(std::string name, std::string value)
|
||||
|
|
|
@ -385,10 +385,14 @@ void StreamServer::ProcessRequest(const jsonrpcpp::request_ptr request, jsonrpcp
|
|||
{
|
||||
if (request->method.find("Stream.SetMeta") == 0)
|
||||
{
|
||||
/// Request: {"id":4,"jsonrpc":"2.0","method":"Stream.SetMeta","params":{"id":"Spotify","meta": {"album": "some album", "artist": "some artist", "track": "some track"...}}}
|
||||
/// Response: {"id":4,"jsonrpc":"2.0","result":{"stream_id":"stream 1"}}
|
||||
/// Request: {"id":4,"jsonrpc":"2.0","method":"Stream.SetMeta","params":{"stream_id":"Spotify",
|
||||
/// "meta": {"album": "some album", "artist": "some artist", "track": "some track"...}}}
|
||||
///
|
||||
/// Response: {"id":4,"jsonrpc":"2.0","result":{"stream_id":"Spotify"}}
|
||||
/// Call onMetaChanged(const PcmStream* pcmStream) for updates and notifications
|
||||
|
||||
LOG(INFO) << "Stream.SetMeta(" << request->params.get("stream_id") << ")" << request->params.get("meta") <<"\n";
|
||||
|
||||
// Find stream
|
||||
string streamId = request->params.get("stream_id");
|
||||
PcmStreamPtr stream = streamManager_->getStream(streamId);
|
||||
|
@ -396,14 +400,10 @@ void StreamServer::ProcessRequest(const jsonrpcpp::request_ptr request, jsonrpcp
|
|||
throw jsonrpcpp::InternalErrorException("Stream not found", request->id);
|
||||
|
||||
// Set metadata from request
|
||||
// stream->
|
||||
stream->setMeta(request->params.get("meta"));
|
||||
|
||||
// Update clients
|
||||
// Setup response
|
||||
|
||||
// Trigger notifications
|
||||
// onMetaChanged(stream);
|
||||
// notification.reset(new jsonrpcpp::Notification("Client.OnLatencyChanged", jsonrpcpp::Parameter("id", clientInfo->id, "latency", clientInfo->config.latency)));
|
||||
result["stream_id"] = streamId;
|
||||
}
|
||||
else
|
||||
throw jsonrpcpp::MethodNotFoundException(request->id);
|
||||
|
|
31
server/streamreader/README.meta
Normal file
31
server/streamreader/README.meta
Normal file
|
@ -0,0 +1,31 @@
|
|||
Metadata tags
|
||||
=============
|
||||
The metatag interface will propegate anything to the client, ie no restrictions on the tags.
|
||||
But, to avoid total confusion in the tag display app (like Kodi), we need some groundrules.
|
||||
|
||||
My suggestion is to stick with the Vorbis standard as described in:
|
||||
|
||||
http://www.xiph.org/vorbis/doc/v-comment.html
|
||||
https://wiki.xiph.org/Field_names
|
||||
|
||||
In addition we should accept unique identifiers of the stream such as Spotify track ID or
|
||||
MusicBrainz id which can be used to lookup additional information online.
|
||||
|
||||
Example:
|
||||
ARTIST: Pink Floyd
|
||||
ALBUM: Dark Side of the Moon
|
||||
TITLE: Money
|
||||
METADATA_BLOCK_PICTURE: base64 (https://xiph.org/flac/format.html#metadata_block_picture)
|
||||
|
||||
|
||||
Parsing tags from streams
|
||||
=========================
|
||||
I've implemented parsing tags from Librespot, but, as Librespot doesn't export anything more than
|
||||
track title I added a patch 'librespot-meta.patch' to apply to get artist/title.
|
||||
|
||||
TBH, the meta tag output from players are a mess, neither Librespot nor Shairport-sync associate
|
||||
metadata with their audio interfaces, ie they don't even export metadata when the audio interface
|
||||
supports it, at least Shairport-sync exports a ritch set of data.
|
||||
|
||||
So, to get artist/album apply the patch to librespot and recompile, otherwise you will only get the
|
||||
track title.
|
31
server/streamreader/librespot-meta.patch
Normal file
31
server/streamreader/librespot-meta.patch
Normal file
|
@ -0,0 +1,31 @@
|
|||
*** librespot/src/player.rs 2017-11-30 08:16:40.865939287 +0100
|
||||
--- librespot.meta/src/player.rs 2017-11-30 08:14:39.232954039 +0100
|
||||
***************
|
||||
*** 13,19 ****
|
||||
use audio_backend::Sink;
|
||||
use audio::{AudioFile, AudioDecrypt};
|
||||
use audio::{VorbisDecoder, VorbisPacket};
|
||||
! use metadata::{FileFormat, Track, Metadata};
|
||||
use mixer::AudioFilter;
|
||||
|
||||
#[derive(Clone)]
|
||||
--- 13,19 ----
|
||||
use audio_backend::Sink;
|
||||
use audio::{AudioFile, AudioDecrypt};
|
||||
use audio::{VorbisDecoder, VorbisPacket};
|
||||
! use metadata::{Artist, FileFormat, Track, Metadata};
|
||||
use mixer::AudioFilter;
|
||||
|
||||
#[derive(Clone)]
|
||||
***************
|
||||
*** 384,389 ****
|
||||
--- 384,392 ----
|
||||
|
||||
info!("Track \"{}\" loaded", track.name);
|
||||
|
||||
+ let artist = Artist::get(&self.session, track.artists[0]).wait().unwrap();
|
||||
+ info!("metadata:{{\"ARTIST\":\"{}\",\"TITLE\":\"{}\"}}", artist.name, track.name);
|
||||
+
|
||||
Some(decoder)
|
||||
}
|
||||
}
|
|
@ -179,3 +179,17 @@ json PcmStream::toJson() const
|
|||
return j;
|
||||
}
|
||||
|
||||
std::shared_ptr<msg::StreamTags> PcmStream::getMeta() const
|
||||
{
|
||||
return meta_;
|
||||
}
|
||||
|
||||
void PcmStream::setMeta(json jtag)
|
||||
{
|
||||
meta_.reset(new msg::StreamTags(jtag));
|
||||
|
||||
// Trigger a stream update
|
||||
if (pcmListener_)
|
||||
pcmListener_->onMetaChanged(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -83,15 +83,12 @@ public:
|
|||
virtual const std::string& getId() const;
|
||||
virtual const SampleFormat& getSampleFormat() const;
|
||||
|
||||
std::shared_ptr<msg::StreamTags> getMeta() const;
|
||||
void setMeta(json j);
|
||||
|
||||
virtual ReaderState getState() const;
|
||||
virtual json toJson() const;
|
||||
|
||||
//const msg::StreamTags *getMeta()
|
||||
std::shared_ptr<msg::StreamTags> getMeta() const
|
||||
{
|
||||
return meta_;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
std::condition_variable cv_;
|
||||
|
@ -99,6 +96,7 @@ protected:
|
|||
std::thread thread_;
|
||||
std::atomic<bool> active_;
|
||||
|
||||
|
||||
virtual void worker() = 0;
|
||||
virtual bool sleep(int32_t ms);
|
||||
void setState(const ReaderState& newState);
|
||||
|
@ -112,8 +110,6 @@ protected:
|
|||
std::unique_ptr<Encoder> encoder_;
|
||||
std::string name_;
|
||||
ReaderState state_;
|
||||
|
||||
// Stream metadata
|
||||
std::shared_ptr<msg::StreamTags> meta_;
|
||||
};
|
||||
|
||||
|
|
|
@ -93,6 +93,9 @@ void SpotifyStream::initExeAndPath(const std::string& filename)
|
|||
|
||||
void SpotifyStream::onStderrMsg(const char* buffer, size_t n)
|
||||
{
|
||||
static bool libreelec_patched = false;
|
||||
smatch m;
|
||||
|
||||
// Watch stderr for 'Loading track' messages and set the stream metadata
|
||||
// For more than track name check: https://github.com/plietar/librespot/issues/154
|
||||
|
||||
|
@ -124,29 +127,37 @@ void SpotifyStream::onStderrMsg(const char* buffer, size_t n)
|
|||
LOG(INFO) << "(" << getName() << ") " << logmsg << "\n";
|
||||
}
|
||||
|
||||
// Track tags "Julia Michaels" "Issues - Acoustic"
|
||||
if (logmsg.find("Track tags") != string::npos)
|
||||
{
|
||||
// Traditional Libreelec meta interface, only track name
|
||||
regex re("Track tags \"(.*)\" \"(.*)\"");
|
||||
smatch m;
|
||||
// Librespot patch:
|
||||
// info!("metadata:{{\"ARTIST\":\"{}\",\"TITLE\":\"{}\"}}", artist.name, track.name);
|
||||
// non patched:
|
||||
// info!("Track \"{}\" loaded", track.name);
|
||||
|
||||
if (regex_search(logmsg, m, re))
|
||||
// If we detect a patched libreelec we don't want to bother with this anymoer
|
||||
// to avoid duplicate metadata pushes
|
||||
if (!libreelec_patched)
|
||||
{
|
||||
static regex re_nonpatched("Track \"(.*)\" loaded");
|
||||
if(regex_search(logmsg, m, re_nonpatched))
|
||||
{
|
||||
// Create a new meta struct?
|
||||
LOG(INFO) << "Loading track <" << m[1] << "> <" << m[2] << ">\n";
|
||||
LOG(INFO) << "metadata: <" << m[1] << ">\n";
|
||||
|
||||
json jtag = {
|
||||
{"artist", (string)m[1]},
|
||||
{"track", (string)m[2]}
|
||||
{"TITLE", (string)m[1]}
|
||||
};
|
||||
meta_.reset(new msg::StreamTags(jtag));
|
||||
|
||||
// Trigger a stream update
|
||||
if (pcmListener_)
|
||||
pcmListener_->onMetaChanged(this);
|
||||
setMeta(jtag);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the patched version
|
||||
static regex re_patched("metadata:(.*)");
|
||||
if (regex_search(logmsg, m, re_patched))
|
||||
{
|
||||
LOG(INFO) << "metadata: <" << m[1] << ">\n";
|
||||
|
||||
json jtag;
|
||||
setMeta(jtag.parse(m[1]));
|
||||
libreelec_patched = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue