mirror of
https://github.com/badaix/snapcast.git
synced 2025-05-18 03:26:15 +02:00
Add support for cover raw images
This commit is contained in:
parent
a5f79cdf90
commit
befc8da440
12 changed files with 210 additions and 27 deletions
|
@ -21,6 +21,7 @@
|
||||||
#include "control_session_ws.hpp"
|
#include "control_session_ws.hpp"
|
||||||
#include "message/pcm_chunk.hpp"
|
#include "message/pcm_chunk.hpp"
|
||||||
#include "stream_session_ws.hpp"
|
#include "stream_session_ws.hpp"
|
||||||
|
#include <boost/beast/http/buffer_body.hpp>
|
||||||
#include <boost/beast/http/file_body.hpp>
|
#include <boost/beast/http/file_body.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
@ -139,7 +140,7 @@ ControlSessionHttp::ControlSessionHttp(ControlMessageReceiver* receiver, boost::
|
||||||
const ServerSettings::Http& settings)
|
const ServerSettings::Http& settings)
|
||||||
: ControlSession(receiver), socket_(std::move(socket)), settings_(settings), strand_(ioc)
|
: ControlSession(receiver), socket_(std::move(socket)), settings_(settings), strand_(ioc)
|
||||||
{
|
{
|
||||||
LOG(DEBUG, LOG_TAG) << "ControlSessionHttp\n";
|
LOG(DEBUG, LOG_TAG) << "ControlSessionHttp, Local IP: " << socket_.local_endpoint().address().to_string() << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -233,11 +234,36 @@ void ControlSessionHttp::handle_request(http::request<Body, http::basic_fields<A
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request path must be absolute and not contain "..".
|
// Request path must be absolute and not contain "..".
|
||||||
if (req.target().empty() || req.target()[0] != '/' || req.target().find("..") != beast::string_view::npos)
|
auto target = req.target();
|
||||||
|
if (target.empty() || target[0] != '/' || target.find("..") != beast::string_view::npos)
|
||||||
return send(bad_request("Illegal request-target"));
|
return send(bad_request("Illegal request-target"));
|
||||||
|
|
||||||
|
static string image_cache_target = "/__image_cache?name=";
|
||||||
|
auto pos = target.find(image_cache_target);
|
||||||
|
if (pos != std::string::npos)
|
||||||
|
{
|
||||||
|
pos += image_cache_target.size();
|
||||||
|
target = target.substr(pos);
|
||||||
|
auto image = settings_.image_cache.getImage(std::string(target));
|
||||||
|
LOG(DEBUG, LOG_TAG) << "image cache: " << target << ", found: " << image.has_value() << "\n";
|
||||||
|
if (image.has_value())
|
||||||
|
{
|
||||||
|
http::response<http::buffer_body> res{http::status::ok, req.version()};
|
||||||
|
res.body().data = image.value().data();
|
||||||
|
const auto size = image.value().size();
|
||||||
|
res.body().size = size;
|
||||||
|
res.body().more = false;
|
||||||
|
res.set(http::field::server, HTTP_SERVER_NAME);
|
||||||
|
res.set(http::field::content_type, mime_type(target));
|
||||||
|
res.content_length(size);
|
||||||
|
res.keep_alive(req.keep_alive());
|
||||||
|
return send(std::move(res));
|
||||||
|
}
|
||||||
|
return send(not_found(req.target()));
|
||||||
|
}
|
||||||
|
|
||||||
// Build the path to the requested file
|
// Build the path to the requested file
|
||||||
std::string path = path_cat(settings_.doc_root, req.target());
|
std::string path = path_cat(settings_.doc_root, target);
|
||||||
if (req.target().back() == '/')
|
if (req.target().back() == '/')
|
||||||
path.append("index.html");
|
path.append("index.html");
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,12 @@
|
||||||
# serve a website from the doc_root location
|
# serve a website from the doc_root location
|
||||||
# disabled if commented or empty
|
# disabled if commented or empty
|
||||||
doc_root = /usr/share/snapserver/snapweb
|
doc_root = /usr/share/snapserver/snapweb
|
||||||
|
|
||||||
|
# Hostname or IP under which clients can reach this host
|
||||||
|
# used to serve cached cover art
|
||||||
|
# use <hostname> as placeholder for your actual host name
|
||||||
|
#host = <hostname>
|
||||||
|
|
||||||
#
|
#
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
93
server/image_cache.hpp
Normal file
93
server/image_cache.hpp
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/***
|
||||||
|
This file is part of snapcast
|
||||||
|
Copyright (C) 2014-2021 Johannes Pohl
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
***/
|
||||||
|
|
||||||
|
#ifndef IMAGE_CACHE_HPP
|
||||||
|
#define IMAGE_CACHE_HPP
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <boost/algorithm/hex.hpp>
|
||||||
|
#include <boost/uuid/detail/md5.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
class ImageCache
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ImageCache() = default;
|
||||||
|
virtual ~ImageCache() = default;
|
||||||
|
|
||||||
|
std::string setImage(const std::string& key, std::string image, const std::string& extension)
|
||||||
|
{
|
||||||
|
if (image.empty())
|
||||||
|
{
|
||||||
|
clear(key);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
using boost::uuids::detail::md5;
|
||||||
|
md5 hash;
|
||||||
|
md5::digest_type digest;
|
||||||
|
hash.process_bytes(image.data(), image.size());
|
||||||
|
hash.get_digest(digest);
|
||||||
|
std::string filename;
|
||||||
|
const auto intDigest = reinterpret_cast<const int*>(&digest);
|
||||||
|
boost::algorithm::hex_lower(intDigest, intDigest + (sizeof(md5::digest_type) / sizeof(int)), std::back_inserter(filename));
|
||||||
|
auto ext = extension;
|
||||||
|
if (ext.find('.') == 0)
|
||||||
|
ext = ext.substr(1);
|
||||||
|
filename += "." + ext;
|
||||||
|
key_to_url_[key] = filename;
|
||||||
|
url_to_data_[filename] = image;
|
||||||
|
return filename;
|
||||||
|
};
|
||||||
|
|
||||||
|
void clear(const std::string& key)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
auto iter = key_to_url_.find(key);
|
||||||
|
if (iter != key_to_url_.end())
|
||||||
|
{
|
||||||
|
auto url = *iter;
|
||||||
|
auto url_iter = url_to_data_.find(url.second);
|
||||||
|
if (url_iter != url_to_data_.end())
|
||||||
|
url_to_data_.erase(url_iter);
|
||||||
|
key_to_url_.erase(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> getImage(const std::string& url)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
auto iter = url_to_data_.find(url);
|
||||||
|
if (iter == url_to_data_.end())
|
||||||
|
return std::nullopt;
|
||||||
|
else
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<std::string, std::string> key_to_url_;
|
||||||
|
std::map<std::string, std::string> url_to_data_;
|
||||||
|
std::mutex mutex_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,6 +1,6 @@
|
||||||
/***
|
/***
|
||||||
This file is part of snapcast
|
This file is part of snapcast
|
||||||
Copyright (C) 2014-2020 Johannes Pohl
|
Copyright (C) 2014-2021 Johannes Pohl
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -22,6 +22,9 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "image_cache.hpp"
|
||||||
|
|
||||||
|
|
||||||
struct ServerSettings
|
struct ServerSettings
|
||||||
{
|
{
|
||||||
struct Server
|
struct Server
|
||||||
|
@ -39,6 +42,8 @@ struct ServerSettings
|
||||||
size_t port{1780};
|
size_t port{1780};
|
||||||
std::vector<std::string> bind_to_address{{"0.0.0.0"}};
|
std::vector<std::string> bind_to_address{{"0.0.0.0"}};
|
||||||
std::string doc_root{""};
|
std::string doc_root{""};
|
||||||
|
std::string host{"<hostname>"};
|
||||||
|
inline static ImageCache image_cache;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Tcp
|
struct Tcp
|
||||||
|
|
|
@ -84,6 +84,7 @@ int main(int argc, char* argv[])
|
||||||
auto http_bind_to_address = conf.add<Value<string>>("", "http.bind_to_address", "address for the server to listen on",
|
auto http_bind_to_address = conf.add<Value<string>>("", "http.bind_to_address", "address for the server to listen on",
|
||||||
settings.http.bind_to_address.front(), &settings.http.bind_to_address[0]);
|
settings.http.bind_to_address.front(), &settings.http.bind_to_address[0]);
|
||||||
conf.add<Implicit<string>>("", "http.doc_root", "serve a website from the doc_root location", settings.http.doc_root, &settings.http.doc_root);
|
conf.add<Implicit<string>>("", "http.doc_root", "serve a website from the doc_root location", settings.http.doc_root, &settings.http.doc_root);
|
||||||
|
conf.add<Value<string>>("", "http.host", "Hostname or IP under which clients can reach this host", settings.http.host, &settings.http.host);
|
||||||
|
|
||||||
// TCP RPC settings
|
// TCP RPC settings
|
||||||
conf.add<Value<bool>>("", "tcp.enabled", "enable TCP Json RPC)", settings.tcp.enabled, &settings.tcp.enabled);
|
conf.add<Value<bool>>("", "tcp.enabled", "enable TCP Json RPC)", settings.tcp.enabled, &settings.tcp.enabled);
|
||||||
|
@ -281,6 +282,11 @@ int main(int argc, char* argv[])
|
||||||
if (settings.http.enabled)
|
if (settings.http.enabled)
|
||||||
{
|
{
|
||||||
dns_services.emplace_back("_snapcast-http._tcp", settings.http.port);
|
dns_services.emplace_back("_snapcast-http._tcp", settings.http.port);
|
||||||
|
if ((settings.http.host == "<hostname>") || settings.http.host.empty())
|
||||||
|
{
|
||||||
|
settings.http.host = boost::asio::ip::host_name();
|
||||||
|
LOG(INFO, LOG_TAG) << "Using HTTP host name: " << settings.http.host << "\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
publishZeroConfg->publish(dns_services);
|
publishZeroConfg->publish(dns_services);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -67,11 +67,6 @@ AirplayStream::AirplayStream(PcmListener* pcmListener, boost::asio::io_context&
|
||||||
#ifdef HAS_EXPAT
|
#ifdef HAS_EXPAT
|
||||||
createParser();
|
createParser();
|
||||||
metadata_dirty_ = false;
|
metadata_dirty_ = false;
|
||||||
metadata_ = json();
|
|
||||||
metadata_["ALBUM"] = "";
|
|
||||||
metadata_["ARTIST"] = "";
|
|
||||||
metadata_["TITLE"] = "";
|
|
||||||
metadata_["COVER"] = "";
|
|
||||||
#else
|
#else
|
||||||
LOG(INFO, LOG_TAG) << "Metadata support not enabled (HAS_EXPAT not defined)"
|
LOG(INFO, LOG_TAG) << "Metadata support not enabled (HAS_EXPAT not defined)"
|
||||||
<< "\n";
|
<< "\n";
|
||||||
|
@ -165,7 +160,7 @@ void AirplayStream::push()
|
||||||
|
|
||||||
if (is_cover)
|
if (is_cover)
|
||||||
{
|
{
|
||||||
setMetaData("COVER", data);
|
setMetaData(meta_.art_data, Metatags::ArtData{data, "jpg"});
|
||||||
// LOG(INFO, LOG_TAG) << "Metadata type: " << entry_->type << " code: " << entry_->code << " data length: " << data.length() << "\n";
|
// LOG(INFO, LOG_TAG) << "Metadata type: " << entry_->type << " code: " << entry_->code << " data length: " << data.length() << "\n";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -174,28 +169,30 @@ void AirplayStream::push()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry_->type == "core" && entry_->code == "asal")
|
if (entry_->type == "core" && entry_->code == "asal")
|
||||||
setMetaData("ALBUM", data);
|
setMetaData(meta_.album, data);
|
||||||
if (entry_->type == "core" && entry_->code == "asar")
|
else if (entry_->type == "core" && entry_->code == "asar")
|
||||||
setMetaData("ARTIST", data);
|
setMetaData(meta_.artist, {data});
|
||||||
if (entry_->type == "core" && entry_->code == "minm")
|
else if (entry_->type == "core" && entry_->code == "minm")
|
||||||
setMetaData("TITLE", data);
|
setMetaData(meta_.title, data);
|
||||||
|
|
||||||
// mden = metadata end, pcen == picture end
|
// mden = metadata end, pcen == picture end
|
||||||
if (metadata_dirty_ && entry_->type == "ssnc" && (entry_->code == "mden" || entry_->code == "pcen"))
|
if (metadata_dirty_ && entry_->type == "ssnc" && (entry_->code == "mden" || entry_->code == "pcen"))
|
||||||
{
|
{
|
||||||
// setMetadata(metadata_);
|
Properties properties;
|
||||||
|
properties.metatags = meta_;
|
||||||
|
setProperties(properties);
|
||||||
metadata_dirty_ = false;
|
metadata_dirty_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AirplayStream::setMetaData(const string& key, const string& newValue)
|
template <typename T>
|
||||||
|
void AirplayStream::setMetaData(std::optional<T>& meta_value, const T& value)
|
||||||
{
|
{
|
||||||
// Only overwrite metadata and set metadata_dirty_ if the metadata has changed.
|
// Only overwrite metadata and set metadata_dirty_ if the metadata has changed.
|
||||||
// This avoids multiple unnecessary transmissions of the same metadata.
|
// This avoids multiple unnecessary transmissions of the same metadata.
|
||||||
const auto& oldValue = metadata_[key];
|
if (!meta_value.has_value() || (meta_value.value() != value))
|
||||||
if (oldValue != newValue)
|
|
||||||
{
|
{
|
||||||
metadata_[key] = newValue;
|
meta_value = value;
|
||||||
metadata_dirty_ = true;
|
metadata_dirty_ = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,8 +67,8 @@ protected:
|
||||||
XML_Parser parser_;
|
XML_Parser parser_;
|
||||||
std::unique_ptr<TageEntry> entry_;
|
std::unique_ptr<TageEntry> entry_;
|
||||||
std::string buf_;
|
std::string buf_;
|
||||||
json metadata_;
|
|
||||||
/// set whenever metadata_ has changed
|
/// set whenever metadata_ has changed
|
||||||
|
Metatags meta_;
|
||||||
bool metadata_dirty_;
|
bool metadata_dirty_;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -77,7 +77,9 @@ protected:
|
||||||
int parse(const std::string& line);
|
int parse(const std::string& line);
|
||||||
void createParser();
|
void createParser();
|
||||||
void push();
|
void push();
|
||||||
void setMetaData(const std::string& key, const std::string& newValue);
|
|
||||||
|
template <typename T>
|
||||||
|
void setMetaData(std::optional<T>& meta_value, const T& value);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void setParamsAndPipePathFromPort();
|
void setParamsAndPipePathFromPort();
|
||||||
|
|
|
@ -79,6 +79,8 @@ std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string base64_decode(std::string const& encoded_string)
|
std::string base64_decode(std::string const& encoded_string)
|
||||||
{
|
{
|
||||||
int in_len = encoded_string.size();
|
int in_len = encoded_string.size();
|
||||||
|
|
|
@ -161,7 +161,9 @@ void LibrespotStream::onStderrMsg(const std::string& line)
|
||||||
Metatags meta;
|
Metatags meta;
|
||||||
meta.artist = std::vector<std::string>{j["ARTIST"].get<std::string>()};
|
meta.artist = std::vector<std::string>{j["ARTIST"].get<std::string>()};
|
||||||
meta.title = j["TITLE"].get<std::string>();
|
meta.title = j["TITLE"].get<std::string>();
|
||||||
// TODO setMetadata(meta);
|
Properties properties;
|
||||||
|
properties.metatags = meta;
|
||||||
|
setProperties(properties);
|
||||||
}
|
}
|
||||||
else if (regex_search(line, m, re_track_loaded))
|
else if (regex_search(line, m, re_track_loaded))
|
||||||
{
|
{
|
||||||
|
@ -169,10 +171,8 @@ void LibrespotStream::onStderrMsg(const std::string& line)
|
||||||
Metatags meta;
|
Metatags meta;
|
||||||
meta.title = string(m[1]);
|
meta.title = string(m[1]);
|
||||||
meta.duration = cpt::stod(m[2]) / 1000.;
|
meta.duration = cpt::stod(m[2]) / 1000.;
|
||||||
// TODO setMetadata(meta);
|
|
||||||
Properties properties;
|
Properties properties;
|
||||||
// properties.can_seek = true;
|
properties.metatags = meta;
|
||||||
// properties.can_control = true;
|
|
||||||
setProperties(properties);
|
setProperties(properties);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,6 +106,10 @@ json Metatags::toJson() const
|
||||||
addTag(j, "trackNumber", track_number);
|
addTag(j, "trackNumber", track_number);
|
||||||
addTag(j, "url", url);
|
addTag(j, "url", url);
|
||||||
addTag(j, "artUrl", art_url);
|
addTag(j, "artUrl", art_url);
|
||||||
|
if (art_data.has_value())
|
||||||
|
{
|
||||||
|
j["artData"] = {{"data", art_data->data}, {"extension", art_data->extension}};
|
||||||
|
}
|
||||||
addTag(j, "useCount", use_count);
|
addTag(j, "useCount", use_count);
|
||||||
addTag(j, "userRating", user_rating);
|
addTag(j, "userRating", user_rating);
|
||||||
addTag(j, "spotifyArtistId", spotify_artist_id);
|
addTag(j, "spotifyArtistId", spotify_artist_id);
|
||||||
|
@ -153,6 +157,7 @@ void Metatags::fromJson(const json& j)
|
||||||
"trackNumber",
|
"trackNumber",
|
||||||
"url",
|
"url",
|
||||||
"artUrl",
|
"artUrl",
|
||||||
|
"artData",
|
||||||
"useCount",
|
"useCount",
|
||||||
"userRating",
|
"userRating",
|
||||||
"spotifyArtistId",
|
"spotifyArtistId",
|
||||||
|
@ -200,6 +205,11 @@ void Metatags::fromJson(const json& j)
|
||||||
readTag(j, "trackNumber", track_number);
|
readTag(j, "trackNumber", track_number);
|
||||||
readTag(j, "url", url);
|
readTag(j, "url", url);
|
||||||
readTag(j, "artUrl", art_url);
|
readTag(j, "artUrl", art_url);
|
||||||
|
art_data = std::nullopt;
|
||||||
|
if (j.contains("artData") && j["artData"].contains("data") && j["artData"].contains("extension"))
|
||||||
|
{
|
||||||
|
art_data = ArtData{j["artData"]["data"].get<std::string>(), j["artData"]["extension"].get<std::string>()};
|
||||||
|
}
|
||||||
readTag(j, "useCount", use_count);
|
readTag(j, "useCount", use_count);
|
||||||
readTag(j, "userRating", user_rating);
|
readTag(j, "userRating", user_rating);
|
||||||
readTag(j, "spotifyArtistId", spotify_artist_id);
|
readTag(j, "spotifyArtistId", spotify_artist_id);
|
||||||
|
|
|
@ -31,6 +31,22 @@ using json = nlohmann::json;
|
||||||
class Metatags
|
class Metatags
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
struct ArtData
|
||||||
|
{
|
||||||
|
std::string data;
|
||||||
|
std::string extension;
|
||||||
|
|
||||||
|
bool operator==(const ArtData& other) const
|
||||||
|
{
|
||||||
|
return ((other.data == data) && (other.extension == extension));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const ArtData& other) const
|
||||||
|
{
|
||||||
|
return !(other == *this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Metatags() = default;
|
Metatags() = default;
|
||||||
Metatags(const json& j);
|
Metatags(const json& j);
|
||||||
|
|
||||||
|
@ -85,6 +101,8 @@ public:
|
||||||
/// URI: The location of an image representing the track or album. Clients should not assume this will continue to exist when the media player stops giving
|
/// URI: The location of an image representing the track or album. Clients should not assume this will continue to exist when the media player stops giving
|
||||||
/// out the URL
|
/// out the URL
|
||||||
std::optional<std::string> art_url;
|
std::optional<std::string> art_url;
|
||||||
|
/// Base64 encoded data of an image representing the track or album
|
||||||
|
std::optional<ArtData> art_data;
|
||||||
/// The track lyrics
|
/// The track lyrics
|
||||||
std::optional<std::string> lyrics;
|
std::optional<std::string> lyrics;
|
||||||
/// The speed of the music, in beats per minute
|
/// The speed of the music, in beats per minute
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <boost/asio/ip/host_name.hpp>
|
||||||
|
|
||||||
|
#include "base64.h"
|
||||||
#include "common/aixlog.hpp"
|
#include "common/aixlog.hpp"
|
||||||
#include "common/error_code.hpp"
|
#include "common/error_code.hpp"
|
||||||
#include "common/snap_exception.hpp"
|
#include "common/snap_exception.hpp"
|
||||||
|
@ -447,11 +450,26 @@ void PcmStream::setProperties(const Properties& properties)
|
||||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||||
|
|
||||||
Properties props = properties;
|
Properties props = properties;
|
||||||
// Missing metadata means, they didn't change, so
|
// Missing metadata means the data didn't change, so
|
||||||
// enrich the new properites with old metadata
|
// enrich the new properites with old metadata
|
||||||
if (!props.metatags.has_value() && properties_.metatags.has_value())
|
if (!props.metatags.has_value() && properties_.metatags.has_value())
|
||||||
props.metatags = properties_.metatags;
|
props.metatags = properties_.metatags;
|
||||||
|
|
||||||
|
// If the cover image is availbale as raw data, cache it on the HTTP Server to make it also available via HTTP
|
||||||
|
if (props.metatags.has_value() && props.metatags->art_data.has_value() && !props.metatags->art_url.has_value())
|
||||||
|
{
|
||||||
|
auto data = base64_decode(props.metatags->art_data.value().data);
|
||||||
|
auto md5 = server_settings_.http.image_cache.setImage(getName(), std::move(data), props.metatags->art_data.value().extension);
|
||||||
|
|
||||||
|
std::stringstream url;
|
||||||
|
url << "http://" << server_settings_.http.host << ":" << server_settings_.http.port << "/__image_cache?name=" << md5;
|
||||||
|
props.metatags->art_url = url.str();
|
||||||
|
}
|
||||||
|
else if (!props.metatags->art_data.has_value())
|
||||||
|
{
|
||||||
|
server_settings_.http.image_cache.clear(getName());
|
||||||
|
}
|
||||||
|
|
||||||
if (props == properties_)
|
if (props == properties_)
|
||||||
{
|
{
|
||||||
LOG(DEBUG, LOG_TAG) << "setProperties: Properties did not change\n";
|
LOG(DEBUG, LOG_TAG) << "setProperties: Properties did not change\n";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue