Modified metadata interface to be tag independent, added JSON api.

This commit is contained in:
frafall 2017-11-30 11:07:23 +01:00
parent 67083975b0
commit ce17b0010a
7 changed files with 116 additions and 33 deletions

View file

@ -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)

View file

@ -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);

View 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.

View 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)
}
}

View file

@ -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);
}

View file

@ -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_;
};

View file

@ -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;
}
}