Simplify the covert art resource

This commit is contained in:
emeric
2018-09-14 21:03:25 +02:00
parent 352bb6506a
commit 060379a56b
7 changed files with 95 additions and 211 deletions
+67 -62
View File
@@ -43,12 +43,15 @@ isFileSupported(const boost::filesystem::path& file, const std::vector<boost::fi
return false;
}
} // namespace
namespace CoverArt {
Grabber::Grabber()
{
if (!_defaultCover.load( Wt::WApplication::instance()->docRoot() + "/images/unknown-cover.jpg"))
throw LmsException("Cannot read default cover file");
}
Grabber&
@@ -58,48 +61,42 @@ Grabber::instance()
return instance;
}
static std::vector<Image::Image>
getFromAvMediaFile(const Av::MediaFile& input, std::size_t nbMaxCovers)
static boost::optional<Image::Image>
getFromAvMediaFile(const Av::MediaFile& input)
{
std::vector<Image::Image> res;
for (Av::Picture& picture : input.getAttachedPictures(nbMaxCovers))
for (auto& picture : input.getAttachedPictures(2))
{
Image::Image image;
if (image.load(picture.data))
res.push_back( image );
return image;
else
LMS_LOG(COVER, ERROR) << "Cannot load embedded cover file in '" << input.getPath().string() << "'";
}
return res;
return boost::none;
}
std::vector<Image::Image>
Grabber::getFromDirectory(const boost::filesystem::path& p, std::size_t nbMaxCovers) const
boost::optional<Image::Image>
Grabber::getFromDirectory(const boost::filesystem::path& p) const
{
std::vector<Image::Image> res;
std::vector<boost::filesystem::path> coverPathes = getCoverPaths(p, nbMaxCovers);
for (auto coverPath : coverPathes)
for (auto coverPath : getCoverPaths(p))
{
if (res.size() >= nbMaxCovers)
break;
Image::Image image;
if (image.load(coverPath))
res.push_back(image);
return image;
else
LMS_LOG(COVER, ERROR) << "Cannot load image in file '" << coverPath.string() << "'";
}
return res;
return boost::none;
}
std::vector<boost::filesystem::path>
Grabber::getCoverPaths(const boost::filesystem::path& directoryPath, std::size_t nbMaxCovers) const
Grabber::getCoverPaths(const boost::filesystem::path& directoryPath) const
{
std::vector<boost::filesystem::path> res;
boost::system::error_code ec;
@@ -126,86 +123,94 @@ Grabber::getCoverPaths(const boost::filesystem::path& directoryPath, std::size_t
}
res.push_back(path);
if (res.size() >= nbMaxCovers)
break;
}
return res;
}
std::vector<Image::Image>
Grabber::getFromTrack(const boost::filesystem::path& p, std::size_t nbMaxCovers) const
boost::optional<Image::Image>
Grabber::getFromTrack(const boost::filesystem::path& p) const
{
try
{
Av::MediaFile input(p);
return getFromAvMediaFile(input, nbMaxCovers);
return getFromAvMediaFile(input);
}
catch (Av::MediaFileException& e)
{
LMS_LOG(COVER, ERROR) << "Cannot get covers from track " << p.string() << ": " << e.what();
return boost::none;
}
return std::vector<Image::Image>();
}
std::vector<Image::Image>
Grabber::getFromTrack(Wt::Dbo::Session& session, Database::IdType trackId, std::size_t nbMaxCovers) const
Image::Image
Grabber::getFromTrack(Wt::Dbo::Session& session, Database::IdType trackId, std::size_t size) const
{
using namespace Database;
Wt::Dbo::Transaction transaction(session);
boost::optional<Image::Image> cover;
Track::pointer track = Track::getById(session, trackId);
if (!track)
return std::vector<Image::Image>();
Track::CoverType coverType = track->getCoverType();
boost::filesystem::path trackPath = track->getPath();
transaction.commit();
switch (coverType)
{
case Track::CoverType::Embedded:
return Grabber::getFromTrack(trackPath, nbMaxCovers);
case Track::CoverType::None:
return Grabber::getFromDirectory(trackPath.parent_path(), nbMaxCovers);
Wt::Dbo::Transaction transaction(session);
Track::pointer track = Track::getById(session, trackId);
if (track)
{
Track::CoverType coverType = track->getCoverType();
boost::filesystem::path trackPath = track->getPath();
transaction.commit();
switch (coverType)
{
case Track::CoverType::Embedded:
cover = getFromTrack(trackPath);
case Track::CoverType::None:
cover = getFromDirectory(trackPath.parent_path());
}
}
}
return std::vector<Image::Image>();
if (!cover)
cover = _defaultCover;
cover->scale(size);
return *cover;
}
std::vector<Image::Image>
Grabber::getFromRelease(Wt::Dbo::Session& session, Database::IdType releaseId, std::size_t nbMaxCovers) const
Image::Image
Grabber::getFromRelease(Wt::Dbo::Session& session, Database::IdType releaseId, std::size_t size) const
{
using namespace Database;
Wt::Dbo::Transaction transaction(session);
boost::optional<Image::Image> cover;
// If the release does not exist, do nothing
Release::pointer release = Release::getById(session, releaseId);
if (!release)
return std::vector<Image::Image>();
{
Wt::Dbo::Transaction transaction(session);
std::vector<Track::pointer> tracks = release->getTracks();
if (tracks.empty())
return std::vector<Image::Image>();
auto release = Release::getById(session, releaseId);
if (release)
{
auto tracks = release->getTracks();
if (!tracks.empty())
{
auto trackId = tracks.front().id();
transaction.commit();
boost::filesystem::path firstTrackPath = tracks.front()->getPath();
bool embeddedCover = (tracks.front()->getCoverType() == Track::CoverType::Embedded);
return getFromTrack(session, trackId, size);
}
}
}
transaction.commit();
if (!cover)
cover = _defaultCover;
// First, try to get covers from the directory of the release
std::vector<Image::Image> res = getFromDirectory( firstTrackPath.parent_path(), nbMaxCovers);
cover->scale(size);
// Fallback on the embedded cover of the first track
if (res.empty() && embeddedCover)
res = getFromTrack( firstTrackPath, nbMaxCovers);
return res;
return *cover;
}
} // namespace CoverArt
+12 -5
View File
@@ -35,18 +35,25 @@ class Grabber
static Grabber& instance();
std::vector<boost::filesystem::path> getCoverPaths(const boost::filesystem::path& directoryPath, std::size_t nbMaxCovers = 1) const;
std::vector<Image::Image> getFromDirectory(const boost::filesystem::path& path, std::size_t nbMaxCovers = 1) const;
std::vector<Image::Image> getFromTrack(const boost::filesystem::path& path, std::size_t nbMaxCovers = 1) const;
std::vector<Image::Image> getFromTrack(Wt::Dbo::Session& session, Database::IdType trackId, std::size_t nbMaxCovers = 1) const;
std::vector<Image::Image> getFromRelease(Wt::Dbo::Session& session, Database::IdType releaseId, std::size_t nbMaxCovers = 1) const;
Image::Image getFromTrack(Wt::Dbo::Session& session, Database::IdType trackId, std::size_t size) const;
Image::Image getFromRelease(Wt::Dbo::Session& session, Database::IdType releaseId, std::size_t size) const;
private:
Grabber();
boost::optional<Image::Image> getFromTrack(const boost::filesystem::path& path) const;
std::vector<boost::filesystem::path> getCoverPaths(const boost::filesystem::path& directoryPath) const;
boost::optional<Image::Image> getFromDirectory(const boost::filesystem::path& path) const;
Image::Image _defaultCover;
std::vector<boost::filesystem::path> _fileExtensions
= {".jpg", ".jpeg", ".png", ".bmp"}; // TODO parametrize
std::size_t _maxFileSize = 5000000;
std::vector<boost::filesystem::path> _preferredFileNames
= {"cover", "front"}; // TODO parametrize
};
+2 -2
View File
@@ -328,8 +328,8 @@ LmsApplication::createHome()
_isAdmin = LmsApp->getUser()->isAdmin();
}
_imageResource = std::make_shared<ImageResource>(_db);
_transcodeResource = std::make_shared<TranscodeResource>(_db);
_imageResource = std::make_shared<ImageResource>();
_transcodeResource = std::make_shared<TranscodeResource>();
setConfirmCloseMessage(Wt::WString::tr("Lms.quit-confirm"));
+10 -114
View File
@@ -37,61 +37,11 @@ namespace UserInterface {
static const std::string unknownCoverPath = "/images/unknown-cover.jpg";
static const std::string unknownArtistImagePath = "/images/unknown-artist.jpg";
ImageResource::ImageResource(Database::Handler& db)
: _db(db)
{
}
ImageResource::~ImageResource()
{
beingDeleted();
}
Image::Image
ImageResource::getDefaultCover(std::size_t size)
{
std::unique_lock<std::mutex> lock(_mutex);
auto itCover = _defaultCovers.find(size);
if (itCover == _defaultCovers.end())
{
// Load default cover art for this size
Image::Image image;
if (!image.load( Wt::WApplication::instance()->docRoot() + unknownCoverPath ))
throw LmsException("Cannot read default cover file");
image.scale(size);
auto res = _defaultCovers.insert(std::make_pair(size, image));
itCover = res.first;
}
return itCover->second;
}
Image::Image
ImageResource::getDefaultArtistImage(std::size_t size)
{
std::unique_lock<std::mutex> lock(_mutex);
auto itCover = _defaultArtistImages.find(size);
if (itCover == _defaultArtistImages.end())
{
Image::Image image;
if (!image.load( Wt::WApplication::instance()->docRoot() + unknownArtistImagePath))
throw LmsException("Cannot read default artist image file");
image.scale(size);
auto res = _defaultArtistImages.insert(std::make_pair(size, image));
itCover = res.first;
}
return itCover->second;
}
std::string
ImageResource::getReleaseUrl(Database::IdType releaseId, std::size_t size) const
{
@@ -127,29 +77,12 @@ ImageResource::putImage(Wt::Http::Response& response, Image::Image cover)
response.out().write(reinterpret_cast<const char *>(&data[0]), data.size());
}
void
ImageResource::putCover(Wt::Http::Response& response, std::vector<Image::Image> covers, size_t size)
{
for (Image::Image cover : covers)
{
if (cover.scale(size))
{
putImage(response, cover);
return;
}
}
// If no cover found, just send default one
putImage(response, getDefaultCover(size));
}
void
ImageResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response)
{
// Retrieve parameters
const std::string *trackIdStr = request.getParameter("trackid");
const std::string *releaseIdStr = request.getParameter("releaseid");
const std::string *artistIdStr = request.getParameter("artistid");
const std::string *sizeStr = request.getParameter("size");
// Mandatory parameter size
@@ -160,48 +93,19 @@ ImageResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons
if (!size || *size > maxSize)
return;
Image::Image cover;
if (trackIdStr)
{
auto trackId = readAs<Database::IdType>(*trackIdStr);
if (!trackId)
return;
boost::filesystem::path path;
Database::Track::CoverType coverType = Database::Track::CoverType::None;
// transactions are not thread safe
{
// transactions are not thread safe
Wt::WApplication::UpdateLock lock(LmsApplication::instance());
Wt::Dbo::Transaction transaction(_db.getSession());
Database::Track::pointer track = Database::Track::getById(_db.getSession(), *trackId);
if (track)
{
coverType = track->getCoverType();
path = track->getPath();
}
Wt::WApplication::UpdateLock lock(LmsApp);
cover = CoverArt::Grabber::instance().getFromTrack(LmsApp->getDboSession(), *trackId, *size);
}
if (!path.empty())
{
std::vector<Image::Image> covers;
switch (coverType)
{
case Database::Track::CoverType::Embedded:
covers = CoverArt::Grabber::instance().getFromTrack(path);
break;
case Database::Track::CoverType::None:
covers = CoverArt::Grabber::instance().getFromDirectory(path.parent_path());
break;
}
putCover(response, covers, *size);
return;
}
putImage(response, getDefaultCover(*size));
}
else if (releaseIdStr)
{
@@ -209,24 +113,16 @@ ImageResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons
if (!releaseId)
return;
std::vector<Image::Image> covers;
// transactions are not thread safe
{
Wt::WApplication::UpdateLock lock(LmsApplication::instance());
covers = CoverArt::Grabber::instance().getFromRelease(_db.getSession(), *releaseId);
Wt::WApplication::UpdateLock lock(LmsApp);
cover = CoverArt::Grabber::instance().getFromRelease(LmsApp->getDboSession(), *releaseId, *size);
}
putCover(response, covers, *size);
}
else if (artistIdStr)
{
putImage(response, getDefaultArtistImage(*size));
}
else
{
putImage(response, getDefaultCover(*size));
}
return;
putImage(response, cover);
}
} // namespace UserInterface
-14
View File
@@ -37,7 +37,6 @@ class ImageResource : public Wt::WResource
public:
static const std::size_t maxSize = 512;
ImageResource(Database::Handler& db);
~ImageResource();
std::string getReleaseUrl(Database::IdType releaseId, size_t size) const;
@@ -49,22 +48,9 @@ class ImageResource : public Wt::WResource
private:
Image::Image getDefaultCover(std::size_t size);
Image::Image getDefaultArtistImage(std::size_t size);
void putImage(Wt::Http::Response& response, Image::Image image);
void putCover(Wt::Http::Response& response, std::vector<Image::Image> covers, std::size_t size);
// Used to protect transactions since they are not thread safe
std::mutex _mutex;
Database::Handler& _db;
// Default cover for different sizes
std::map<std::size_t, Image::Image> _defaultCovers;
// Default artist images for different sizes
std::map<std::size_t, Image::Image> _defaultArtistImages;
// TODO construct a cache for covers?
};
} // namespace UserInterface
+4 -11
View File
@@ -29,15 +29,9 @@
namespace UserInterface {
TranscodeResource::TranscodeResource(Database::Handler& db)
: _db(db)
{
LMS_LOG(UI, DEBUG) << "CONSTRUCTING RESOURCE";
}
TranscodeResource:: ~TranscodeResource()
{
LMS_LOG(UI, DEBUG) << "DESTRUCTING RESOURCE";
beingDeleted();
}
@@ -100,12 +94,11 @@ TranscodeResource::handleRequest(const Wt::Http::Request& request,
// transactions are not thread safe
{
Wt::WApplication::UpdateLock lock(LmsApplication::instance());
Wt::WApplication::UpdateLock lock(LmsApp);
Wt::Dbo::Transaction transaction(_db.getSession());
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
Database::User::pointer user = _db.getCurrentUser();
Database::Track::pointer track = Database::Track::getById(_db.getSession(), trackId);
Database::Track::pointer track = Database::Track::getById(LmsApp->getDboSession(), trackId);
if (!track)
{
@@ -113,7 +106,7 @@ TranscodeResource::handleRequest(const Wt::Http::Request& request,
return;
}
parameters.bitrate = user->getAudioBitrate();
parameters.bitrate = LmsApp->getUser()->getAudioBitrate();
transcoder = std::make_shared<Av::Transcoder>(track->getPath(), parameters);
}
-3
View File
@@ -34,7 +34,6 @@ namespace UserInterface {
class TranscodeResource : public Wt::WResource
{
public:
TranscodeResource(Database::Handler& db);
~TranscodeResource();
std::string getUrl(Database::IdType trackId, Av::Encoding encoding) const;
@@ -44,8 +43,6 @@ class TranscodeResource : public Wt::WResource
private:
Database::Handler& _db;
static const std::size_t _chunkSize = 65536*4;
};