[Cover] Adding external cover file support (lookup in the same directory)
This commit is contained in:
+156
-38
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user