Merge branch 'develop'

This commit is contained in:
emeric
2015-06-28 19:50:16 +02:00
22 changed files with 470 additions and 96 deletions
+2 -1
View File
@@ -12,7 +12,8 @@
[Cover]
- Scaling: find something more "reliable" than GIL and its custom extensions (adobe work, io_new)?
- Get the cover from image files in the same folder ("cover.jpg", etc.)
- Handle several file formats (not only jpg)
- Handle preferred cover file names
[Database]
- Optim, use SQL query to get the "genre" orphans
+7 -1
View File
@@ -11,7 +11,13 @@ main = {
path = "/var/lms/lms.db";
audio_extensions = "mp3 ogg oga aac m4a flac wav wma aif aiff ape mpc shn";
video_extensions = "flv avi mpg mpeg mp4 m4v mkv mov wmv ogv divx m2ts"
video_extensions = "flv avi mpg mpeg mp4 m4v mkv mov wmv ogv divx m2ts";
}
cover = {
file_extensions = "jpg jpeg";
file_max_size = 500000;
file_preferred_names = "cover front";
}
}
+9 -2
View File
@@ -127,8 +127,8 @@ InputFormatContext::getNbPictures(void) const
return res;
}
void
InputFormatContext::getPictures(std::vector<Picture>& pictures) const
std::vector<Picture>
InputFormatContext::getPictures(std::size_t nbMaxPictures) const
{
static const std::map<int, std::string> codecMimeMap =
{
@@ -140,6 +140,8 @@ InputFormatContext::getPictures(std::vector<Picture>& pictures) const
{ AV_CODEC_ID_PPM, "image/x-portable-pixmap" },
};
std::vector<Picture> pictures;
for (std::size_t i = 0; i < native()->nb_streams; ++i)
{
Stream stream(native()->streams[i]);
@@ -159,8 +161,13 @@ InputFormatContext::getPictures(std::vector<Picture>& pictures) const
std::copy(pkt.data, pkt.data + pkt.size, std::back_inserter(picture.data));
pictures.push_back( picture );
if (pictures.size() >= nbMaxPictures)
break;
}
}
return pictures;
}
} //namespace Av
+1 -1
View File
@@ -52,7 +52,7 @@ class InputFormatContext : public FormatContext
// Get attached pictures
std::size_t getNbPictures(void) const;
void getPictures(std::vector<Picture>& pictures) const;
std::vector<Picture> getPictures(std::size_t nbMaxPictures) const;
// Get the streams
std::vector<Stream> getStreams(void);
+11
View File
@@ -47,6 +47,17 @@ ConfigReader::getLoggerConfig(Logger::Config& config)
config.minSeverity = static_cast<Severity>((int)_config.lookup("main.logger.level"));
}
void
ConfigReader::getCoverGrabberConfig(CoverArt::Grabber::Config& config)
{
std::string extensions = _config.lookup("main.cover.file_extensions");
config.maxFileSize = static_cast<unsigned int>(_config.lookup("main.cover.file_max_size"));
std::string filenames = _config.lookup("main.cover.file_preferred_names");
splitStrings(extensions, config.fileExtensions);
splitStrings(filenames, config.preferredFileNames);
}
void
ConfigReader::getUserInterfaceConfig(Service::UserInterfaceService::Config& config)
{
+4
View File
@@ -23,6 +23,7 @@
#include <boost/filesystem.hpp>
#include <libconfig.h++>
#include "cover/CoverArtGrabber.hpp"
#include "logger/Logger.hpp"
#include "service/UserInterfaceService.hpp"
@@ -38,6 +39,9 @@ class ConfigReader
// Logger configuration
void getLoggerConfig(Logger::Config& config);
// Covers
void getCoverGrabberConfig(CoverArt::Grabber::Config& config);
// Service configurations
void getUserInterfaceConfig(Service::UserInterfaceService::Config& config);
void getRemoteServerConfig(Service::RemoteServerService::Config& config);
+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
+28 -4
View File
@@ -33,11 +33,35 @@ namespace CoverArt {
class Grabber
{
public:
Grabber(const Grabber&) = delete;
Grabber& operator=(const Grabber&) = delete;
static Grabber& instance();
struct Config
{
std::vector<std::string> fileExtensions;
std::size_t maxFileSize;
std::vector<std::string> preferredFileNames;
};
void init(const Config& config);
std::vector<boost::filesystem::path> getCoverPaths(const boost::filesystem::path& directoryPath, std::size_t nbMaxCovers = 1) const;
std::vector<CoverArt> getFromDirectory(const boost::filesystem::path& path, std::size_t nbMaxCovers = 1) const;
std::vector<CoverArt> getFromInputFormatContext(const Av::InputFormatContext& input, std::size_t nbMaxCovers = 1) const;
std::vector<CoverArt> getFromTrack(const boost::filesystem::path& path, std::size_t nbMaxCovers = 1) const;
std::vector<CoverArt> getFromTrack(Wt::Dbo::Session& session, Database::Track::id_type trackId, std::size_t nbMaxCovers = 1) const;
std::vector<CoverArt> getFromRelease(Wt::Dbo::Session& session, std::string releaseName, std::size_t nbMaxCovers = 1) const;
private:
Grabber();
std::vector<boost::filesystem::path> _fileExtensions;
std::size_t _maxFileSize;
std::vector<boost::filesystem::path> _preferredFileNames;
static std::vector<CoverArt> getFromInputFormatContext(const Av::InputFormatContext& input);
static std::vector<CoverArt> getFromTrack(Database::Track::pointer track);
static std::vector<CoverArt> getFromTrack(const boost::filesystem::path& path);
static std::vector<CoverArt> getFromRelease(Wt::Dbo::Session& session, std::string releaseName);
};
} // namespace CoverArt
+24 -3
View File
@@ -29,6 +29,7 @@
#include "Checksum.hpp"
#include "DatabaseUpdater.hpp"
#include "cover/CoverArtGrabber.hpp"
namespace {
@@ -272,9 +273,23 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
// Skip file if last write is the same
Wt::Dbo::ptr<Track> track = Track::getByPath(_db.getSession(), file);
if (track && track->getLastWriteTime() == lastWriteTime)
// if the file is the same and embeds covers, no need to update
if (track && track->getLastWriteTime() == lastWriteTime
&& (track->getCoverType() == Database::Track::CoverType::Embedded))
return;
// Check for external covers
std::vector<boost::filesystem::path> externalCovers = CoverArt::Grabber::instance().getCoverPaths(file.parent_path());
if (track && track->getLastWriteTime() == lastWriteTime)
{
// no change since last time we updated
// Skip only if no external covers has to be set
if (track->getCoverType() == Database::Track::CoverType::None && externalCovers.empty()
|| track->getCoverType() == Database::Track::CoverType::ExternalFile && !externalCovers.empty())
return;
}
MetaData::Items items;
_metadataParser.parse(file, items);
@@ -400,11 +415,17 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
if (items.find(MetaData::Type::HasCover) != items.end())
{
track.modify()->setHasCover( boost::any_cast<bool>(items[MetaData::Type::HasCover]));
bool hasCover = boost::any_cast<bool>(items[MetaData::Type::HasCover]);
if (hasCover)
track.modify()->setCoverType( Track::CoverType::Embedded );
else if (!externalCovers.empty())
track.modify()->setCoverType( Track::CoverType::ExternalFile );
else
track.modify()->setCoverType( Track::CoverType::None);
}
transaction.commit();
}
catch( std::exception& e ) {
LMS_LOG(MOD_DBUPDATER, SEV_ERROR) << "Exception while parsing audio file : '" << file << "': '" << e.what() << "' => skipping!";
+1 -1
View File
@@ -132,7 +132,7 @@ Track::Track(const boost::filesystem::path& p)
_trackNumber(0),
_discNumber(0),
_filePath( p.string() ),
_hasCover(false)
_coverType(CoverType::None)
{
}
+11 -5
View File
@@ -104,6 +104,12 @@ class Track
Track() {}
Track(const boost::filesystem::path& p);
enum class CoverType
{
Embedded, // Contains embedded cover
ExternalFile, // Cover is in an external file
None, // No local cover available
};
// Find utility functions
static pointer getByPath(Wt::Dbo::Session& session, const boost::filesystem::path& p);
@@ -141,14 +147,14 @@ class Track
void setOriginalDate(const boost::posix_time::ptime& date) { _originalDate = date; }
void setGenres(const std::string& genreList) { _genreList = genreList; }
void setGenres(std::vector<Genre::pointer> genres);
void setHasCover(bool hasCover) { _hasCover = hasCover; }
void setCoverType(CoverType coverType) { _coverType = coverType; }
int getTrackNumber(void) const { return _trackNumber; }
int getDiscNumber(void) const { return _discNumber; }
std::string getName(void) const { return _name; }
std::string getArtistName(void) const { return _artistName; }
std::string getReleaseName(void) const { return _releaseName; }
const std::string& getPath(void) const { return _filePath; }
boost::filesystem::path getPath(void) const { return _filePath; }
boost::posix_time::time_duration getDuration(void) const { return _duration; }
boost::posix_time::ptime getDate(void) const { return _date; }
boost::posix_time::ptime getOriginalDate(void) const { return _originalDate; }
@@ -157,7 +163,7 @@ class Track
boost::posix_time::ptime getLastWriteTime(void) const { return _fileLastWrite; }
const std::vector<unsigned char>& getChecksum(void) const { return _fileChecksum; }
bool hasCover(void) const { return _hasCover; }
CoverType getCoverType(void) const { return _coverType; }
template<class Action>
void persist(Action& a)
@@ -174,7 +180,7 @@ class Track
Wt::Dbo::field(a, _filePath, "path");
Wt::Dbo::field(a, _fileLastWrite, "last_write");
Wt::Dbo::field(a, _fileChecksum, "checksum");
Wt::Dbo::field(a, _hasCover, "has_cover");
Wt::Dbo::field(a, _coverType, "cover_type");
Wt::Dbo::hasMany(a, _genres, Wt::Dbo::ManyToMany, "track_genre", "", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::hasMany(a, _playlistEntries, Wt::Dbo::ManyToOne, "track");
}
@@ -199,7 +205,7 @@ class Track
std::string _filePath;
std::vector<unsigned char> _fileChecksum;
boost::posix_time::ptime _fileLastWrite;
bool _hasCover;
CoverType _coverType;
Wt::Dbo::collection< Genre::pointer > _genres; // Genres that are related to this track
Wt::Dbo::collection< Wt::Dbo::ptr<PlaylistEntry> > _playlistEntries;
+3 -1
View File
@@ -60,6 +60,9 @@ class Logger
{
public:
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
static Logger& instance();
struct Config {
@@ -76,7 +79,6 @@ class Logger
get(Module module);
private:
Logger();
std::map<Module, boost::log::sources::severity_logger< Severity > > _loggers;
+7
View File
@@ -67,6 +67,12 @@ int main(int argc, char* argv[])
Logger::instance().init(loggerConfig);
}
{
CoverArt::Grabber::Config config;
configReader.getCoverGrabberConfig(config);
CoverArt::Grabber::instance().init(config);
}
LMS_LOG(MOD_MAIN, SEV_INFO) << "Reading service configurations...";
Service::DatabaseUpdateService::Config dbUpdateConfig;
@@ -103,6 +109,7 @@ int main(int argc, char* argv[])
res = EXIT_SUCCESS;
}
// TODO catch setting not found exception
catch( libconfig::ParseException& e)
{
std::cerr << "Caught libconfig::ParseException! error='" << e.getError() << "', file = '" << e.getFile() << "', line = " << e.getLine() << std::endl;
@@ -294,23 +294,14 @@ AudioCollectionRequestHandler::processGetCoverArt(const AudioCollectionRequest::
response.set_type(AudioCollectionResponse::TypeCoverArt);
Wt::Dbo::Transaction transaction( _db.getSession() );
Database::Track::pointer track;
std::vector<CoverArt::CoverArt> coverArts;
switch(request.type())
{
case AudioCollectionRequest::GetCoverArt::TypeGetCoverArtRelease:
if (request.has_release())
{
SearchFilter filter;
filter.exactMatch[SearchFilter::Field::Release].push_back(request.release());
std::vector<Database::Track::pointer> tracks
= Database::Track::getAll(_db.getSession(), filter, -1, 1 /* limit reuslt size */);
if (!tracks.empty())
track = tracks.front();
coverArts = CoverArt::Grabber::instance().getFromRelease( _db.getSession(), request.release());
res = true;
}
break;
@@ -318,19 +309,12 @@ AudioCollectionRequestHandler::processGetCoverArt(const AudioCollectionRequest::
case AudioCollectionRequest::GetCoverArt::TypeGetCoverArtTrack:
if (request.has_track_id())
{
// Get the request release
track = Database::Track::getById( _db.getSession(), request.track_id());
coverArts = CoverArt::Grabber::instance().getFromTrack(_db.getSession(), request.track_id());
res = true;
}
break;
}
if (!res)
return false;
std::vector<CoverArt::CoverArt> coverArts = CoverArt::Grabber::getFromTrack(track);
BOOST_FOREACH(CoverArt::CoverArt& coverArt, coverArts)
{
AudioCollectionResponse_CoverArt* cover_art = response.add_cover_art();
+136
View File
@@ -0,0 +1,136 @@
/*
* Copyright (C) 2015 Emeric Poupon
*
* This file is part of LMS.
*
* LMS 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.
*
* LMS 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 LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Wt/Http/Request>
#include <Wt/Http/Response>
#include "logger/Logger.hpp"
#include "database/DatabaseHandler.hpp"
#include "transcode/Format.hpp"
#include "transcode/InputMediaFile.hpp"
#include "transcode/Parameters.hpp"
#include "transcode/AvConvTranscoder.hpp"
#include "Resource.hpp"
namespace RestAPI {
Resource::Resource(boost::filesystem::path dbPath)
: _dbPath(dbPath)
{
}
void
Resource::handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response)
{
static const std::size_t _bufferSize = 65536;
LMS_LOG(MOD_REST_API, SEV_DEBUG) << "Handle request...";
// see if this request is for a continuation:
Wt::Http::ResponseContinuation *continuation = request.continuation();
LMS_LOG(MOD_UI, SEV_DEBUG) << "Handling request. Continuation = " << std::boolalpha << continuation;
std::shared_ptr<Transcode::AvConvTranscoder> transcoder;
if (continuation)
{
transcoder = boost::any_cast<std::shared_ptr<Transcode::AvConvTranscoder> >(continuation->data());
}
else
{
const Wt::Http::ParameterMap& parameters = request.getParameterMap();
for (auto itParameter : parameters)
{
LMS_LOG(MOD_REST_API, SEV_DEBUG) << "Param name: '" << itParameter.first << "'";
for (auto value : itParameter.second)
{
LMS_LOG(MOD_REST_API, SEV_DEBUG) << "\tvalue: '" << value << "'";
}
}
auto itParamMediaId = parameters.find("mediaid");
if (itParamMediaId == parameters.end())
{
LMS_LOG(MOD_REST_API, SEV_DEBUG) << "Cannot find parameter mediaid";
return;
}
std::string mediaId = itParamMediaId->second.front();
LMS_LOG(MOD_REST_API, SEV_DEBUG) << "MediaId = " << mediaId;
try
{
Database::Handler db(_dbPath);
Wt::Dbo::Transaction transaction(db.getSession());
Database::Track::pointer track = Database::Track::getById(db.getSession(), std::stol(mediaId));
LMS_LOG(MOD_UI, SEV_DEBUG) << "Launching transcoder";
Transcode::InputMediaFile input(track->getPath());
Transcode::Parameters parameters(input, Transcode::Format::get(Transcode::Format::OGA));
transcoder = std::make_shared<Transcode::AvConvTranscoder>(parameters);
LMS_LOG(MOD_UI, SEV_DEBUG) << "Mime type set to '" << parameters.getOutputFormat().getMimeType() << "'";
response.setMimeType(parameters.getOutputFormat().getMimeType());
}
catch(std::exception &e)
{
LMS_LOG(MOD_UI, SEV_DEBUG) << "Caught exception: " << e.what();
return;
}
}
if (!transcoder)
{
LMS_LOG(MOD_UI, SEV_ERROR) << "No transcoder ?!";
return;
}
if (!transcoder->isComplete())
{
std::vector<unsigned char> data;
data.reserve(_bufferSize);
transcoder->process(data, _bufferSize);
// Give the client all the output data
response.out().write(reinterpret_cast<char*>(&data[0]), data.size());
LMS_LOG(MOD_UI, SEV_DEBUG) << "Written " << data.size() << " bytes! complete = " << std::boolalpha << transcoder->isComplete() << ", produced bytes = " << transcoder->getOutputBytes();
if (!response.out())
LMS_LOG(MOD_UI, SEV_ERROR) << "Write failed!";
}
if (!transcoder->isComplete() && response.out()) {
continuation = response.createContinuation();
continuation->setData(transcoder);
}
else
LMS_LOG(MOD_UI, SEV_DEBUG) << "No more data!";
}
} // namespace API
+44
View File
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2015 Emeric Poupon
*
* This file is part of LMS.
*
* LMS 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.
*
* LMS 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 LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef API_RESOURCE_HPP
#define API_RESOURCE_HPP
#include <Wt/WResource>
#include <boost/filesystem.hpp>
namespace RestAPI {
class Resource : public Wt::WResource
{
public:
Resource(boost::filesystem::path dbPath);
void handleRequest(const Wt::Http::Request& request,
Wt::Http::Response& response);
private:
boost::filesystem::path _dbPath;
};
} // namespace API
#endif
-2
View File
@@ -96,8 +96,6 @@ InputMediaFile::InputMediaFile(const boost::filesystem::path& p)
else
LMS_LOG(MOD_TRANSCODE, SEV_WARNING) << "Cannot find best stream for type " << type;
}
_covers = CoverArt::Grabber::getFromInputFormatContext(input);
}
std::vector<Stream>
-7
View File
@@ -34,8 +34,6 @@
namespace Transcode
{
class InputMediaFile
{
public:
@@ -53,9 +51,6 @@ class InputMediaFile
boost::filesystem::path getPath(void) const {return _path;}
boost::posix_time::time_duration getDuration(void) const {return _duration;}
// Pictures
const std::vector< CoverArt::CoverArt >& getCovers(void) const { return _covers; }
// Stream handling
const Stream& getStream(Stream::Id id) const;
std::vector<Stream> getStreams(Stream::Type type) const;
@@ -68,8 +63,6 @@ class InputMediaFile
std::vector<Stream> _streams;
std::map<Stream::Type, Stream::Id> _bestStreams;
std::vector< CoverArt::CoverArt > _covers;
};
+1 -1
View File
@@ -71,7 +71,7 @@ AudioMediaPlayer::AudioMediaPlayer(Wt::WContainerWidget *parent)
mainLayout->addLayout(currentMediaLayout, 1);
currentMediaLayout->addWidget( _mediaCover = new Wt::WImage());
_mediaCover->setImageLink( LmsApplication::instance()->getCoverResource()->getUnkownTrackUrl(72));
_mediaCover->setImageLink( LmsApplication::instance()->getCoverResource()->getUnknownTrackUrl(72));
_mediaCover->setStyleClass("mediaplayer-current-cover");
Wt::WVBoxLayout* mediaInfoLayout = new Wt::WVBoxLayout();
+3 -2
View File
@@ -382,10 +382,11 @@ PlayQueue::addTracks(const std::vector<Database::Track::id_type>& trackIds)
_model->setData(dataRow, COLUMN_ID_TRACK_ID, track.id(), Wt::UserRole);
std::string coverUrl;
if (track->hasCover())
if (track->getCoverType() != Database::Track::CoverType::None)
coverUrl = LmsApplication::instance()->getCoverResource()->getTrackUrl(track.id(), 64);
else
coverUrl = LmsApplication::instance()->getCoverResource()->getUnkownTrackUrl(64);
coverUrl = LmsApplication::instance()->getCoverResource()->getUnknownTrackUrl(64);
_model->setData(dataRow, COLUMN_ID_COVER, coverUrl, Wt::DecorationRole);
_model->setData(dataRow, COLUMN_ID_COVER, "playqueue-cover", Wt::StyleClassRole);
+18 -7
View File
@@ -85,7 +85,7 @@ CoverResource::getTrackUrl(Database::Track::id_type trackId, std::size_t size) c
}
std::string
CoverResource::getUnkownTrackUrl(size_t size) const
CoverResource::getUnknownTrackUrl(size_t size) const
{
return url() + "&size=" + std::to_string(size);
}
@@ -120,8 +120,8 @@ CoverResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons
if (trackIdStr)
{
Database::Track::id_type trackId = std::stol(*trackIdStr);
std::string path;
bool hasCover = false;
boost::filesystem::path path;
Database::Track::CoverType coverType = Database::Track::CoverType::None;
{
// transactions are not thread safe
@@ -132,13 +132,24 @@ CoverResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons
Database::Track::pointer track = Database::Track::getById(_db.getSession(), trackId);
if (track)
{
hasCover = track->hasCover();
coverType = track->getCoverType();
path = track->getPath();
}
}
if (hasCover)
covers = CoverArt::Grabber::getFromTrack(path);
switch (coverType)
{
case Database::Track::CoverType::Embedded:
covers = CoverArt::Grabber::instance().getFromTrack(path);
break;
case Database::Track::CoverType::ExternalFile:
covers = CoverArt::Grabber::instance().getFromDirectory(path.parent_path());
break;
case Database::Track::CoverType::None:
break;
}
}
else if (releaseStr)
{
@@ -146,7 +157,7 @@ CoverResource::handleRequest(const Wt::Http::Request& request, Wt::Http::Respons
std::unique_lock<std::mutex> lock(_mutex);
Wt::Dbo::Transaction transaction(_db.getSession());
covers = CoverArt::Grabber::getFromRelease(_db.getSession(), *releaseStr);
covers = CoverArt::Grabber::instance().getFromRelease(_db.getSession(), *releaseStr);
}
BOOST_FOREACH(CoverArt::CoverArt& cover, covers)
+1 -1
View File
@@ -41,7 +41,7 @@ class CoverResource : public Wt::WResource
std::string getReleaseUrl(std::string releaseName, size_t size) const;
std::string getTrackUrl(Database::Track::id_type trackId, size_t size) const;
std::string getUnkownTrackUrl(size_t size) const;
std::string getUnknownTrackUrl(size_t size) const;
void handleRequest(const Wt::Http::Request& request, Wt::Http::Response& response);