[Cover] Adding external cover file support (lookup in the same directory)

This commit is contained in:
emeric
2015-06-28 19:49:58 +02:00
parent 794de2fda8
commit 75c700df2a
22 changed files with 470 additions and 96 deletions
+156 -38
View File
@@ -26,19 +26,54 @@
#include "CoverArtGrabber.hpp"
namespace {
bool
isFileSupported(const boost::filesystem::path& file, const std::vector<boost::filesystem::path> extensions)
{
boost::filesystem::path fileExtension = file.extension();
for (auto extension : extensions)
{
if (extension == fileExtension)
return true;
}
return false;
}
} // namespace
namespace CoverArt {
Grabber::Grabber()
: _maxFileSize(0)
{
}
Grabber&
Grabber::instance()
{
static Grabber instance;
return instance;
}
void
Grabber::init(const Config& config)
{
for (auto extension : config.fileExtensions)
_fileExtensions.push_back("." + extension);
_maxFileSize = config.maxFileSize;
}
std::vector<CoverArt>
Grabber::getFromInputFormatContext(const Av::InputFormatContext& input)
Grabber::getFromInputFormatContext(const Av::InputFormatContext& input, std::size_t nbMaxCovers) const
{
std::vector<CoverArt> res;
try
{
std::vector<Av::Picture> pictures;
input.getPictures(pictures);
std::vector<Av::Picture> pictures = input.getPictures(nbMaxCovers);
BOOST_FOREACH(const Av::Picture& picture, pictures)
res.push_back( CoverArt(picture.mimeType, picture.data) );
@@ -53,7 +88,65 @@ Grabber::getFromInputFormatContext(const Av::InputFormatContext& input)
}
std::vector<CoverArt>
Grabber::getFromTrack(const boost::filesystem::path& p)
Grabber::getFromDirectory(const boost::filesystem::path& p, std::size_t nbMaxCovers) const
{
std::vector<CoverArt> res;
std::vector<boost::filesystem::path> coverPathes = getCoverPaths(p, nbMaxCovers);
for (auto coverPath : coverPathes)
{
if (res.size() >= nbMaxCovers)
break;
std::vector<unsigned char> data;
std::ifstream file(coverPath.string(), std::ios::binary);
char c;
while (file.get(c))
data.push_back(c);
// TODO handle other formats
res.push_back(CoverArt("image/jpeg", data));
}
return res;
}
std::vector<boost::filesystem::path>
Grabber::getCoverPaths(const boost::filesystem::path& directoryPath, std::size_t nbMaxCovers) const
{
std::vector<boost::filesystem::path> res;
// TODO handle preferred file names
boost::filesystem::directory_iterator itPath(directoryPath);
boost::filesystem::directory_iterator itEnd;
while (itPath != itEnd)
{
boost::filesystem::path path = *itPath;
itPath++;
if (!boost::filesystem::is_regular(path))
continue;
if (!isFileSupported(path, _fileExtensions))
continue;
if (boost::filesystem::file_size(path) > _maxFileSize)
{
LMS_LOG(MOD_COVER, SEV_INFO) << "Cover file '" << path << " is too big (" << boost::filesystem::file_size(path) << "), limit is " << _maxFileSize;
continue;
}
res.push_back(path);
if (res.size() >= nbMaxCovers)
break;
}
return res;
}
std::vector<CoverArt>
Grabber::getFromTrack(const boost::filesystem::path& p, std::size_t nbMaxCovers) const
{
std::vector<CoverArt> res;
@@ -61,55 +154,80 @@ Grabber::getFromTrack(const boost::filesystem::path& p)
{
Av::InputFormatContext input(p);
return getFromInputFormatContext(input);
res = getFromInputFormatContext(input, nbMaxCovers);
}
catch(std::exception& e)
{
LMS_LOG(MOD_COVER, SEV_ERROR) << "Cannot get pictures: " << e.what();
LMS_LOG(MOD_COVER, SEV_ERROR) << "Cannot get covers from file " << p << ": " << e.what();
}
return res;
}
std::vector<CoverArt>
Grabber::getFromTrack(Database::Track::pointer track)
{
std::vector<CoverArt> res;
if (!track || !track->hasCover())
return std::vector<CoverArt>();
try
{
Av::InputFormatContext input(track->getPath());
return getFromInputFormatContext(input);
}
catch(std::exception& e)
{
LMS_LOG(MOD_COVER, SEV_ERROR) << "Cannot get pictures: " << e.what();
}
return res;
}
std::vector<CoverArt>
Grabber::getFromRelease(Wt::Dbo::Session& session, std::string releaseName)
Grabber::getFromTrack(Wt::Dbo::Session& session, Database::Track::id_type trackId, std::size_t nbMaxCovers) const
{
using namespace Database;
// For now, just return the embedded cover of the first track
SearchFilter filter;
filter.exactMatch[SearchFilter::Field::Release].push_back(releaseName);
Wt::Dbo::Transaction transaction(session);
std::vector<Track::pointer> tracks
= Track::getAll(session, filter, -1, 1 /* limit result size */);
if (!tracks.empty())
return getFromTrack( tracks.front() );
else
Track::pointer track = Track::getById(session, trackId);
if (!track)
return std::vector<CoverArt>();
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::ExternalFile:
return Grabber::getFromDirectory(trackPath.parent_path(), nbMaxCovers);
case Track::CoverType::None:
return std::vector<CoverArt>();
}
return std::vector<CoverArt>();
}
std::vector<CoverArt>
Grabber::getFromRelease(Wt::Dbo::Session& session, std::string releaseName, std::size_t nbMaxCovers) const
{
using namespace Database;
boost::filesystem::path firstTrackPath;
bool embeddedCover = false;
// Get the first track of the release
{
SearchFilter filter;
filter.exactMatch[SearchFilter::Field::Release].push_back(releaseName);
Wt::Dbo::Transaction transaction(session);
std::vector<Track::pointer> tracks
= Track::getAll(session, filter, -1, 1 /* limit result size */);
if (tracks.empty())
return std::vector<CoverArt>();
firstTrackPath = tracks.front()->getPath();
embeddedCover = (tracks.front()->getCoverType() == Track::CoverType::Embedded);
}
// First, try to get covers from the directory of the release
std::vector<CoverArt> res = getFromDirectory( firstTrackPath.parent_path(), nbMaxCovers);
// Fallback on the embedded cover of the first track
if (res.empty() && embeddedCover)
res = getFromTrack( firstTrackPath, nbMaxCovers);
return res;
}
} // namespace CoverArt